history-service-0.1+14.04.20140407/0000755000015301777760000000000012320627745017120 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/tools/0000755000015301777760000000000012320627745020260 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/tools/tplogger-import/0000755000015301777760000000000012320627745023413 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/tools/tplogger-import/telepathylogimporter.cpp0000644000015301777760000001255012320627220030371 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "telepathylogimporter.h" #include "telepathylogreader.h" #include "manager.h" #include "thread.h" #include "textevent.h" #include "voiceevent.h" #include TelepathyLogImporter::TelepathyLogImporter(QObject *parent) : QObject(parent), mTextEventCount(0), mVoiceEventCount(0), mBatchSize(200) { connect(TelepathyLogReader::instance(), SIGNAL(loadedCallEvent(Tpl::CallEventPtr)), SLOT(onCallEventLoaded(Tpl::CallEventPtr))); connect(TelepathyLogReader::instance(), SIGNAL(loadedMessageEvent(Tpl::TextEventPtr)), SLOT(onMessageEventLoaded(Tpl::TextEventPtr))); connect(TelepathyLogReader::instance(), SIGNAL(finished()), SLOT(onFinished())); qDebug() << "Starting to import..."; } void TelepathyLogImporter::onCallEventLoaded(const Tpl::CallEventPtr &event) { // FIXME: add support for conf call bool incoming = event->receiver()->entityType() == Tpl::EntityTypeSelf; Tpl::EntityPtr remote = incoming ? event->sender() : event->receiver(); History::Thread thread = History::Manager::instance()->threadForParticipants(event->account()->uniqueIdentifier(), History::EventTypeVoice, QStringList() << remote->identifier(), History::MatchCaseSensitive, true); QString eventId = QString("%1:%2").arg(thread.threadId()).arg(event->timestamp().toString()); History::VoiceEvent historyEvent = History::VoiceEvent(thread.accountId(), thread.threadId(), eventId, incoming ? remote->identifier() : "self", event->timestamp(), false, event->endReason() == Tp::CallStateChangeReasonNoAnswer, event->duration()); mEvents << historyEvent; if (mEvents.count() >= mBatchSize) { History::Manager::instance()->writeEvents(mEvents); mEvents.clear(); } mVoiceEventCount++; } void TelepathyLogImporter::onMessageEventLoaded(const Tpl::TextEventPtr &event) { // FIXME: add support for conf call bool incoming = event->receiver()->entityType() == Tpl::EntityTypeSelf; Tpl::EntityPtr remote = incoming ? event->sender() : event->receiver(); History::Thread thread = History::Manager::instance()->threadForParticipants(event->account()->uniqueIdentifier(), History::EventTypeText, QStringList() << remote->identifier(), History::MatchCaseSensitive, true); History::TextEvent historyEvent = History::TextEvent(thread.accountId(), thread.threadId(), event->messageToken(), incoming ? remote->identifier() : "self", event->timestamp(), false, event->message(), History::MessageTypeText, History::MessageStatusUnknown, event->timestamp()); mEvents << historyEvent; if (mEvents.count() >= mBatchSize) { History::Manager::instance()->writeEvents(mEvents); mEvents.clear(); } mTextEventCount++; } void TelepathyLogImporter::onFinished() { // write the remaining items if (!mEvents.isEmpty()) { History::Manager::instance()->writeEvents(mEvents); mEvents.clear(); } qDebug() << "... finished"; qDebug() << "Text events:" << mTextEventCount; qDebug() << "Voice events:" << mVoiceEventCount; qApp->quit(); } history-service-0.1+14.04.20140407/tools/tplogger-import/telepathylogreader.cpp0000644000015301777760000001340612320627220027773 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "telepathylogreader.h" #include #include #include #include #include #include #include #include #include TelepathyLogReader::TelepathyLogReader(QObject *parent) : QObject(parent), mLogManager(Tpl::LogManager::instance()) { Tp::Features accountFeatures; Tp::Features contactFeatures; accountFeatures << Tp::Account::FeatureCore; contactFeatures << Tp::Contact::FeatureAlias << Tp::Contact::FeatureAvatarData << Tp::Contact::FeatureAvatarToken << Tp::Contact::FeatureCapabilities << Tp::Contact::FeatureSimplePresence; mAccountManager = Tp::AccountManager::create( Tp::AccountFactory::create(QDBusConnection::sessionBus(), accountFeatures), Tp::ConnectionFactory::create(QDBusConnection::sessionBus()), Tp::ChannelFactory::create(QDBusConnection::sessionBus()), Tp::ContactFactory::create(contactFeatures)); connect(mAccountManager->becomeReady(Tp::AccountManager::FeatureCore), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountManagerReady(Tp::PendingOperation*))); } TelepathyLogReader *TelepathyLogReader::instance() { static TelepathyLogReader *self = new TelepathyLogReader(); return self; } void TelepathyLogReader::fetchLog(const Tp::AccountPtr &account) { Tpl::PendingEntities *pendingEntities = mLogManager->queryEntities(account); mOperations.append(pendingEntities); /* Fetching the log work like this: - Start by fetching the entities from the log - Once you get the entities, fetch the available dates - After you get the dates, fetch the events themselves */ connect(pendingEntities, SIGNAL(finished(Tpl::PendingOperation*)), SLOT(onPendingEntitiesFinished(Tpl::PendingOperation*))); } void TelepathyLogReader::requestDatesForEntities(const Tp::AccountPtr &account, const Tpl::EntityPtrList &entities) { Q_FOREACH(Tpl::EntityPtr entity, entities) { Tpl::PendingDates *pendingDates = mLogManager->queryDates(account, entity, Tpl::EventTypeMaskAny); connect(pendingDates, SIGNAL(finished(Tpl::PendingOperation*)), SLOT(onPendingDatesFinished(Tpl::PendingOperation*))); mOperations.append(pendingDates); } } void TelepathyLogReader::requestEventsForDates(const Tp::AccountPtr &account, const Tpl::EntityPtr &entity, const Tpl::QDateList &dates) { Q_FOREACH(QDate date, dates) { Tpl::PendingEvents *pendingEvents = mLogManager->queryEvents(account, entity, Tpl::EventTypeMaskAny, date); connect(pendingEvents, SIGNAL(finished(Tpl::PendingOperation*)), SLOT(onPendingEventsFinished(Tpl::PendingOperation*))); mOperations.append(pendingEvents); } } void TelepathyLogReader::onPendingEntitiesFinished(Tpl::PendingOperation *op) { Tpl::PendingEntities *pe = qobject_cast(op); if (!pe) { return; } // request the dates for all the entities requestDatesForEntities(pe->account(), pe->entities()); mOperations.removeAll(op); if (mOperations.isEmpty()) { Q_EMIT finished(); } } void TelepathyLogReader::onPendingDatesFinished(Tpl::PendingOperation *op) { Tpl::PendingDates *pd = qobject_cast(op); if (!pd) { return; } // request all events requestEventsForDates(pd->account(), pd->entity(), pd->dates()); mOperations.removeAll(op); if (mOperations.isEmpty()) { Q_EMIT finished(); } } void TelepathyLogReader::onPendingEventsFinished(Tpl::PendingOperation *op) { Tpl::PendingEvents *pe = qobject_cast(op); if (!pe) { return; } Q_FOREACH(const Tpl::EventPtr &event, pe->events()) { bool incoming = event->receiver()->entityType() == Tpl::EntityTypeSelf; Tpl::EntityPtr remoteEntity = incoming ? event->sender() : event->receiver(); QString phoneNumber = remoteEntity->identifier(); QDateTime timestamp = event->timestamp(); Tpl::CallEventPtr callEvent = event.dynamicCast(); Tpl::TextEventPtr textEvent = event.dynamicCast(); if (!callEvent.isNull()) { Q_EMIT loadedCallEvent(callEvent); } if (!textEvent.isNull()) { Q_EMIT loadedMessageEvent(textEvent); } } mOperations.removeAll(op); if (mOperations.isEmpty()) { Q_EMIT finished(); } } void TelepathyLogReader::onAccountManagerReady(Tp::PendingOperation *op) { mLogManager->setAccountManagerPtr(mAccountManager); Q_FOREACH(const Tp::AccountPtr account, mAccountManager->allAccounts()) { fetchLog(account); } } history-service-0.1+14.04.20140407/tools/tplogger-import/telepathylogreader.h0000644000015301777760000000423112320627220027434 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef TELEPATHYLOGREADER_H #define TELEPATHYLOGREADER_H #include #include #include #include #include #include #include #include #include #include #include class TelepathyLogReader : public QObject { Q_OBJECT public: static TelepathyLogReader *instance(); public Q_SLOTS: void fetchLog(const Tp::AccountPtr &account); protected: void requestDatesForEntities(const Tp::AccountPtr &account, const Tpl::EntityPtrList &entities); void requestEventsForDates(const Tp::AccountPtr &account, const Tpl::EntityPtr &entity, const Tpl::QDateList &dates); Q_SIGNALS: void loadedCallEvent(const Tpl::CallEventPtr &event); void loadedMessageEvent(const Tpl::TextEventPtr &event); void finished(); protected Q_SLOTS: void onAccountManagerReady(Tp::PendingOperation *op); void onPendingEntitiesFinished(Tpl::PendingOperation *op); void onPendingDatesFinished(Tpl::PendingOperation *op); void onPendingEventsFinished(Tpl::PendingOperation *op); protected: Tpl::LogManagerPtr mLogManager; Tp::AccountManagerPtr mAccountManager; private: explicit TelepathyLogReader(QObject *parent = 0); QList mOperations; }; #endif // CALLLOGMODEL_H history-service-0.1+14.04.20140407/tools/tplogger-import/main.cpp0000644000015301777760000000211012320627220025021 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include #include #include "telepathylogimporter.h" int main(int argc, char **argv) { QCoreApplication app(argc, argv); Tp::registerTypes(); Tpl::init(); TelepathyLogImporter importer; Q_UNUSED(importer) return app.exec(); } history-service-0.1+14.04.20140407/tools/tplogger-import/CMakeLists.txt0000644000015301777760000000107312320627220026140 0ustar pbusernogroup00000000000000set(tplogger_SRCS main.cpp telepathylogreader.cpp telepathylogimporter.cpp ) include_directories( ${TP_QT5_INCLUDE_DIRS} ${TPL_QT5_INCLUDE_DIRS} ${QTGLIB_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src ) add_executable(history-tplogger-import ${tplogger_SRCS}) qt5_use_modules(history-tplogger-import Core DBus) target_link_libraries(history-tplogger-import ${TP_QT5_LIBRARIES} ${TPL_QT5_LIBRARIES} ${QTGLIB_LIBRARIES} historyservice ) install(TARGETS history-tplogger-import RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) history-service-0.1+14.04.20140407/tools/tplogger-import/telepathylogimporter.h0000644000015301777760000000261012320627220030032 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef TELEPATHYLOGIMPORTER_H #define TELEPATHYLOGIMPORTER_H #include #include #include #include #include #include class TelepathyLogImporter : public QObject { Q_OBJECT public: explicit TelepathyLogImporter(QObject *parent = 0); public Q_SLOTS: void onCallEventLoaded(const Tpl::CallEventPtr &event); void onMessageEventLoaded(const Tpl::TextEventPtr &event); void onFinished(); private: int mTextEventCount; int mVoiceEventCount; History::Events mEvents; int mBatchSize; }; #endif // TELEPATHYLOGIMPORTER_H history-service-0.1+14.04.20140407/tools/CMakeLists.txt0000644000015301777760000000022712320627220023005 0ustar pbusernogroup00000000000000add_subdirectory(reader) if (${TPL_QT5_FOUND} AND ${QTGLIB_FOUND}) add_subdirectory(tplogger-import) endif (${TPL_QT5_FOUND} AND ${QTGLIB_FOUND}) history-service-0.1+14.04.20140407/tools/qmlclient/0000755000015301777760000000000012320627745022250 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/tools/qmlclient/items.qml0000644000015301777760000000374012320627220024074 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ import QtQuick 2.0 import Ubuntu.History 0.1 ListView { id: listView width: 600 height: 800 model: HistoryEventModel { type: HistoryThreadModel.EventTypeText sort: HistorySort { sortField: "timestamp" sortOrder: HistorySort.DescendingOrder } } delegate: Rectangle { anchors.left: parent.left anchors.right: parent.right height: 100 border.color: "black" color: "lightGray" Column { anchors.fill: parent anchors.margins: 3 spacing: 3 Text { anchors.left: parent.left text: "Participants: " + participants } Text { anchors.left: parent.left text: "AccountId: " + accountId } Text { anchors.left: parent.left text: "ThreadId: " + threadId } Text { anchors.left: parent.left text: "Sender: " + senderId } Text { anchors.left: parent.left text: "Message: " + textMessage } } } } history-service-0.1+14.04.20140407/tools/qmlclient/sample_client.qml0000644000015301777760000000440312320627220025567 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ import QtQuick 2.0 import Ubuntu.History 0.1 ListView { id: listView width: 600 height: 800 model: HistoryThreadModel { sort: HistorySort { sortField: "lastEventTimestamp" sortOrder: HistorySort.DescendingOrder } } Component { id: sectionDelegate Text { text: section } } section.property: "eventDate" section.delegate: sectionDelegate delegate: Rectangle { anchors.left: parent.left anchors.right: parent.right height: childrenRect.height + 5 border.color: "black" color: "lightGray" Column { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: 3 spacing: 3 height: childrenRect.height Text { anchors.left: parent.left text: "AccountId: " + accountId } Text { anchors.left: parent.left text: "ThreadId: " + threadId } Text { anchors.left: parent.left text: "Participants: " + participants } Text { anchors.left: parent.left text: "Sender: " + eventSenderId } Text { anchors.left: parent.left text: "Message: " + eventTextMessage } } } } history-service-0.1+14.04.20140407/tools/reader/0000755000015301777760000000000012320627745021522 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/tools/reader/main.cpp0000644000015301777760000001046612320627220023145 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "manager.h" #include "eventview.h" #include "filter.h" #include "intersectionfilter.h" #include "thread.h" #include "threadview.h" #include "textevent.h" #include "voiceevent.h" #include #include void printEvent(const History::Event &event) { QString extraInfo; History::TextEvent textEvent; History::VoiceEvent voiceEvent; switch (event.type()) { case History::EventTypeText: textEvent = event; extraInfo = QString("message: %1").arg(textEvent.message()); break; case History::EventTypeVoice: voiceEvent = event; extraInfo = QString("missed: %1\n duration: %2").arg(voiceEvent.missed() ? "yes" : "no", voiceEvent.duration().toString()); break; } qDebug() << qPrintable(QString(" * Event: accountId: %1\n threadId: %2\n eventId: %3\n senderId: %4\n timestamp: %5\n newEvent: %6") .arg(event.accountId(), event.threadId(), event.eventId(), event.senderId(), event.timestamp().toString(), event.newEvent() ? "yes" : "no")); qDebug() << qPrintable(QString(" %1").arg(extraInfo)); } void printThread(const History::Thread &thread) { QString type = "Unknown"; switch (thread.type()) { case History::EventTypeText: type = "Text"; break; case History::EventTypeVoice: type = "Voice"; break; } qDebug() << qPrintable(QString("- %1 thread - accountId: %2 threadId: %3 count: %4 unreadCount: %5").arg(type, thread.accountId(), thread.threadId(), QString::number(thread.count()), QString::number(thread.unreadCount()))); qDebug() << qPrintable(QString(" Participants: %1").arg(thread.participants().join(", "))); if (!thread.lastEvent().isNull()) { qDebug() << " Last event:"; printEvent(thread.lastEvent()); } qDebug() << " All events:"; } int main(int argc, char **argv) { QCoreApplication app(argc, argv); History::Manager *manager = History::Manager::instance(); QList eventTypes; eventTypes << History::EventTypeText << History::EventTypeVoice; Q_FOREACH(History::EventType type, eventTypes) { History::ThreadViewPtr view = manager->queryThreads(type); History::Threads threads = view->nextPage(); while (!threads.isEmpty()) { Q_FOREACH(const History::Thread &thread, threads) { printThread(thread); // now print the events for this thread History::IntersectionFilter filter; filter.append(History::Filter(History::FieldThreadId, thread.threadId())); filter.append(History::Filter(History::FieldAccountId, thread.accountId())); History::EventViewPtr eventView = manager->queryEvents(type, History::Sort(), filter); History::Events events = eventView->nextPage(); while (!events.isEmpty()) { Q_FOREACH(const History::Event &event, events) { printEvent(event); } events = eventView->nextPage(); } } threads = view->nextPage(); } } } history-service-0.1+14.04.20140407/tools/reader/CMakeLists.txt0000644000015301777760000000033412320627220024246 0ustar pbusernogroup00000000000000set(reader_SRCS main.cpp) include_directories( ${CMAKE_SOURCE_DIR}/src ) add_executable(history-reader ${reader_SRCS}) qt5_use_modules(history-reader Core) target_link_libraries(history-reader historyservice) history-service-0.1+14.04.20140407/cmake/0000755000015301777760000000000012320627745020200 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/cmake/modules/0000755000015301777760000000000012320627745021650 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/cmake/modules/FindLcov.cmake0000644000015301777760000000172012320627220024342 0ustar pbusernogroup00000000000000# - Find lcov # Will define: # # LCOV_EXECUTABLE - the lcov binary # GENHTML_EXECUTABLE - the genhtml executable # # Copyright (C) 2010 by Johannes Wienke # # 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 2, 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. # INCLUDE(FindPackageHandleStandardArgs) FIND_PROGRAM(LCOV_EXECUTABLE lcov) FIND_PROGRAM(GENHTML_EXECUTABLE genhtml) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lcov DEFAULT_MSG LCOV_EXECUTABLE GENHTML_EXECUTABLE) # only visible in advanced view MARK_AS_ADVANCED(LCOV_EXECUTABLE GENHTML_EXECUTABLE) history-service-0.1+14.04.20140407/cmake/modules/EnableCoverageReport.cmake0000644000015301777760000001531112320627220026675 0ustar pbusernogroup00000000000000# - Creates a special coverage build type and target on GCC. # # Defines a function ENABLE_COVERAGE_REPORT which generates the coverage target # for selected targets. Optional arguments to this function are used to filter # unwanted results using globbing expressions. Moreover targets with tests for # the source code can be specified to trigger regenerating the report if the # test has changed # # ENABLE_COVERAGE_REPORT(TARGETS target... [FILTER filter...] [TESTS test targets...]) # # To generate a coverage report first build the project with # CMAKE_BUILD_TYPE=coverage, then call make test and afterwards make coverage. # # The coverage report is based on gcov. Depending on the availability of lcov # a HTML report will be generated and/or an XML report of gcovr is found. # The generated coverage target executes all found solutions. Special targets # exist to create e.g. only the xml report: coverage-xml. # # Copyright (C) 2010 by Johannes Wienke # # 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 2, 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. # INCLUDE(ParseArguments) FIND_PACKAGE(Lcov) FIND_PACKAGE(gcovr) FUNCTION(ENABLE_COVERAGE_REPORT) # argument parsing PARSE_ARGUMENTS(ARG "FILTER;TARGETS;TESTS" "" ${ARGN}) SET(COVERAGE_RAW_FILE "${CMAKE_BINARY_DIR}/coverage.raw.info") SET(COVERAGE_FILTERED_FILE "${CMAKE_BINARY_DIR}/coverage.info") SET(COVERAGE_REPORT_DIR "${CMAKE_BINARY_DIR}/coveragereport") SET(COVERAGE_XML_FILE "${CMAKE_BINARY_DIR}/coverage.xml") SET(COVERAGE_XML_COMMAND_FILE "${CMAKE_BINARY_DIR}/coverage-xml.cmake") # decide if there is any tool to create coverage data SET(TOOL_FOUND FALSE) IF(LCOV_FOUND OR GCOVR_FOUND) SET(TOOL_FOUND TRUE) ENDIF() IF(NOT TOOL_FOUND) MESSAGE(STATUS "Cannot enable coverage targets because neither lcov nor gcovr are found.") ENDIF() STRING(TOLOWER "${CMAKE_BUILD_TYPE}" COVERAGE_BUILD_TYPE) IF(CMAKE_COMPILER_IS_GNUCXX AND TOOL_FOUND AND "${COVERAGE_BUILD_TYPE}" MATCHES "coverage") MESSAGE(STATUS "Coverage support enabled for targets: ${ARG_TARGETS}") # create coverage build type SET(CMAKE_CXX_FLAGS_COVERAGE ${CMAKE_CXX_FLAGS_DEBUG} PARENT_SCOPE) SET(CMAKE_C_FLAGS_COVERAGE ${CMAKE_C_FLAGS_DEBUG} PARENT_SCOPE) SET(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES} coverage PARENT_SCOPE) # instrument targets SET_TARGET_PROPERTIES(${ARG_TARGETS} PROPERTIES COMPILE_FLAGS --coverage LINK_FLAGS --coverage) # html report IF (LCOV_FOUND) MESSAGE(STATUS "Enabling HTML coverage report") # set up coverage target ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_RAW_FILE} COMMAND ${LCOV_EXECUTABLE} -c -d ${CMAKE_BINARY_DIR} -o ${COVERAGE_RAW_FILE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMENT "Collecting coverage data" DEPENDS ${ARG_TARGETS} ${ARG_TESTS} VERBATIM) # filter unwanted stuff LIST(LENGTH ARG_FILTER FILTER_LENGTH) IF(${FILTER_LENGTH} GREATER 0) SET(FILTER COMMAND ${LCOV_EXECUTABLE}) FOREACH(F ${ARG_FILTER}) SET(FILTER ${FILTER} -r ${COVERAGE_FILTERED_FILE} ${F}) ENDFOREACH() SET(FILTER ${FILTER} -o ${COVERAGE_FILTERED_FILE}) ELSE() SET(FILTER "") ENDIF() ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_FILTERED_FILE} COMMAND ${LCOV_EXECUTABLE} -e ${COVERAGE_RAW_FILE} "${CMAKE_SOURCE_DIR}*" -o ${COVERAGE_FILTERED_FILE} ${FILTER} DEPENDS ${COVERAGE_RAW_FILE} COMMENT "Filtering recorded coverage data for project-relevant entries" VERBATIM) ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_REPORT_DIR} COMMAND ${CMAKE_COMMAND} -E make_directory ${COVERAGE_REPORT_DIR} COMMAND ${GENHTML_EXECUTABLE} --legend --show-details -t "${PROJECT_NAME} test coverage" -o ${COVERAGE_REPORT_DIR} ${COVERAGE_FILTERED_FILE} DEPENDS ${COVERAGE_FILTERED_FILE} COMMENT "Generating HTML coverage report in ${COVERAGE_REPORT_DIR}" VERBATIM) ADD_CUSTOM_TARGET(coverage-html DEPENDS ${COVERAGE_REPORT_DIR}) ENDIF() # xml coverage report IF(GCOVR_FOUND) MESSAGE(STATUS "Enabling XML coverage report") # gcovr cannot write directly to a file so the execution needs to # be wrapped in a cmake file that generates the file output FILE(WRITE ${COVERAGE_XML_COMMAND_FILE} "SET(ENV{LANG} en)\n") FILE(APPEND ${COVERAGE_XML_COMMAND_FILE} "EXECUTE_PROCESS(COMMAND \"${GCOVR_EXECUTABLE}\" -x -r \"${CMAKE_SOURCE_DIR}\" OUTPUT_FILE \"${COVERAGE_XML_FILE}\" WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")\n") ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_XML_FILE} COMMAND ${CMAKE_COMMAND} ARGS -P ${COVERAGE_XML_COMMAND_FILE} COMMENT "Generating coverage XML report" VERBATIM) ADD_CUSTOM_TARGET(coverage-xml DEPENDS ${COVERAGE_XML_FILE}) ENDIF() # provide a global coverage target executing both steps if available SET(GLOBAL_DEPENDS "") IF(LCOV_FOUND) LIST(APPEND GLOBAL_DEPENDS ${COVERAGE_REPORT_DIR}) ENDIF() IF(GCOVR_FOUND) LIST(APPEND GLOBAL_DEPENDS ${COVERAGE_XML_FILE}) ENDIF() IF(LCOV_FOUND OR GCOVR_FOUND) ADD_CUSTOM_TARGET(coverage DEPENDS ${GLOBAL_DEPENDS}) ENDIF() ENDIF() ENDFUNCTION() history-service-0.1+14.04.20140407/cmake/modules/Findgcovr.cmake0000644000015301777760000000170212320627220024557 0ustar pbusernogroup00000000000000# - Find gcovr scrip # Will define: # # GCOVR_EXECUTABLE - the gcovr script # # Uses: # # GCOVR_ROOT - root to search for the script # # Copyright (C) 2011 by Johannes Wienke # # 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 2, 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. # INCLUDE(FindPackageHandleStandardArgs) FIND_PROGRAM(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin") FIND_PACKAGE_HANDLE_STANDARD_ARGS(gcovr DEFAULT_MSG GCOVR_EXECUTABLE) # only visible in advanced view MARK_AS_ADVANCED(GCOVR_EXECUTABLE) history-service-0.1+14.04.20140407/cmake/modules/ParseArguments.cmake0000644000015301777760000000340612320627220025601 0ustar pbusernogroup00000000000000# Parse arguments passed to a function into several lists separated by # upper-case identifiers and options that do not have an associated list e.g.: # # SET(arguments # hello OPTION3 world # LIST3 foo bar # OPTION2 # LIST1 fuz baz # ) # PARSE_ARGUMENTS(ARG "LIST1;LIST2;LIST3" "OPTION1;OPTION2;OPTION3" ${arguments}) # # results in 7 distinct variables: # * ARG_DEFAULT_ARGS: hello;world # * ARG_LIST1: fuz;baz # * ARG_LIST2: # * ARG_LIST3: foo;bar # * ARG_OPTION1: FALSE # * ARG_OPTION2: TRUE # * ARG_OPTION3: TRUE # # taken from http://www.cmake.org/Wiki/CMakeMacroParseArguments MACRO(PARSE_ARGUMENTS prefix arg_names option_names) SET(DEFAULT_ARGS) FOREACH(arg_name ${arg_names}) SET(${prefix}_${arg_name}) ENDFOREACH(arg_name) FOREACH(option ${option_names}) SET(${prefix}_${option} FALSE) ENDFOREACH(option) SET(current_arg_name DEFAULT_ARGS) SET(current_arg_list) FOREACH(arg ${ARGN}) SET(larg_names ${arg_names}) LIST(FIND larg_names "${arg}" is_arg_name) IF (is_arg_name GREATER -1) SET(${prefix}_${current_arg_name} ${current_arg_list}) SET(current_arg_name ${arg}) SET(current_arg_list) ELSE (is_arg_name GREATER -1) SET(loption_names ${option_names}) LIST(FIND loption_names "${arg}" is_option) IF (is_option GREATER -1) SET(${prefix}_${arg} TRUE) ELSE (is_option GREATER -1) SET(current_arg_list ${current_arg_list} ${arg}) ENDIF (is_option GREATER -1) ENDIF (is_arg_name GREATER -1) ENDFOREACH(arg) SET(${prefix}_${current_arg_name} ${current_arg_list}) ENDMACRO(PARSE_ARGUMENTS) history-service-0.1+14.04.20140407/Ubuntu/0000755000015301777760000000000012320627745020402 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/Ubuntu/History/0000755000015301777760000000000012320627745022043 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlunionfilter.cpp0000644000015301777760000000217112320627220027226 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historyqmlunionfilter.h" #include "unionfilter.h" HistoryQmlUnionFilter::HistoryQmlUnionFilter(QObject *parent) : HistoryQmlCompoundFilter(parent) { } History::Filter HistoryQmlUnionFilter::filter() const { History::UnionFilter unionFilter; Q_FOREACH(HistoryQmlFilter *filter, mFilters) { unionFilter.append(filter->filter()); } return unionFilter; } history-service-0.1+14.04.20140407/Ubuntu/History/historythreadmodel.cpp0000644000015301777760000002745412320627220026461 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historythreadmodel.h" #include "thread.h" #include "historyqmlfilter.h" #include "historyqmlsort.h" #include "manager.h" #include "threadview.h" #include "textevent.h" #include "texteventattachment.h" #include "historyqmltexteventattachment.h" #include "voiceevent.h" #include #include Q_DECLARE_METATYPE(History::TextEventAttachments) HistoryThreadModel::HistoryThreadModel(QObject *parent) : QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), mSort(0), mType(EventTypeText), mFetchTimer(0) { // configure the roles mRoles[AccountIdRole] = "accountId"; mRoles[ThreadIdRole] = "threadId"; mRoles[TypeRole] = "type"; mRoles[ParticipantsRole] = "participants"; mRoles[CountRole] = "count"; mRoles[UnreadCountRole] = "unreadCount"; // roles related to the thread´s last event mRoles[LastEventIdRole] = "eventId"; mRoles[LastEventSenderIdRole] = "eventSenderId"; mRoles[LastEventTimestampRole] = "eventTimestamp"; mRoles[LastEventDateRole] = "eventDate"; mRoles[LastEventNewRole] = "eventNew"; mRoles[LastEventTextMessageRole] = "eventTextMessage"; mRoles[LastEventTextMessageTypeRole] = "eventTextMessageType"; mRoles[LastEventTextMessageStatusRole] = "eventTextMessageStatus"; mRoles[LastEventTextReadTimestampRole] = "eventTextReadTimestamp"; mRoles[LastEventTextAttachmentsRole] = "eventTextAttachments"; mRoles[LastEventTextSubjectRole] = "eventTextSubject"; mRoles[LastEventCallMissedRole] = "eventCallMissed"; mRoles[LastEventCallDurationRole] = "eventCallDuration"; // create the results view updateQuery(); } int HistoryThreadModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return mThreads.count(); } QVariant HistoryThreadModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= mThreads.count()) { return QVariant(); } History::Thread thread = mThreads[index.row()]; History::Event event = thread.lastEvent(); History::TextEvent textEvent; History::VoiceEvent voiceEvent; if (!event.isNull()) { switch (event.type()) { case History::EventTypeText: textEvent = event; break; case History::EventTypeVoice: voiceEvent = event; break; } } QVariant result; switch (role) { case AccountIdRole: result = thread.accountId(); break; case ThreadIdRole: result = thread.threadId(); break; case TypeRole: result = (int) thread.type(); break; case ParticipantsRole: result = thread.participants(); break; case CountRole: result = thread.count(); break; case UnreadCountRole: result = thread.unreadCount(); break; case LastEventIdRole: if (!event.isNull()) { result = event.eventId(); } break; case LastEventSenderIdRole: if (!event.isNull()) { result = event.senderId(); } break; case LastEventTimestampRole: if (!event.isNull()) { result = event.timestamp(); } break; case LastEventDateRole: if (!event.isNull()) { result = event.timestamp().date(); } break; case LastEventNewRole: if (!event.isNull()) { result = event.newEvent(); } break; case LastEventTextMessageRole: if (!textEvent.isNull()) { result = textEvent.message(); } break; case LastEventTextMessageTypeRole: if (!textEvent.isNull()) { result = (int) textEvent.messageType(); } break; case LastEventTextMessageStatusRole: if (!textEvent.isNull()) { result = (int) textEvent.messageStatus(); } break; case LastEventTextReadTimestampRole: if (!textEvent.isNull()) { result = textEvent.readTimestamp(); } break; case LastEventTextSubjectRole: if (!textEvent.isNull()) { result = textEvent.subject(); } break; case LastEventTextAttachmentsRole: if (!textEvent.isNull()) { if (mAttachmentCache.contains(textEvent)) { result = mAttachmentCache.value(textEvent); } else { QList attachments; Q_FOREACH(const History::TextEventAttachment &attachment, textEvent.attachments()) { attachments << QVariant::fromValue(new HistoryQmlTextEventAttachment(attachment, const_cast(this))); } mAttachmentCache[textEvent] = attachments; result = attachments; } } break; case LastEventCallMissedRole: if (!voiceEvent.isNull()) { result = voiceEvent.missed(); } break; case LastEventCallDurationRole: if (!voiceEvent.isNull()) { result = voiceEvent.duration(); } break; } return result; } bool HistoryThreadModel::canFetchMore(const QModelIndex &parent) const { if (parent.isValid()) { return false; } return mCanFetchMore; } void HistoryThreadModel::fetchMore(const QModelIndex &parent) { if (parent.isValid()) { return; } History::Threads threads = mThreadView->nextPage(); if (threads.isEmpty()) { mCanFetchMore = false; } else { beginInsertRows(QModelIndex(), mThreads.count(), mThreads.count() + threads.count() - 1); mThreads << threads; endInsertRows(); } } QHash HistoryThreadModel::roleNames() const { return mRoles; } HistoryQmlFilter *HistoryThreadModel::filter() const { return mFilter; } void HistoryThreadModel::setFilter(HistoryQmlFilter *value) { // disconnect the previous filter if (mFilter) { mFilter->disconnect(this); } mFilter = value; if (mFilter) { connect(mFilter, SIGNAL(filterChanged()), SLOT(updateQuery())); } Q_EMIT filterChanged(); updateQuery(); } HistoryQmlSort *HistoryThreadModel::sort() const { return mSort; } void HistoryThreadModel::setSort(HistoryQmlSort *value) { // disconnect the previous sort if (mSort) { mSort->disconnect(this); } mSort = value; if (mSort) { connect(mSort, SIGNAL(sortChanged()), SLOT(updateQuery())); } Q_EMIT sortChanged(); updateQuery(); } HistoryThreadModel::EventType HistoryThreadModel::type() const { return mType; } void HistoryThreadModel::setType(HistoryThreadModel::EventType value) { mType = value; Q_EMIT typeChanged(); updateQuery(); } QString HistoryThreadModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create) { if (participants.isEmpty()) { return QString::null; } History::Thread thread = History::Manager::instance()->threadForParticipants(accountId, (History::EventType)eventType, participants, (History::MatchFlags)matchFlags, create); if (!thread.isNull()) { return thread.threadId(); } return QString::null; } bool HistoryThreadModel::removeThread(const QString &accountId, const QString &threadId, int eventType) { History::Thread thread = History::Manager::instance()->getSingleThread((History::EventType)eventType, accountId, threadId); return History::Manager::instance()->removeThreads(History::Threads() << thread); } void HistoryThreadModel::updateQuery() { // remove all events from the model if (!mThreads.isEmpty()) { beginRemoveRows(QModelIndex(), 0, mThreads.count() - 1); mThreads.clear(); endRemoveRows(); } // and fetch again mCanFetchMore = true; History::Filter queryFilter; History::Sort querySort; if (!mThreadView.isNull()) { mThreadView->disconnect(this); } if (mFilter) { queryFilter = mFilter->filter(); } if (mSort) { querySort = mSort->sort(); } mThreadView = History::Manager::instance()->queryThreads((History::EventType)mType, querySort, queryFilter); connect(mThreadView.data(), SIGNAL(threadsAdded(History::Threads)), SLOT(onThreadsAdded(History::Threads))); connect(mThreadView.data(), SIGNAL(threadsModified(History::Threads)), SLOT(onThreadsModified(History::Threads))); connect(mThreadView.data(), SIGNAL(threadsRemoved(History::Threads)), SLOT(onThreadsRemoved(History::Threads))); connect(mThreadView.data(), SIGNAL(invalidated()), SLOT(updateQuery())); Q_FOREACH(const QVariant &attachment, mAttachmentCache) { HistoryQmlTextEventAttachment *qmlAttachment = attachment.value(); if(qmlAttachment) { qmlAttachment->deleteLater(); } } mAttachmentCache.clear(); if (mFetchTimer) { killTimer(mFetchTimer); } // delay the loading just to give the settings some time to settle down mFetchTimer = startTimer(100); } void HistoryThreadModel::onThreadsAdded(const History::Threads &threads) { if (threads.isEmpty()) { return; } // FIXME: handle sorting beginInsertRows(QModelIndex(), mThreads.count(), mThreads.count() + threads.count() - 1); mThreads << threads; endInsertRows(); } void HistoryThreadModel::onThreadsModified(const History::Threads &threads) { Q_FOREACH(const History::Thread &thread, threads) { int pos = mThreads.indexOf(thread); if (pos >= 0) { mThreads[pos] = thread; QModelIndex idx = index(pos); Q_EMIT dataChanged(idx, idx); } } // FIXME: append modified threads that are not loaded yet and make sure they don´t // get added twice to the model } void HistoryThreadModel::onThreadsRemoved(const History::Threads &threads) { Q_FOREACH(const History::Thread &thread, threads) { int pos = mThreads.indexOf(thread); if (pos >= 0) { beginRemoveRows(QModelIndex(), pos, pos); mThreads.removeAt(pos); endRemoveRows(); } } // FIXME: there is a corner case here: if a thread was not loaded yet, but was already // removed by another client, it will still show up when a new page is requested. Maybe it // should be handle internally in History::ThreadView? } void HistoryThreadModel::timerEvent(QTimerEvent *event) { if (event->timerId() == mFetchTimer) { killTimer(mFetchTimer); mFetchTimer = 0; fetchMore(QModelIndex()); } } history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlintersectionfilter.h0000644000015301777760000000220012320627220030242 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYQMLINTERSECTIONFILTER_H #define HISTORYQMLINTERSECTIONFILTER_H #include "historyqmlfilter.h" #include "types.h" #include "intersectionfilter.h" class HistoryQmlIntersectionFilter : public HistoryQmlCompoundFilter { Q_OBJECT public: explicit HistoryQmlIntersectionFilter(QObject *parent = 0); History::Filter filter() const; }; #endif // HISTORYQMLINTERSECTIONFILTER_H history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlfilter.h0000644000015301777760000000551212320627220025624 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYQMLFILTER_H #define HISTORYQMLFILTER_H #include #include #include "types.h" #include "filter.h" class HistoryQmlFilter : public QObject { Q_OBJECT Q_ENUMS(MatchFlag) Q_PROPERTY(QString filterProperty READ filterProperty WRITE setFilterProperty NOTIFY filterPropertyChanged) Q_PROPERTY(QVariant filterValue READ filterValue WRITE setFilterValue NOTIFY filterValueChanged) Q_PROPERTY(int matchFlags READ matchFlags WRITE setMatchFlags NOTIFY matchFlagsChanged) public: enum MatchFlag { MatchCaseSensitive = History::MatchCaseSensitive, MatchCaseInsensitive = History::MatchCaseInsensitive, MatchContains = History::MatchContains, MatchPhoneNumber = History::MatchPhoneNumber }; explicit HistoryQmlFilter(QObject *parent = 0); QString filterProperty() const; void setFilterProperty(const QString &value); QVariant filterValue() const; void setFilterValue(const QVariant &value); int matchFlags() const; void setMatchFlags(int flags); virtual History::Filter filter() const; Q_SIGNALS: void filterPropertyChanged(); void filterValueChanged(); void matchFlagsChanged(); void filterChanged(); protected: History::Filter mFilter; }; // compound filter class HistoryQmlCompoundFilter : public HistoryQmlFilter { Q_OBJECT Q_PROPERTY(QQmlListProperty filters READ filters NOTIFY filtersChanged) Q_CLASSINFO("DefaultProperty", "filters") public: explicit HistoryQmlCompoundFilter(QObject* parent = 0); virtual ~HistoryQmlCompoundFilter(); QQmlListProperty filters(); static void filtersAppend(QQmlListProperty* prop, HistoryQmlFilter* filter); static int filtersCount(QQmlListProperty* prop); static HistoryQmlFilter* filtersAt(QQmlListProperty* prop, int index); static void filtersClear(QQmlListProperty* prop); Q_SIGNALS: void filtersChanged(); protected: QList mFilters; }; #endif // HISTORYQMLFILTER_H history-service-0.1+14.04.20140407/Ubuntu/History/sortproxymodel.h0000644000015301777760000000243412320627220025315 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef SORTPROXYMODEL_H #define SORTPROXYMODEL_H #include class SortProxyModel : public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY(bool ascending READ ascending WRITE setAscending NOTIFY ascendingChanged) public: explicit SortProxyModel(QObject *parent = 0); bool ascending() const; void setAscending(bool value); private Q_SLOTS: void updateSorting(); Q_SIGNALS: void ascendingChanged(); private: bool mAscending; }; #endif // SORTPROXYMODEL_H history-service-0.1+14.04.20140407/Ubuntu/History/historyeventmodel.cpp0000644000015301777760000003060112320627220026317 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historyeventmodel.h" #include "historyqmlfilter.h" #include "historyqmlsort.h" #include "eventview.h" #include "intersectionfilter.h" #include "manager.h" #include "thread.h" #include "textevent.h" #include "texteventattachment.h" #include "historyqmltexteventattachment.h" #include "thread.h" #include "voiceevent.h" #include #include HistoryEventModel::HistoryEventModel(QObject *parent) : QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), mSort(0), mType(HistoryThreadModel::EventTypeText), mEventWritingTimer(0), mFetchTimer(0) { // configure the roles mRoles[AccountIdRole] = "accountId"; mRoles[ThreadIdRole] = "threadId"; mRoles[ParticipantsRole] = "participants"; mRoles[TypeRole] = "type"; mRoles[EventIdRole] = "eventId"; mRoles[SenderIdRole] = "senderId"; mRoles[TimestampRole] = "timestamp"; mRoles[DateRole] = "date"; mRoles[NewEventRole] = "newEvent"; mRoles[TextMessageRole] = "textMessage"; mRoles[TextMessageTypeRole] = "textMessageType"; mRoles[TextMessageStatusRole] = "textMessageStatus"; mRoles[TextMessageAttachmentsRole] = "textMessageAttachments"; mRoles[TextReadTimestampRole] = "textReadTimestamp"; mRoles[TextReadSubjectRole] = "textSubject"; mRoles[CallMissedRole] = "callMissed"; mRoles[CallDurationRole] = "callDuration"; // create the view and get some objects updateQuery(); } int HistoryEventModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return mEvents.count(); } QVariant HistoryEventModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= mEvents.count()) { return QVariant(); } History::Event event = mEvents[index.row()]; History::TextEvent textEvent; History::VoiceEvent voiceEvent; History::Thread thread; switch (event.type()) { case History::EventTypeText: textEvent = event; break; case History::EventTypeVoice: voiceEvent = event; break; } QVariant result; switch (role) { case AccountIdRole: result = event.accountId(); break; case ThreadIdRole: result = event.threadId(); break; case ParticipantsRole: result = event.participants(); break; case TypeRole: result = event.type(); break; case EventIdRole: result = event.eventId(); break; case SenderIdRole: result = event.senderId(); break; case TimestampRole: result = event.timestamp(); break; case DateRole: result = event.timestamp().date(); break; case NewEventRole: result = event.newEvent(); break; case TextMessageRole: if (!textEvent.isNull()) { result = textEvent.message(); } break; case TextMessageTypeRole: if (!textEvent.isNull()) { result = (int)textEvent.messageType(); } break; case TextMessageStatusRole: if (!textEvent.isNull()) { result = (int)textEvent.messageStatus(); } break; case TextReadTimestampRole: if (!textEvent.isNull()) { result = textEvent.readTimestamp(); } break; case TextReadSubjectRole: if (!textEvent.isNull()) { result = textEvent.subject(); } break; case TextMessageAttachmentsRole: if (!textEvent.isNull()) { if (mAttachmentCache.contains(textEvent)) { result = mAttachmentCache.value(textEvent); } else { QList attachments; Q_FOREACH(const History::TextEventAttachment &attachment, textEvent.attachments()) { attachments << QVariant::fromValue(new HistoryQmlTextEventAttachment(attachment, const_cast(this))); } mAttachmentCache[textEvent] = attachments; result = attachments; } } break; case CallMissedRole: if (!voiceEvent.isNull()) { result = voiceEvent.missed(); } break; case CallDurationRole: if (!voiceEvent.isNull()) { result = voiceEvent.duration(); } break; } return result; } bool HistoryEventModel::canFetchMore(const QModelIndex &parent) const { if (parent.isValid()) { return false; } return mCanFetchMore; } void HistoryEventModel::fetchMore(const QModelIndex &parent) { if (parent.isValid()) { return; } History::Events events = mView->nextPage(); qDebug() << "Got events:" << events.count(); if (events.isEmpty()) { mCanFetchMore = false; } else { beginInsertRows(QModelIndex(), mEvents.count(), mEvents.count() + events.count() - 1); mEvents << events; endInsertRows(); } } QHash HistoryEventModel::roleNames() const { return mRoles; } HistoryQmlFilter *HistoryEventModel::filter() const { return mFilter; } void HistoryEventModel::setFilter(HistoryQmlFilter *value) { if (mFilter) { mFilter->disconnect(this); } mFilter = value; if (mFilter) { connect(mFilter, SIGNAL(filterChanged()), SLOT(updateQuery())); } Q_EMIT filterChanged(); updateQuery(); } HistoryQmlSort *HistoryEventModel::sort() const { return mSort; } void HistoryEventModel::setSort(HistoryQmlSort *value) { // disconnect the previous sort if (mSort) { mSort->disconnect(this); } mSort = value; if (mSort) { connect(mSort, SIGNAL(sortChanged()), SLOT(updateQuery())); } Q_EMIT sortChanged(); updateQuery(); } HistoryThreadModel::EventType HistoryEventModel::type() const { return mType; } void HistoryEventModel::setType(HistoryThreadModel::EventType value) { mType = value; Q_EMIT typeChanged(); updateQuery(); } QString HistoryEventModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create) { if (participants.isEmpty()) { return QString::null; } History::Thread thread = History::Manager::instance()->threadForParticipants(accountId, (History::EventType)eventType, participants, (History::MatchFlags)matchFlags, create); if (!thread.isNull()) { return thread.threadId(); } return QString::null; } bool HistoryEventModel::removeEvent(const QString &accountId, const QString &threadId, const QString &eventId, int eventType) { History::Event event = History::Manager::instance()->getSingleEvent((History::EventType)eventType, accountId, threadId, eventId); return History::Manager::instance()->removeEvents(History::Events() << event); } bool HistoryEventModel::markEventAsRead(const QString &accountId, const QString &threadId, const QString &eventId, int eventType) { History::Event event = History::Manager::instance()->getSingleEvent((History::EventType)eventType, accountId, threadId, eventId); event.setNewEvent(false); if (event.type() == History::EventTypeText) { History::TextEvent textEvent = event; textEvent.setReadTimestamp(QDateTime::currentDateTime()); event = textEvent; } mEventWritingQueue << event; if (mEventWritingTimer != 0) { killTimer(mEventWritingTimer); } mEventWritingTimer = startTimer(500); return true; } void HistoryEventModel::updateQuery() { // remove all events from the model if (!mEvents.isEmpty()) { beginRemoveRows(QModelIndex(), 0, mEvents.count() - 1); mEvents.clear(); endRemoveRows(); } // and create the view again History::Filter queryFilter; History::Sort querySort; if (!mView.isNull()) { mView->disconnect(this); } if (mFilter) { queryFilter = mFilter->filter(); } if (mSort) { querySort = mSort->sort(); } mView = History::Manager::instance()->queryEvents((History::EventType)mType, querySort, queryFilter); connect(mView.data(), SIGNAL(eventsAdded(History::Events)), SLOT(onEventsAdded(History::Events))); connect(mView.data(), SIGNAL(eventsModified(History::Events)), SLOT(onEventsModified(History::Events))); connect(mView.data(), SIGNAL(eventsRemoved(History::Events)), SLOT(onEventsRemoved(History::Events))); connect(mView.data(), SIGNAL(invalidated()), SLOT(updateQuery())); mCanFetchMore = true; Q_FOREACH(const QVariant &attachment, mAttachmentCache) { HistoryQmlTextEventAttachment *qmlAttachment = attachment.value(); if(qmlAttachment) { qmlAttachment->deleteLater(); } } mAttachmentCache.clear(); if (mFetchTimer) { killTimer(mFetchTimer); } // delay the loading of the model data until the settings settle down mFetchTimer = startTimer(100); } void HistoryEventModel::onEventsAdded(const History::Events &events) { if (!events.count()) { return; } // filter the list for items already in the model History::Events filteredEvents; Q_FOREACH(const History::Event &event, events) { if (!mEvents.contains(event)) { filteredEvents << event; } } //FIXME: handle sorting beginInsertRows(QModelIndex(), mEvents.count(), mEvents.count() + filteredEvents.count() - 1); mEvents << filteredEvents; endInsertRows(); } void HistoryEventModel::onEventsModified(const History::Events &events) { History::Events newEvents; Q_FOREACH(const History::Event &event, events) { int pos = mEvents.indexOf(event); if (pos >= 0) { mEvents[pos] = event; QModelIndex idx = index(pos); Q_EMIT dataChanged(idx, idx); } else { newEvents << event; } } // append the events that were not yet on the model if (!newEvents.isEmpty()) { onEventsAdded(newEvents); } } void HistoryEventModel::onEventsRemoved(const History::Events &events) { Q_FOREACH(const History::Event &event, events) { int pos = mEvents.indexOf(event); if (pos >= 0) { beginRemoveRows(QModelIndex(), pos, pos); mEvents.removeAt(pos); endRemoveRows(); } } // FIXME: there is a corner case here: if an event was not loaded yet, but was already // removed by another client, it will still show up when a new page is requested. Maybe it // should be handle internally in History::EventView? } void HistoryEventModel::timerEvent(QTimerEvent *event) { if (event->timerId() == mEventWritingTimer) { killTimer(mEventWritingTimer); mEventWritingTimer = 0; if (mEventWritingQueue.isEmpty()) { return; } qDebug() << "Goint to update" << mEventWritingQueue.count() << "events."; if (History::Manager::instance()->writeEvents(mEventWritingQueue)) { qDebug() << "... succeeded!"; mEventWritingQueue.clear(); } } else if (event->timerId() == mFetchTimer) { killTimer(mFetchTimer); mFetchTimer = 0; fetchMore(QModelIndex()); } } history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlsort.h0000644000015301777760000000401012320627220025316 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYQMLSORT_H #define HISTORYQMLSORT_H #include #include "types.h" #include "sort.h" class HistoryQmlSort : public QObject { Q_OBJECT Q_ENUMS(SortOrder) Q_ENUMS(CaseSensitivity) Q_PROPERTY(QString sortField READ sortField WRITE setSortField NOTIFY sortFieldChanged) Q_PROPERTY(SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) Q_PROPERTY(CaseSensitivity caseSensitivity READ CaseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) public: enum SortOrder { AscendingOrder = Qt::AscendingOrder, DescendingOrder = Qt::DescendingOrder }; enum CaseSensitivity { CaseInsensitive = Qt::CaseInsensitive, CaseSensitive = Qt::CaseSensitive }; explicit HistoryQmlSort(QObject *parent = 0); QString sortField() const; void setSortField(const QString &value); SortOrder sortOrder() const; void setSortOrder(SortOrder order); CaseSensitivity caseSensitivity() const; void setCaseSensitivity(CaseSensitivity value); History::Sort sort() const; Q_SIGNALS: void sortChanged(); void sortFieldChanged(); void sortOrderChanged(); void caseSensitivityChanged(); private: History::Sort mSort; }; #endif // HISTORYQMLSORT_H history-service-0.1+14.04.20140407/Ubuntu/History/sortproxymodel.cpp0000644000015301777760000000243712320627220025653 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "sortproxymodel.h" #include SortProxyModel::SortProxyModel(QObject *parent) : QSortFilterProxyModel(parent), mAscending(true) { setDynamicSortFilter(true); updateSorting(); } bool SortProxyModel::ascending() const { return mAscending; } void SortProxyModel::setAscending(bool value) { if (mAscending != value) { mAscending = value; updateSorting(); Q_EMIT ascendingChanged(); } } void SortProxyModel::updateSorting() { sort(0, mAscending ? Qt::AscendingOrder : Qt::DescendingOrder); } history-service-0.1+14.04.20140407/Ubuntu/History/historyqmltexteventattachment.cpp0000644000015301777760000000331612320627220030771 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "texteventattachment.h" #include "historyqmltexteventattachment.h" HistoryQmlTextEventAttachment::HistoryQmlTextEventAttachment(const History::TextEventAttachment &attachment, QObject *parent) : QObject(parent), mAttachment(attachment) { } QString HistoryQmlTextEventAttachment::accountId() const { return mAttachment.accountId(); } QString HistoryQmlTextEventAttachment::threadId() const { return mAttachment.threadId(); } QString HistoryQmlTextEventAttachment::eventId() const { return mAttachment.eventId(); } QString HistoryQmlTextEventAttachment::attachmentId() const { return mAttachment.attachmentId(); } QString HistoryQmlTextEventAttachment::contentType() const { return mAttachment.contentType(); } QString HistoryQmlTextEventAttachment::filePath() const { return mAttachment.filePath(); } int HistoryQmlTextEventAttachment::status() const { return mAttachment.status(); } history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlunionfilter.h0000644000015301777760000000212212320627220026667 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYQMLUNIONFILTER_H #define HISTORYQMLUNIONFILTER_H #include "historyqmlfilter.h" #include #include class HistoryQmlUnionFilter : public HistoryQmlCompoundFilter { Q_OBJECT public: explicit HistoryQmlUnionFilter(QObject *parent = 0); History::Filter filter() const; }; #endif // HISTORYQMLUNIONFILTER_H history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlsort.cpp0000644000015301777760000000371412320627220025663 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historyqmlsort.h" #include "sort.h" HistoryQmlSort::HistoryQmlSort(QObject *parent) : QObject(parent) { connect(this, SIGNAL(sortFieldChanged()), SIGNAL(sortChanged())); connect(this, SIGNAL(sortOrderChanged()), SIGNAL(sortChanged())); connect(this, SIGNAL(caseSensitivityChanged()), SIGNAL(sortChanged())); } QString HistoryQmlSort::sortField() const { return mSort.sortField(); } void HistoryQmlSort::setSortField(const QString &value) { mSort.setSortField(value); Q_EMIT sortFieldChanged(); } HistoryQmlSort::SortOrder HistoryQmlSort::sortOrder() const { return (SortOrder) mSort.sortOrder(); } void HistoryQmlSort::setSortOrder(HistoryQmlSort::SortOrder order) { mSort.setSortOrder((Qt::SortOrder) order); Q_EMIT sortOrderChanged(); } HistoryQmlSort::CaseSensitivity HistoryQmlSort::caseSensitivity() const { return (CaseSensitivity) mSort.caseSensitivity(); } void HistoryQmlSort::setCaseSensitivity(HistoryQmlSort::CaseSensitivity value) { mSort.setCaseSensitivity((Qt::CaseSensitivity) value); Q_EMIT caseSensitivityChanged(); } History::Sort HistoryQmlSort::sort() const { return mSort; } history-service-0.1+14.04.20140407/Ubuntu/History/historyqmltexteventattachment.h0000644000015301777760000000406112320627220030434 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYQMLTEXTEVENTATTACHMENT_H #define HISTORYQMLTEXTEVENTATTACHMENT_H #include #include #include "historyqmltexteventattachment.h" #include "types.h" #include "texteventattachment.h" class HistoryQmlTextEventAttachment : public QObject { Q_OBJECT Q_ENUMS(AttachmentFlag) Q_PROPERTY(QString accountId READ accountId) Q_PROPERTY(QString threadId READ threadId) Q_PROPERTY(QString eventId READ eventId) Q_PROPERTY(QString attachmentId READ attachmentId) Q_PROPERTY(QString contentType READ contentType) Q_PROPERTY(QString filePath READ filePath) Q_PROPERTY(int status READ status) public: enum AttachmentFlag { AttachmentDownloaded = History::AttachmentDownloaded, AttachmentPending = History::AttachmentPending, AttachmentError = History::AttachmentError }; explicit HistoryQmlTextEventAttachment(const History::TextEventAttachment &attachment, QObject *parent = 0); QString accountId() const; QString threadId() const; QString eventId() const; QString attachmentId() const; QString contentType() const; QString filePath() const; int status() const; protected: History::TextEventAttachment mAttachment; }; #endif // HISTORYQMLTEXTEVENTATTACHMENT_H history-service-0.1+14.04.20140407/Ubuntu/History/historythreadmodel.h0000644000015301777760000001111712320627220026113 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYTHREADMODEL_H #define HISTORYTHREADMODEL_H #include #include "types.h" #include "textevent.h" #include "thread.h" class HistoryQmlFilter; class HistoryQmlSort; class HistoryThreadModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged) Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged) Q_ENUMS(EventType) Q_ENUMS(Role) Q_ENUMS(MatchFlag) Q_ENUMS(MessageStatus) public: enum EventType { EventTypeText = History::EventTypeText, EventTypeVoice = History::EventTypeVoice }; enum MatchFlag { MatchCaseSensitive = History::MatchCaseSensitive, MatchCaseInsensitive = History::MatchCaseInsensitive, MatchContains = History::MatchContains, MatchPhoneNumber = History::MatchPhoneNumber }; enum MessageStatus { MessageStatusUnknown = History::MessageStatusUnknown, MessageStatusDelivered = History::MessageStatusDelivered, MessageStatusTemporarilyFailed = History::MessageStatusTemporarilyFailed, MessageStatusPermanentlyFailed = History::MessageStatusPermanentlyFailed, MessageStatusAccepted = History::MessageStatusAccepted, MessageStatusRead = History::MessageStatusRead, MessageStatusDeleted = History::MessageStatusDeleted, MessageStatusPending = History::MessageStatusPending // pending attachment download }; enum Role { AccountIdRole = Qt::UserRole, ThreadIdRole, TypeRole, ParticipantsRole, CountRole, UnreadCountRole, LastEventIdRole, LastEventSenderIdRole, LastEventTimestampRole, LastEventDateRole, LastEventNewRole, LastEventTextMessageRole, LastEventTextMessageTypeRole, LastEventTextMessageStatusRole, LastEventTextReadTimestampRole, LastEventTextSubjectRole, LastEventTextAttachmentsRole, LastEventCallMissedRole, LastEventCallDurationRole }; explicit HistoryThreadModel(QObject *parent = 0); int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; bool canFetchMore(const QModelIndex &parent) const; void fetchMore(const QModelIndex &parent); QHash roleNames() const; HistoryQmlFilter *filter() const; void setFilter(HistoryQmlFilter *value); HistoryQmlSort *sort() const; void setSort(HistoryQmlSort *value); EventType type() const; void setType(EventType value); Q_INVOKABLE QString threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags = (int)History::MatchCaseSensitive, bool create = false); Q_INVOKABLE bool removeThread(const QString &accountId, const QString &threadId, int eventType); Q_SIGNALS: void filterChanged(); void sortChanged(); void typeChanged(); protected Q_SLOTS: void updateQuery(); void onThreadsAdded(const History::Threads &threads); void onThreadsModified(const History::Threads &threads); void onThreadsRemoved(const History::Threads &threads); protected: void timerEvent(QTimerEvent *event); private: History::ThreadViewPtr mThreadView; History::Threads mThreads; bool mCanFetchMore; HistoryQmlFilter *mFilter; HistoryQmlSort *mSort; EventType mType; QHash mRoles; mutable QMap > mAttachmentCache; int mFetchTimer; }; #endif // HISTORYTHREADMODEL_H history-service-0.1+14.04.20140407/Ubuntu/History/qmldir0000644000015301777760000000005112320627220023236 0ustar pbusernogroup00000000000000module Ubuntu.History plugin history-qml history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlintersectionfilter.cpp0000644000015301777760000000227012320627220030604 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historyqmlintersectionfilter.h" #include "intersectionfilter.h" HistoryQmlIntersectionFilter::HistoryQmlIntersectionFilter(QObject *parent) : HistoryQmlCompoundFilter(parent) { } History::Filter HistoryQmlIntersectionFilter::filter() const { History::IntersectionFilter intersectionFilter; Q_FOREACH(HistoryQmlFilter *filter, mFilters) { intersectionFilter.append(filter->filter()); } return intersectionFilter; } history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlplugin.cpp0000644000015301777760000000376312320627220026176 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historyqmlplugin.h" #include "historyqmlfilter.h" #include "historyqmlintersectionfilter.h" #include "historyqmlsort.h" #include "historyqmlunionfilter.h" #include "historythreadmodel.h" #include "historyeventmodel.h" #include "historyqmltexteventattachment.h" #include "sortproxymodel.h" #include #include void HistoryQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { // FIXME: check what to do here Q_UNUSED(engine) Q_UNUSED(uri) } void HistoryQmlPlugin::registerTypes(const char *uri) { // @uri History qmlRegisterType(uri, 0, 1, "HistoryEventModel"); qmlRegisterType(uri, 0, 1, "HistoryThreadModel"); qmlRegisterType(uri, 0, 1, "HistoryFilter"); qmlRegisterType(uri, 0, 1, "HistoryIntersectionFilter"); qmlRegisterType(uri, 0, 1, "HistorySort"); qmlRegisterType(uri, 0, 1, "HistoryUnionFilter"); qmlRegisterType(uri, 0, 1, "SortProxyModel"); qmlRegisterUncreatableType(uri, 0, 1, "HistoryTextEventAttachment", ""); qmlRegisterUncreatableType(uri, 0, 1, "QAbstractItemModel", ""); } history-service-0.1+14.04.20140407/Ubuntu/History/CMakeLists.txt0000644000015301777760000000163212320627220024571 0ustar pbusernogroup00000000000000# QML plugin set(plugin_SRCS historyeventmodel.cpp historyqmltexteventattachment.cpp historyqmlfilter.cpp historyqmlintersectionfilter.cpp historyqmlplugin.cpp historyqmlsort.cpp historyqmlunionfilter.cpp historythreadmodel.cpp sortproxymodel.cpp ) set(plugin_HDRS historyeventmodel.h historyqmltexteventattachment.h historyqmlfilter.h historyqmlintersectionfilter.h historyqmlplugin.h historyqmlsort.h historyqmlunionfilter.h historythreadmodel.h sortproxymodel.h ) include_directories( ${CMAKE_SOURCE_DIR}/src ) add_library(history-qml SHARED ${plugin_SRCS} ${plugin_HDRS}) qt5_use_modules(history-qml Core Qml Quick) target_link_libraries(history-qml historyservice ) set(PLUGIN_DIR ${QT_INSTALL_QML}/Ubuntu/History) install(TARGETS history-qml DESTINATION ${PLUGIN_DIR}) install(FILES qmldir DESTINATION ${PLUGIN_DIR}) history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlplugin.h0000644000015301777760000000214412320627220025633 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYQMLPLUGIN_H #define HISTORYQMLPLUGIN_H #include class HistoryQmlPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void initializeEngine(QQmlEngine *engine, const char *uri); void registerTypes(const char *uri); }; #endif // HISTORYQMLPLUGIN_H history-service-0.1+14.04.20140407/Ubuntu/History/historyeventmodel.h0000644000015301777760000000720612320627220025771 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYEVENTMODEL_H #define HISTORYEVENTMODEL_H #include #include #include "historythreadmodel.h" class HistoryEventModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged) Q_PROPERTY(HistoryThreadModel::EventType type READ type WRITE setType NOTIFY typeChanged) Q_ENUMS(Role) public: enum Role { AccountIdRole = Qt::UserRole, ThreadIdRole, ParticipantsRole, TypeRole, EventIdRole, SenderIdRole, TimestampRole, DateRole, NewEventRole, TextMessageRole, TextMessageTypeRole, TextMessageStatusRole, TextReadTimestampRole, TextReadSubjectRole, TextMessageAttachmentsRole, CallMissedRole, CallDurationRole }; explicit HistoryEventModel(QObject *parent = 0); int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; bool canFetchMore(const QModelIndex &parent) const; void fetchMore(const QModelIndex &parent); QHash roleNames() const; HistoryQmlFilter *filter() const; void setFilter(HistoryQmlFilter *value); HistoryQmlSort *sort() const; void setSort(HistoryQmlSort *value); HistoryThreadModel::EventType type() const; void setType(HistoryThreadModel::EventType value); Q_INVOKABLE QString threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags = (int)History::MatchCaseSensitive, bool create = false); Q_INVOKABLE bool removeEvent(const QString &accountId, const QString &threadId, const QString &eventId, int eventType); Q_INVOKABLE bool markEventAsRead(const QString &accountId, const QString &threadId, const QString &eventId, int eventType); Q_SIGNALS: void filterChanged(); void sortChanged(); void typeChanged(); protected Q_SLOTS: void updateQuery(); void onEventsAdded(const History::Events &events); void onEventsModified(const History::Events &events); void onEventsRemoved(const History::Events &events); protected: void timerEvent(QTimerEvent *event); private: History::EventViewPtr mView; History::Events mEvents; bool mCanFetchMore; HistoryQmlFilter *mFilter; HistoryQmlSort *mSort; HistoryThreadModel::EventType mType; QHash mRoles; mutable QMap > mAttachmentCache; History::Events mEventWritingQueue; int mEventWritingTimer; int mFetchTimer; }; #endif // HISTORYEVENTMODEL_H history-service-0.1+14.04.20140407/Ubuntu/History/historyqmlfilter.cpp0000644000015301777760000000742112320627220026160 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historyqmlfilter.h" #include "filter.h" HistoryQmlFilter::HistoryQmlFilter(QObject *parent) : QObject(parent) { connect(this, SIGNAL(filterPropertyChanged()), SIGNAL(filterChanged())); connect(this, SIGNAL(filterValueChanged()), SIGNAL(filterChanged())); connect(this, SIGNAL(matchFlagsChanged()), SIGNAL(filterChanged())); } QString HistoryQmlFilter::filterProperty() const { return mFilter.filterProperty(); } void HistoryQmlFilter::setFilterProperty(const QString &value) { mFilter.setFilterProperty(value); Q_EMIT filterPropertyChanged(); } QVariant HistoryQmlFilter::filterValue() const { return mFilter.filterValue(); } void HistoryQmlFilter::setFilterValue(const QVariant &value) { mFilter.setFilterValue(value); Q_EMIT filterValueChanged(); } int HistoryQmlFilter::matchFlags() const { return mFilter.matchFlags(); } void HistoryQmlFilter::setMatchFlags(int flags) { mFilter.setMatchFlags((History::MatchFlags)flags); Q_EMIT matchFlagsChanged(); } History::Filter HistoryQmlFilter::filter() const { return mFilter; } HistoryQmlCompoundFilter::HistoryQmlCompoundFilter(QObject *parent) : HistoryQmlFilter(parent) { } HistoryQmlCompoundFilter::~HistoryQmlCompoundFilter() { } QQmlListProperty HistoryQmlCompoundFilter::filters() { return QQmlListProperty(this, 0, // opaque data filtersAppend, filtersCount, filtersAt, filtersClear); } void HistoryQmlCompoundFilter::filtersAppend(QQmlListProperty *prop, HistoryQmlFilter *filter) { HistoryQmlCompoundFilter* compoundFilter = static_cast(prop->object); compoundFilter->mFilters.append(filter); QObject::connect(filter, SIGNAL(filterChanged()), compoundFilter, SIGNAL(filterChanged()), Qt::UniqueConnection); Q_EMIT compoundFilter->filterChanged(); } int HistoryQmlCompoundFilter::filtersCount(QQmlListProperty *prop) { HistoryQmlCompoundFilter *compoundFilter = static_cast(prop->object); return compoundFilter->mFilters.count(); } HistoryQmlFilter *HistoryQmlCompoundFilter::filtersAt(QQmlListProperty *prop, int index) { HistoryQmlCompoundFilter* compoundFilter = static_cast(prop->object); return compoundFilter->mFilters[index]; } void HistoryQmlCompoundFilter::filtersClear(QQmlListProperty *prop) { HistoryQmlCompoundFilter* compoundFilter = static_cast(prop->object); if (!compoundFilter->mFilters.isEmpty()) { Q_FOREACH(HistoryQmlFilter *filter, compoundFilter->mFilters) { filter->disconnect(compoundFilter); } compoundFilter->mFilters.clear(); } } history-service-0.1+14.04.20140407/Ubuntu/CMakeLists.txt0000644000015301777760000000003212320627220023121 0ustar pbusernogroup00000000000000add_subdirectory(History) history-service-0.1+14.04.20140407/daemon/0000755000015301777760000000000012320627745020363 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/daemon/com.canonical.HistoryService.service.in0000644000015301777760000000016412320627220030024 0ustar pbusernogroup00000000000000[D-BUS Service] Name=com.canonical.HistoryService Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/history-daemon history-service-0.1+14.04.20140407/daemon/callchannelobserver.cpp0000644000015301777760000000350512320627220025072 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "callchannelobserver.h" CallChannelObserver::CallChannelObserver(QObject *parent) : QObject(parent) { } void CallChannelObserver::onCallChannelAvailable(Tp::CallChannelPtr callChannel) { // save the timestamp as a property in the call channel callChannel->setProperty("timestamp", QDateTime::currentDateTime()); if (callChannel->callState() == Tp::CallStateActive) { callChannel->setProperty("activeTimestamp", QDateTime::currentDateTime()); } connect(callChannel.data(), SIGNAL(callStateChanged(Tp::CallState)), SLOT(onCallStateChanged(Tp::CallState))); mChannels.append(callChannel); } void CallChannelObserver::onCallStateChanged(Tp::CallState state) { Tp::CallChannel *channel = qobject_cast(sender()); if (!channel) { return; } switch (state) { case Tp::CallStateEnded: Q_EMIT callEnded(Tp::CallChannelPtr(channel)); break; case Tp::CallStateActive: channel->setProperty("activeTimestamp", QDateTime::currentDateTime()); break; } } history-service-0.1+14.04.20140407/daemon/history-daemon.desktop.in0000644000015301777760000000035612320627220025315 0ustar pbusernogroup00000000000000[Desktop Entry] Name=tr("History Service Daemon") Comment=tr("Listens for calls and messages and stores them.") Icon=history-daemon Exec=history-daemon Terminal=false Type=Application NoDisplay=true X-Ubuntu-Gettext-Domain=history-daemon history-service-0.1+14.04.20140407/daemon/tests/0000755000015301777760000000000012320627745021525 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/daemon/tests/approver.h0000644000015301777760000000371212320627220023523 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann */ #ifndef APPROVER_H #define APPROVER_H #include #include #include #include #define TELEPHONY_SERVICE_HANDLER TP_QT_IFACE_CLIENT + ".HistoryTestHandler" #define TELEPHONY_SERVICE_APPROVER TP_QT_IFACE_CLIENT + ".HistoryTestApprover" class Approver : public QObject, public Tp::AbstractClientApprover { Q_OBJECT public: Approver(QObject *parent = 0); ~Approver(); Tp::ChannelClassSpecList channelFilters() const; void addDispatchOperation(const Tp::MethodInvocationContextPtr<> &context, const Tp::ChannelDispatchOperationPtr &dispatchOperation); Q_SIGNALS: void newCall(); public Q_SLOTS: void acceptCall(); void rejectCall(); private Q_SLOTS: void processChannels(); void onClaimFinished(Tp::PendingOperation* op); void onHangupFinished(Tp::PendingOperation* op); void onChannelReady(Tp::PendingOperation *op); protected: Tp::ChannelDispatchOperationPtr dispatchOperation(Tp::PendingOperation *op); private: QList mDispatchOps; QMap mChannels; }; #endif // APPROVER_H history-service-0.1+14.04.20140407/daemon/tests/mock/0000755000015301777760000000000012320627745022456 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/daemon/tests/mock/callchannel.cpp0000644000015301777760000001707612320627220025425 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #include "callchannel.h" MockCallChannel::MockCallChannel(MockConnection *conn, QString phoneNumber, QString state, uint targetHandle, QObject *parent): QObject(parent), mIncoming(false), mRequestedHangup(false), mConnection(conn), mPhoneNumber(phoneNumber), mTargetHandle(targetHandle), mState(state) { Tp::BaseChannelPtr baseChannel = Tp::BaseChannel::create(mConnection, TP_QT_IFACE_CHANNEL_TYPE_CALL, targetHandle, Tp::HandleTypeContact); Tp::BaseChannelCallTypePtr callType = Tp::BaseChannelCallType::create(baseChannel.data(), true, Tp::StreamTransportTypeUnknown, true, false, "",""); baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(callType)); mHoldIface = Tp::BaseChannelHoldInterface::create(); mHoldIface->setSetHoldStateCallback(Tp::memFun(this,&MockCallChannel::onHoldStateChanged)); mMuteIface = Tp::BaseCallMuteInterface::create(); mMuteIface->setSetMuteStateCallback(Tp::memFun(this,&MockCallChannel::onMuteStateChanged)); baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mHoldIface)); baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMuteIface)); mBaseChannel = baseChannel; mCallChannel = Tp::BaseChannelCallTypePtr::dynamicCast(mBaseChannel->interface(TP_QT_IFACE_CHANNEL_TYPE_CALL)); mCallChannel->setHangupCallback(Tp::memFun(this,&MockCallChannel::onHangup)); mCallChannel->setAcceptCallback(Tp::memFun(this,&MockCallChannel::onAccept)); // init must be called after initialization, otherwise we will have no object path registered. QTimer::singleShot(0, this, SLOT(init())); } void MockCallChannel::onHangup(uint reason, const QString &detailedReason, const QString &message, Tp::DBusError *error) { // TODO: use the parameters sent by telepathy mRequestedHangup = true; setCallState("disconnected"); } void MockCallChannel::onAccept(Tp::DBusError*) { setCallState("active"); } void MockCallChannel::init() { Tp::CallMemberMap memberFlags; Tp::HandleIdentifierMap identifiers; QVariantMap stateDetails; Tp::CallStateReason reason; mIncoming = mState == "incoming" || mState == "waiting"; identifiers[mTargetHandle] = mPhoneNumber; reason.actor = 0; reason.reason = Tp::CallStateChangeReasonProgressMade; reason.message = ""; reason.DBusReason = ""; if (mIncoming) { memberFlags[mTargetHandle] = 0; } else { memberFlags[mTargetHandle] = Tp::CallMemberFlagRinging; } mCallChannel->setCallState(Tp::CallStateInitialising, 0, reason, stateDetails); mCallContent = Tp::BaseCallContent::create(baseChannel()->dbusConnection(), baseChannel().data(), "audio", Tp::MediaStreamTypeAudio, Tp::MediaStreamDirectionNone); mDTMFIface = Tp::BaseCallContentDTMFInterface::create(); mCallContent->plugInterface(Tp::AbstractCallContentInterfacePtr::dynamicCast(mDTMFIface)); mCallChannel->addContent(mCallContent); mDTMFIface->setStartToneCallback(Tp::memFun(this,&MockCallChannel::onDTMFStartTone)); mDTMFIface->setStopToneCallback(Tp::memFun(this,&MockCallChannel::onDTMFStopTone)); mCallChannel->setMembersFlags(memberFlags, identifiers, Tp::UIntList(), reason); mCallChannel->setCallState(Tp::CallStateInitialised, 0, reason, stateDetails); QObject::connect(mBaseChannel.data(), SIGNAL(closed()), this, SLOT(deleteLater())); } void MockCallChannel::onOfonoMuteChanged(bool mute) { Tp::LocalMuteState state = mute ? Tp::LocalMuteStateMuted : Tp::LocalMuteStateUnmuted; mMuteIface->setMuteState(state); } void MockCallChannel::onHoldStateChanged(const Tp::LocalHoldState &state, const Tp::LocalHoldStateReason &reason, Tp::DBusError *error) { #if 0 FIXME: reimplement if (state == Tp::LocalHoldStateHeld && this->state() == "active") { mConnection->voiceCallManager()->swapCalls(); } else if (state == Tp::LocalHoldStateUnheld && this->state() == "held") { mConnection->voiceCallManager()->swapCalls(); } #endif } void MockCallChannel::onMuteStateChanged(const Tp::LocalMuteState &state, Tp::DBusError *error) { #if 0 FIXME: reimplement if (state == Tp::LocalMuteStateMuted) { mConnection->callVolume()->setMuted(true); } else if (state == Tp::LocalMuteStateUnmuted) { mConnection->callVolume()->setMuted(false); } #endif } void MockCallChannel::onDTMFStartTone(uchar event, Tp::DBusError *error) { QString finalString; if (event == 10) { finalString = "*"; } else if (event == 11) { finalString = "#"; } else { finalString = QString::number(event); } qDebug() << "start tone" << finalString; } void MockCallChannel::onDTMFStopTone(Tp::DBusError *error) { } MockCallChannel::~MockCallChannel() { qDebug() << "call channel closed"; // TODO - for some reason the object is not being removed mConnection->dbusConnection().unregisterObject(mObjPath, QDBusConnection::UnregisterTree); } Tp::BaseChannelPtr MockCallChannel::baseChannel() { return mBaseChannel; } void MockCallChannel::setCallState(const QString &state) { Tp::CallStateReason reason; QVariantMap stateDetails; reason.actor = 0; reason.reason = Tp::CallStateChangeReasonUserRequested; reason.message = ""; reason.DBusReason = ""; if (state == "disconnected") { qDebug() << "disconnected"; if (mIncoming && mState == "incoming" && !mRequestedHangup) { reason.reason = Tp::CallStateChangeReasonNoAnswer; } mCallChannel->setCallState(Tp::CallStateEnded, 0, reason, stateDetails); mBaseChannel->close(); } else if (state == "active") { qDebug() << "active"; mHoldIface->setHoldState(Tp::LocalHoldStateUnheld, Tp::LocalHoldStateReasonNone); if (mState == "dialing" || mState == "alerting" || mState == "incoming") { mCallChannel->setCallState(Tp::CallStateAccepted, 0, reason, stateDetails); } mCallChannel->setCallState(Tp::CallStateActive, 0, reason, stateDetails); } else if (state == "held") { mHoldIface->setHoldState(Tp::LocalHoldStateHeld, Tp::LocalHoldStateReasonNone); qDebug() << "held"; } else if (state == "dialing") { qDebug() << "dialing"; } else if (state == "alerting") { qDebug() << "alerting"; } else if (state == "incoming") { mIncoming = true; qDebug() << "incoming"; } else if (state == "waiting") { mIncoming = true; qDebug() << "waiting"; } mState = state; } history-service-0.1+14.04.20140407/daemon/tests/mock/dbustypes.h0000644000015301777760000000232212320627220024634 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann */ #ifndef DBUSTYPES #define DBUSTYPES struct MessageStruct { QDBusObjectPath path; QVariantMap properties; }; struct AttachmentStruct { QString id; QString contentType; QString filePath; quint64 offset; quint64 length; }; typedef QList AttachmentList; Q_DECLARE_METATYPE(AttachmentStruct) Q_DECLARE_METATYPE(AttachmentList) typedef QList MessageList; Q_DECLARE_METATYPE(MessageStruct) Q_DECLARE_METATYPE(MessageList) #endif history-service-0.1+14.04.20140407/daemon/tests/mock/mockconnectiondbus.cpp0000644000015301777760000000441212320627220027036 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "connection.h" #include "mockconnectiondbus.h" #include "mockconnectionadaptor.h" #include "types.h" Q_DECLARE_METATYPE(QList< QVariantMap >) MockConnectionDBus::MockConnectionDBus(MockConnection *parent) : QObject(parent), mAdaptor(0), mConnection(parent) { connect(mConnection, SIGNAL(messageSent(QString,QVariantMap)), SIGNAL(MessageSent(QString,QVariantMap))); qDBusRegisterMetaType >(); connectToBus(); } bool MockConnectionDBus::connectToBus() { bool ok = QDBusConnection::sessionBus().registerService("com.canonical.MockConnection"); if (!ok) { return false; } if (!mAdaptor) { mAdaptor = new MockConnectionAdaptor(this); } return QDBusConnection::sessionBus().registerObject("/com/canonical/MockConnection", this); } void MockConnectionDBus::PlaceIncomingMessage(const QString &message, const QVariantMap &properties) { mConnection->placeIncomingMessage(message, properties); } void MockConnectionDBus::PlaceCall(const QVariantMap &properties) { mConnection->placeCall(properties); } void MockConnectionDBus::HangupCall(const QString &callerId) { mConnection->hangupCall(callerId); } void MockConnectionDBus::SetCallState(const QString &phoneNumber, const QString &state) { mConnection->setCallState(phoneNumber, state); } void MockConnectionDBus::SendDeliveryReport(const QString &phoneNumber, const QString &messageId, const QString &status) { mConnection->sendDeliveryReport(phoneNumber, messageId, status); } history-service-0.1+14.04.20140407/daemon/tests/mock/mockconnectiondbus.h0000644000015301777760000000333612320627220026507 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Gustavo Pichorim Boiko */ #ifndef MOCKCONNECTIONDBUS_H #define MOCKCONNECTIONDBUS_H #include #include #include "types.h" class MockConnectionAdaptor; class MockConnection; class MockConnectionDBus : public QObject, public QDBusContext { Q_OBJECT public: explicit MockConnectionDBus(MockConnection *parent); bool connectToBus(); void PlaceIncomingMessage(const QString &message, const QVariantMap &properties); void PlaceCall(const QVariantMap &properties); void HangupCall(const QString &callerId); void SetCallState(const QString &phoneNumber, const QString &state); void SendDeliveryReport(const QString &phoneNumber, const QString &messageId, const QString &status); Q_SIGNALS: // signals that will be relayed into the bus void MessageSent(const QString &mesasge, const QVariantMap &properties); void CallReceived(const QVariantMap &properties); private: MockConnectionAdaptor *mAdaptor; MockConnection *mConnection; }; #endif // MOCKCONNECTIONDBUS_H history-service-0.1+14.04.20140407/daemon/tests/mock/protocol.h0000644000015301777760000000216112320627220024454 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Tiago Salem Herrmann * * This file is part of telepathy-ofono. * * telepathy-ofono is free software; you can redistribute it and/or modify * it under the terms of the GNU LESSER General Public License as published by * the Free Software Foundation; version 3. * * telepathy-ofono 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 LESSER General Public License * along with this program. If not, see . */ #ifndef MOCKPROTOCOL_H #define MOCKPROTOCOL_H #include class Protocol : public Tp::BaseProtocol { Q_OBJECT Q_DISABLE_COPY(Protocol) public: Protocol(const QDBusConnection &dbusConnection, const QString &name); private: Tp::BaseConnectionPtr createConnection(const QVariantMap ¶meters, Tp::DBusError *error); }; #endif history-service-0.1+14.04.20140407/daemon/tests/mock/connection.cpp0000644000015301777760000003741312320627220025315 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #include #include #include #include // telepathy-mock #include "connection.h" #include "phoneutils_p.h" #include "protocol.h" #include "mockconnectiondbus.h" MockConnection::MockConnection(const QDBusConnection &dbusConnection, const QString &cmName, const QString &protocolName, const QVariantMap ¶meters) : Tp::BaseConnection(dbusConnection, cmName, protocolName, parameters), mHandleCount(0) { setSelfHandle(newHandle("")); setConnectCallback(Tp::memFun(this,&MockConnection::connect)); setInspectHandlesCallback(Tp::memFun(this,&MockConnection::inspectHandles)); setRequestHandlesCallback(Tp::memFun(this,&MockConnection::requestHandles)); setCreateChannelCallback(Tp::memFun(this,&MockConnection::createChannel)); // initialise requests interface (Connection.Interface.Requests) requestsIface = Tp::BaseConnectionRequestsInterface::create(this); // set requestable text channel properties Tp::RequestableChannelClass text; text.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_TEXT; text.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"] = Tp::HandleTypeContact; text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle"); text.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID"); // set requestable call channel properties Tp::RequestableChannelClass call; call.fixedProperties[TP_QT_IFACE_CHANNEL+".ChannelType"] = TP_QT_IFACE_CHANNEL_TYPE_CALL; call.fixedProperties[TP_QT_IFACE_CHANNEL+".TargetHandleType"] = Tp::HandleTypeContact; call.fixedProperties[TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudio"] = true; call.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetHandle"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL+".TargetID"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudio"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialVideo"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialAudioName"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialVideoName"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".InitialTransport"); call.allowedProperties.append(TP_QT_IFACE_CHANNEL_TYPE_CALL+".HardwareStreaming"); requestsIface->requestableChannelClasses << text << call; plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(requestsIface)); // init presence interface simplePresenceIface = Tp::BaseConnectionSimplePresenceInterface::create(); simplePresenceIface->setSetPresenceCallback(Tp::memFun(this,&MockConnection::setPresence)); plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(simplePresenceIface)); // Set Presence Tp::SimpleStatusSpec presenceOnline; presenceOnline.type = Tp::ConnectionPresenceTypeAvailable; presenceOnline.maySetOnSelf = true; presenceOnline.canHaveMessage = false; Tp::SimpleStatusSpec presenceOffline; presenceOffline.type = Tp::ConnectionPresenceTypeOffline; presenceOffline.maySetOnSelf = false; presenceOffline.canHaveMessage = false; Tp::SimpleStatusSpecMap statuses; statuses.insert(QLatin1String("available"), presenceOnline); statuses.insert(QLatin1String("offline"), presenceOffline); simplePresenceIface->setStatuses(statuses); mSelfPresence.type = Tp::ConnectionPresenceTypeOffline; mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeOffline; contactsIface = Tp::BaseConnectionContactsInterface::create(); contactsIface->setGetContactAttributesCallback(Tp::memFun(this,&MockConnection::getContactAttributes)); contactsIface->setContactAttributeInterfaces(QStringList() << TP_QT_IFACE_CONNECTION << TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE); plugInterface(Tp::AbstractConnectionInterfacePtr::dynamicCast(contactsIface)); mDBus = new MockConnectionDBus(this); setOnline(true); } void MockConnection::addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath) { qDebug() << "addMMSToService " << path << properties << servicePath; #if 0 FIXME: re-implement MMSDMessage *msg = new MMSDMessage(path, properties); QObject::connect(msg, SIGNAL(propertyChanged(QString,QVariant)), SLOT(onMMSPropertyChanged(QString,QVariant))); mServiceMMSList[servicePath].append(msg); if (properties["Status"] == "received") { const QString normalizedNumber = PhoneUtils::normalizePhoneNumber(properties["Sender"].toString()); // check if there is an open channel for this number and use it Q_FOREACH(const QString &phoneNumber, mTextChannels.keys()) { if (PhoneUtils::comparePhoneNumbers(normalizedNumber, phoneNumber)) { qDebug() << "existing channel" << mTextChannels[phoneNumber]; mTextChannels[phoneNumber]->mmsReceived(path, properties); return; } } Tp::DBusError error; bool yours; qDebug() << "new handle" << normalizedNumber; uint handle = newHandle(normalizedNumber); ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, &error); if(error.isValid()) { qCritical() << "Error creating channel for incoming message " << error.name() << error.message(); return; } mTextChannels[normalizedNumber]->mmsReceived(path, properties); } #endif } MockConnection::~MockConnection() { } uint MockConnection::setPresence(const QString& status, const QString& statusMessage, Tp::DBusError *error) { qDebug() << "setPresence" << status; if (status == "available") { mRequestedSelfPresence.type = Tp::ConnectionPresenceTypeAvailable; } return selfHandle(); } Tp::ContactAttributesMap MockConnection::getContactAttributes(const Tp::UIntList &handles, const QStringList &ifaces, Tp::DBusError *error) { qDebug() << "getContactAttributes" << handles << ifaces; Tp::ContactAttributesMap attributesMap; QVariantMap attributes; Q_FOREACH(uint handle, handles) { attributes[TP_QT_IFACE_CONNECTION+"/contact-id"] = inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error).at(0); if (ifaces.contains(TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) { attributes[TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE+"/presence"] = QVariant::fromValue(mSelfPresence); } attributesMap[handle] = attributes; } return attributesMap; } void MockConnection::setOnline(bool online) { qDebug() << "setOnline" << online; Tp::SimpleContactPresences presences; if (online) { mSelfPresence.status = "available"; mSelfPresence.statusMessage = ""; mSelfPresence.type = Tp::ConnectionPresenceTypeAvailable; } else { mSelfPresence.status = "offline"; mSelfPresence.statusMessage = ""; mSelfPresence.type = Tp::ConnectionPresenceTypeOffline; } presences[selfHandle()] = mSelfPresence; simplePresenceIface->setPresences(presences); } uint MockConnection::newHandle(const QString &identifier) { mHandles[++mHandleCount] = identifier; return mHandleCount; } QStringList MockConnection::inspectHandles(uint handleType, const Tp::UIntList& handles, Tp::DBusError *error) { QStringList identifiers; if( handleType != Tp::HandleTypeContact ) { error->set(TP_QT_ERROR_INVALID_ARGUMENT,"Not supported"); return QStringList(); } qDebug() << "MockConnection::inspectHandles " << handles; Q_FOREACH( uint handle, handles) { if (mHandles.keys().contains(handle)) { identifiers.append(mHandles.value(handle)); } else { error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found"); return QStringList(); } } qDebug() << "MockConnection::inspectHandles " << identifiers; return identifiers; } void MockConnection::connect(Tp::DBusError *error) { qDebug() << "MockConnection::connect"; setStatus(Tp::ConnectionStatusConnected, Tp::ConnectionStatusReasonRequested); } Tp::UIntList MockConnection::requestHandles(uint handleType, const QStringList& identifiers, Tp::DBusError* error) { qDebug() << "requestHandles"; Tp::UIntList handles; if( handleType != Tp::HandleTypeContact ) { error->set(TP_QT_ERROR_INVALID_ARGUMENT, "Not supported"); return Tp::UIntList(); } Q_FOREACH( const QString& identifier, identifiers) { if (mHandles.values().contains(identifier)) { handles.append(mHandles.key(identifier)); } else { handles.append(newHandle(identifier)); } } qDebug() << "requestHandles" << handles; return handles; } Tp::BaseChannelPtr MockConnection::createTextChannel(uint targetHandleType, uint targetHandle, Tp::DBusError *error) { Q_UNUSED(targetHandleType); Q_UNUSED(error); QString requestedId = mHandles.value(targetHandle); if (mTextChannels.contains(requestedId)) { return mTextChannels[requestedId]->baseChannel(); } MockTextChannel *channel = new MockTextChannel(this, requestedId, targetHandle); QObject::connect(channel, SIGNAL(messageRead(QString)), SLOT(onMessageRead(QString))); QObject::connect(channel, SIGNAL(destroyed()), SLOT(onTextChannelClosed())); QObject::connect(channel, SIGNAL(messageSent(QString,QVariantMap)), SIGNAL(messageSent(QString,QVariantMap))); qDebug() << channel; mTextChannels[requestedId] = channel; return channel->baseChannel(); } void MockConnection::onMessageRead(const QString &id) { // FIXME: implement } Tp::BaseChannelPtr MockConnection::createCallChannel(uint targetHandleType, uint targetHandle, Tp::DBusError *error) { Q_UNUSED(targetHandleType); bool success = true; QString requestedId = mHandles.value(targetHandle); Q_FOREACH(const QString &id, mCallChannels.keys()) { if (id == requestedId) { return mCallChannels[id]->baseChannel(); } } QString state = "dialing"; if (mInitialCallStatus.contains(requestedId)) { state = mInitialCallStatus.take(requestedId); } mCallChannels[requestedId] = new MockCallChannel(this, requestedId, state, targetHandle); QObject::connect(mCallChannels[requestedId], SIGNAL(destroyed()), SLOT(onCallChannelClosed())); qDebug() << mCallChannels[requestedId]; return mCallChannels[requestedId]->baseChannel(); } Tp::BaseChannelPtr MockConnection::createChannel(const QString& channelType, uint targetHandleType, uint targetHandle, const QVariantMap &hints, Tp::DBusError *error) { qDebug() << "MockConnection::createChannel" << targetHandle; if( (targetHandleType != Tp::HandleTypeContact) || targetHandle == 0 || !mHandles.keys().contains(targetHandle)) { error->set(TP_QT_ERROR_INVALID_HANDLE, "Handle not found"); return Tp::BaseChannelPtr(); } if (mSelfPresence.type != Tp::ConnectionPresenceTypeAvailable) { error->set(TP_QT_ERROR_NETWORK_ERROR, "No network available"); return Tp::BaseChannelPtr(); } if (channelType == TP_QT_IFACE_CHANNEL_TYPE_TEXT) { return createTextChannel(targetHandleType, targetHandle, error); } else if (channelType == TP_QT_IFACE_CHANNEL_TYPE_CALL) { return createCallChannel(targetHandleType, targetHandle, error); } else { error->set(TP_QT_ERROR_NOT_IMPLEMENTED, "Channel type not available"); } return Tp::BaseChannelPtr(); } void MockConnection::placeIncomingMessage(const QString &message, const QVariantMap &info) { const QString sender = info["Sender"].toString(); // check if there is an open channel for this sender and use it Q_FOREACH(const QString &id, mTextChannels.keys()) { if (id == sender) { mTextChannels[id]->messageReceived(message, info); return; } } Tp::DBusError error; bool yours; uint handle = newHandle(sender); ensureChannel(TP_QT_IFACE_CHANNEL_TYPE_TEXT,Tp::HandleTypeContact, handle, yours, handle, false, QVariantMap(), &error); if(error.isValid()) { qWarning() << "Error creating channel for incoming message" << error.name() << error.message(); return; } mTextChannels[sender]->messageReceived(message, info); } void MockConnection::onTextChannelClosed() { MockTextChannel *channel = static_cast(sender()); if (channel) { QString key = mTextChannels.key(channel); qDebug() << "text channel closed for number " << key; mTextChannels.remove(key); } } void MockConnection::onCallChannelClosed() { qDebug() << "onCallChannelClosed()"; MockCallChannel *channel = static_cast(sender()); if (channel) { QString key = mCallChannels.key(channel); qDebug() << "call channel closed for number " << key; mCallChannels.remove(key); } } uint MockConnection::ensureHandle(const QString &id) { if (mHandles.values().contains(id)) { return mHandles.key(id); } return newHandle(id); } void MockConnection::placeCall(const QVariantMap &properties) { qDebug() << "new call" << properties; bool yours; Tp::DBusError error; QString callerId = properties["Caller"].toString(); QString state = properties["State"].toString(); if (mCallChannels.contains(callerId)) { return; } uint handle = ensureHandle(callerId); uint initiatorHandle = 0; if (state == "incoming" || state == "waiting") { initiatorHandle = handle; } else { initiatorHandle = selfHandle(); } qDebug() << "initiatorHandle " <setCallState("disconnected"); } void MockConnection::setCallState(const QString &phoneNumber, const QString &state) { if (!mCallChannels.contains(phoneNumber)) { return; } mCallChannels[phoneNumber]->setCallState(state); } void MockConnection::sendDeliveryReport(const QString &phoneNumber, const QString &messageId, const QString &status) { if (!mTextChannels.contains(phoneNumber)) { return; } mTextChannels[phoneNumber]->placeDeliveryReport(messageId, status); } history-service-0.1+14.04.20140407/daemon/tests/mock/callchannel.h0000644000015301777760000000450112320627220025057 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann Gustavo Pichorim Boiko */ #ifndef MOCKCALLCHANNEL_H #define MOCKCALLCHANNEL_H #include #include #include #include #include #include "connection.h" class MockConnection; class MockCallChannel : public QObject { Q_OBJECT public: MockCallChannel(MockConnection *conn, QString phoneNumber, QString state, uint targetHandle, QObject *parent = 0); ~MockCallChannel(); Tp::BaseChannelPtr baseChannel(); void onHangup(uint reason, const QString &detailedReason, const QString &message, Tp::DBusError* error); void onAccept(Tp::DBusError*); void onMuteStateChanged(const Tp::LocalMuteState &state, Tp::DBusError *error); void onHoldStateChanged(const Tp::LocalHoldState &state, const Tp::LocalHoldStateReason &reason, Tp::DBusError *error); void onDTMFStartTone(uchar event, Tp::DBusError *error); void onDTMFStopTone(Tp::DBusError *error); public Q_SLOTS: void setCallState(const QString &state); void init(); void onOfonoMuteChanged(bool mute); private: QString mObjPath; QString mState; bool mIncoming; bool mRequestedHangup; Tp::BaseChannelPtr mBaseChannel; QString mPhoneNumber; MockConnection *mConnection; uint mTargetHandle; Tp::BaseChannelHoldInterfacePtr mHoldIface; Tp::BaseCallMuteInterfacePtr mMuteIface; Tp::BaseChannelCallTypePtr mCallChannel; Tp::BaseCallContentDTMFInterfacePtr mDTMFIface; Tp::BaseCallContentPtr mCallContent; }; #endif // MOCKCALLCHANNEL_H history-service-0.1+14.04.20140407/daemon/tests/mock/textchannel.h0000644000015301777760000000405312320627220025132 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #ifndef MOCKTEXTCHANNEL_H #define MOCKTEXTCHANNEL_H #include #include #include #include #include #include "connection.h" class MockConnection; class MockTextChannel : public QObject { Q_OBJECT public: MockTextChannel(MockConnection *conn, QString phoneNumber, uint targetHandle, QObject *parent = 0); QString sendMessage(const Tp::MessagePartList& message, uint flags, Tp::DBusError* error); void messageReceived(const QString & message, const QVariantMap &info); Tp::BaseChannelPtr baseChannel(); void messageAcknowledged(const QString &id); void mmsReceived(const QString &id, const QVariantMap &properties); public Q_SLOTS: void placeDeliveryReport(const QString &messageId, const QString &status); Q_SIGNALS: void messageRead(const QString &id); void messageSent(const QString &message, const QVariantMap &info); private: ~MockTextChannel(); Tp::BaseChannelPtr mBaseChannel; QString mPhoneNumber; MockConnection *mConnection; uint mTargetHandle; Tp::BaseChannelMessagesInterfacePtr mMessagesIface; Tp::BaseChannelTextTypePtr mTextChannel; uint mMessageCounter; }; #endif // MOCKTEXTCHANNEL_H history-service-0.1+14.04.20140407/daemon/tests/mock/textchannel.cpp0000644000015301777760000002047012320627220025466 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann * Gustavo Pichorim Boiko */ // telepathy-ofono #include "textchannel.h" QDBusArgument &operator<<(QDBusArgument &argument, const AttachmentStruct &attachment) { argument.beginStructure(); argument << attachment.id << attachment.contentType << attachment.filePath << attachment.offset << attachment.length; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, AttachmentStruct &attachment) { argument.beginStructure(); argument >> attachment.id >> attachment.contentType >> attachment.filePath >> attachment.offset >> attachment.length; argument.endStructure(); return argument; } MockTextChannel::MockTextChannel(MockConnection *conn, QString phoneNumber, uint targetHandle, QObject *parent): QObject(parent), mConnection(conn), mPhoneNumber(phoneNumber), mTargetHandle(targetHandle), mMessageCounter(1) { qDBusRegisterMetaType(); qDBusRegisterMetaType(); Tp::BaseChannelPtr baseChannel = Tp::BaseChannel::create(mConnection, TP_QT_IFACE_CHANNEL_TYPE_TEXT, targetHandle, Tp::HandleTypeContact); Tp::BaseChannelTextTypePtr textType = Tp::BaseChannelTextType::create(baseChannel.data()); baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(textType)); QStringList supportedContentTypes = QStringList() << "text/plain"; Tp::UIntList messageTypes = Tp::UIntList() << Tp::ChannelTextMessageTypeNormal << Tp::ChannelTextMessageTypeDeliveryReport; uint messagePartSupportFlags = 0; uint deliveryReportingSupport = Tp::DeliveryReportingSupportFlagReceiveSuccesses; mMessagesIface = Tp::BaseChannelMessagesInterface::create(textType.data(), supportedContentTypes, messageTypes, messagePartSupportFlags, deliveryReportingSupport); mMessagesIface->setSendMessageCallback(Tp::memFun(this,&MockTextChannel::sendMessage)); baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMessagesIface)); mBaseChannel = baseChannel; mTextChannel = Tp::BaseChannelTextTypePtr::dynamicCast(mBaseChannel->interface(TP_QT_IFACE_CHANNEL_TYPE_TEXT)); mTextChannel->setMessageAcknowledgedCallback(Tp::memFun(this,&MockTextChannel::messageAcknowledged)); QObject::connect(mBaseChannel.data(), SIGNAL(closed()), this, SLOT(deleteLater())); } MockTextChannel::~MockTextChannel() { } Tp::BaseChannelPtr MockTextChannel::baseChannel() { return mBaseChannel; } void MockTextChannel::messageAcknowledged(const QString &id) { Q_EMIT messageRead(id); } QString MockTextChannel::sendMessage(const Tp::MessagePartList& message, uint flags, Tp::DBusError* error) { Tp::MessagePart header = message.at(0); Tp::MessagePart body = message.at(1); static int serial = 0; // FIXME: check what other data we need to emit in the signal QString id = QString("sentmessage%1").arg(serial++); QString messageText = body["content"].variant().toString(); QVariantMap properties; properties["SentTime"] = QDateTime::currentDateTime().toString(Qt::ISODate); properties["Recipients"] = QStringList() << mPhoneNumber; properties["Id"] = id; Q_EMIT messageSent(messageText, properties); return id; } void MockTextChannel::placeDeliveryReport(const QString &messageId, const QString &status) { Tp::DeliveryStatus delivery_status; if (status == "delivered") { delivery_status = Tp::DeliveryStatusDelivered; } else if (status == "temporarily_failed") { delivery_status = Tp::DeliveryStatusTemporarilyFailed; } else if (status == "permanently_failed") { delivery_status = Tp::DeliveryStatusPermanentlyFailed; } else if (status == "accepted") { delivery_status = Tp::DeliveryStatusAccepted; } else if (status == "read") { delivery_status = Tp::DeliveryStatusRead; } else if (status == "deleted") { delivery_status = Tp::DeliveryStatusDeleted; } else { delivery_status = Tp::DeliveryStatusUnknown; } Tp::MessagePartList partList; Tp::MessagePart header; header["message-sender"] = QDBusVariant(mTargetHandle); header["message-sender-id"] = QDBusVariant(mPhoneNumber); header["message-type"] = QDBusVariant(Tp::ChannelTextMessageTypeDeliveryReport); header["delivery-status"] = QDBusVariant(delivery_status); header["delivery-token"] = QDBusVariant(messageId); partList << header; mTextChannel->addReceivedMessage(partList); } void MockTextChannel::messageReceived(const QString &message, const QVariantMap &info) { Tp::MessagePartList partList; Tp::MessagePart body; body["content-type"] = QDBusVariant("text/plain"); body["content"] = QDBusVariant(message); Tp::MessagePart header; header["message-token"] = QDBusVariant(info["SentTime"].toString() +"-" + QString::number(mMessageCounter++)); header["message-received"] = QDBusVariant(QDateTime::fromString(info["SentTime"].toString(), Qt::ISODate).toTime_t()); header["message-sender"] = QDBusVariant(mTargetHandle); header["message-sender-id"] = QDBusVariant(mPhoneNumber); header["message-type"] = QDBusVariant(Tp::ChannelTextMessageTypeNormal); partList << header << body; mTextChannel->addReceivedMessage(partList); } void MockTextChannel::mmsReceived(const QString &id, const QVariantMap &properties) { Tp::MessagePartList message; QString subject = properties["Subject"].toString(); QString smil = properties["Smil"].toString(); Tp::MessagePart header; header["message-token"] = QDBusVariant(id); header["message-sender"] = QDBusVariant(mTargetHandle); header["message-received"] = QDBusVariant(QDateTime::fromString(properties["Date"].toString(), Qt::ISODate).toTime_t()); header["message-type"] = QDBusVariant(Tp::DeliveryStatusDelivered); if (!subject.isEmpty()) { header["subject"] = QDBusVariant(subject); } message << header; AttachmentList mmsdAttachments = qdbus_cast(properties["Attachments"]); Q_FOREACH(const AttachmentStruct &attachment, mmsdAttachments) { QFile attachmentFile(attachment.filePath); if (!attachmentFile.open(QIODevice::ReadOnly)) { qWarning() << "fail to load attachment" << attachmentFile.errorString() << attachment.filePath; continue; } // FIXME check if we managed to read the total attachment file attachmentFile.seek(attachment.offset); QByteArray fileData = attachmentFile.read(attachment.length); Tp::MessagePart part; part["content-type"] = QDBusVariant(attachment.contentType); part["identifier"] = QDBusVariant(attachment.id); part["content"] = QDBusVariant(fileData); part["size"] = QDBusVariant(attachment.length); message << part; } if (!smil.isEmpty()) { Tp::MessagePart part; part["content-type"] = QDBusVariant(QString("application/smil")); part["identifier"] = QDBusVariant(QString("smil")); part["content"] = QDBusVariant(smil); part["size"] = QDBusVariant(smil.size()); message << part; } mTextChannel->addReceivedMessage(message); } history-service-0.1+14.04.20140407/daemon/tests/mock/main.cpp0000644000015301777760000000255012320627220024074 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann */ #include #include #include #include "protocol.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Tp::registerTypes(); Tp::enableDebug(true); Tp::enableWarnings(true); Tp::BaseProtocolPtr proto = Tp::BaseProtocol::create( QDBusConnection::sessionBus(), QLatin1String("mock")); Tp::BaseConnectionManagerPtr cm = Tp::BaseConnectionManager::create( QDBusConnection::sessionBus(), QLatin1String("mock")); cm->addProtocol(proto); cm->registerObject(); return a.exec(); } history-service-0.1+14.04.20140407/daemon/tests/mock/CMakeLists.txt0000644000015301777760000000123412320627220025202 0ustar pbusernogroup00000000000000include_directories( ${TP_QT5_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) find_library(TELEPATHY_QT5_SERVICE_LIBRARIES telepathy-qt5-service) set(mock_SRCS main.cpp protocol.cpp connection.cpp textchannel.cpp callchannel.cpp mockconnectiondbus.cpp) qt5_add_dbus_adaptor(mock_SRCS MockConnection.xml mockconnectiondbus.h MockConnectionDBus) add_executable(telepathy-mock ${mock_SRCS}) qt5_use_modules(telepathy-mock Core DBus) target_link_libraries(telepathy-mock ${TP_QT5_LIBRARIES} ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES}) history-service-0.1+14.04.20140407/daemon/tests/mock/protocol.cpp0000644000015301777760000000306712320627220025015 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann */ #include "protocol.h" #include "connection.h" #include #include Protocol::Protocol(const QDBusConnection &dbusConnection, const QString &name) : Tp::BaseProtocol(dbusConnection, name) { setRequestableChannelClasses(Tp::RequestableChannelClassSpecList() << Tp::RequestableChannelClassSpec::textChat() << Tp::RequestableChannelClassSpec::audioCall()); setCreateConnectionCallback(memFun(this, &Protocol::createConnection)); } Tp::BaseConnectionPtr Protocol::createConnection(const QVariantMap ¶meters, Tp::DBusError *error) { Q_UNUSED(error); return Tp::BaseConnection::create(QDBusConnection::sessionBus(), "mock", name().toLatin1(), parameters); } history-service-0.1+14.04.20140407/daemon/tests/mock/MockConnection.xml0000644000015301777760000000520012320627220026072 0ustar pbusernogroup00000000000000 An interface to the fake connection manager history-service-0.1+14.04.20140407/daemon/tests/mock/connection.h0000644000015301777760000000744612320627220024765 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #ifndef MOCKCONNECTION_H #define MOCKCONNECTION_H // qt #include // telepathy-qt #include #include #include #include #include // mock-cm #include "textchannel.h" #include "callchannel.h" #include "dbustypes.h" class MockTextChannel; class MockCallChannel; class MockConnectionDBus; class MockConnection : public Tp::BaseConnection { Q_OBJECT Q_DISABLE_COPY(MockConnection) public: MockConnection(const QDBusConnection &dbusConnection, const QString &cmName, const QString &protocolName, const QVariantMap ¶meters); QStringList inspectHandles(uint handleType, const Tp::UIntList& handles, Tp::DBusError *error); Tp::UIntList requestHandles(uint handleType, const QStringList& identifiers, Tp::DBusError* error); Tp::BaseChannelPtr createChannel(const QString& channelType, uint targetHandleType, uint targetHandle, const QVariantMap &hints, Tp::DBusError *error); Tp::ContactAttributesMap getContactAttributes(const Tp::UIntList &handles, const QStringList &ifaces, Tp::DBusError *error); uint setPresence(const QString& status, const QString& statusMessage, Tp::DBusError *error); void connect(Tp::DBusError *error); void setOnline(bool online); Tp::BaseConnectionRequestsInterfacePtr requestsIface; Tp::BaseConnectionSimplePresenceInterfacePtr simplePresenceIface; Tp::BaseConnectionContactsInterfacePtr contactsIface; uint newHandle(const QString &identifier); uint ensureHandle(const QString &id); Tp::BaseChannelPtr createTextChannel(uint targetHandleType, uint targetHandle, Tp::DBusError *error); Tp::BaseChannelPtr createCallChannel(uint targetHandleType, uint targetHandle, Tp::DBusError *error); ~MockConnection(); Q_SIGNALS: void messageSent(const QString &message, const QVariantMap &info); public Q_SLOTS: void placeIncomingMessage(const QString &message, const QVariantMap &info); void placeCall(const QVariantMap &properties); void hangupCall(const QString &callerId); void setCallState(const QString &phoneNumber, const QString &state); void sendDeliveryReport(const QString &phoneNumber, const QString &messageId, const QString &status); void onTextChannelClosed(); void onCallChannelClosed(); void onMessageRead(const QString &id); private: void addMMSToService(const QString &path, const QVariantMap &properties, const QString &servicePath); QMap mHandles; QMap mTextChannels; QMap mCallChannels; QMap mInitialCallStatus; QStringList mModems; uint mHandleCount; Tp::SimplePresence mSelfPresence; Tp::SimplePresence mRequestedSelfPresence; MockConnectionDBus *mDBus; }; #endif history-service-0.1+14.04.20140407/daemon/tests/telepathyhelper.cpp0000644000015301777760000002030212320627220025411 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #include "telepathyhelper.h" #include #include #include #include #include TelepathyHelper::TelepathyHelper(QObject *parent) : QObject(parent), //mChannelObserver(0), mFirstTime(true), mConnected(false), mHandlerInterface(0) { Tp::registerTypes(); mAccountFeatures << Tp::Account::FeatureCore; mContactFeatures << Tp::Contact::FeatureAlias << Tp::Contact::FeatureAvatarData << Tp::Contact::FeatureAvatarToken << Tp::Contact::FeatureCapabilities << Tp::Contact::FeatureSimplePresence; mConnectionFeatures << Tp::Connection::FeatureCore << Tp::Connection::FeatureSelfContact << Tp::Connection::FeatureSimplePresence; Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus()); channelFactory->addCommonFeatures(Tp::Channel::FeatureCore); mAccountManager = Tp::AccountManager::create( Tp::AccountFactory::create(QDBusConnection::sessionBus(), mAccountFeatures), Tp::ConnectionFactory::create(QDBusConnection::sessionBus(), mConnectionFeatures), channelFactory, Tp::ContactFactory::create(mContactFeatures)); connect(mAccountManager->becomeReady(Tp::AccountManager::FeatureCore), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountManagerReady(Tp::PendingOperation*))); mClientRegistrar = Tp::ClientRegistrar::create(mAccountManager); } TelepathyHelper::~TelepathyHelper() { } TelepathyHelper *TelepathyHelper::instance() { static TelepathyHelper* helper = new TelepathyHelper(); return helper; } QString TelepathyHelper::accountId() const { if (mAccount) { return mAccount->uniqueIdentifier(); } return QString(); } Tp::AccountPtr TelepathyHelper::account() const { return mAccount; } /* ChannelObserver *TelepathyHelper::channelObserver() const { return mChannelObserver; } */ bool TelepathyHelper::connected() const { return mConnected; } /* void TelepathyHelper::registerChannelObserver(const QString &observerName) { QString name = observerName; if (name.isEmpty()) { name = "TelephonyPluginObserver"; } if (mChannelObserver) { mChannelObserver->deleteLater(); } mChannelObserver = new ChannelObserver(this); registerClient(mChannelObserver, name); // messages connect(mChannelObserver, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)), ChatManager::instance(), SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); // calls connect(mChannelObserver, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), CallManager::instance(), SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); Q_EMIT channelObserverCreated(mChannelObserver); } void TelepathyHelper::unregisterChannelObserver() { Tp::AbstractClientPtr clientPtr(mChannelObserver); if (clientPtr) { mClientRegistrar->unregisterClient(clientPtr); } mChannelObserver->deleteLater(); mChannelObserver = NULL; Q_EMIT channelObserverUnregistered(); } */ QStringList TelepathyHelper::supportedProtocols() const { QStringList protocols; protocols << "mock"; return protocols; } void TelepathyHelper::initializeAccount() { // watch for account state and connection changes connect(mAccount.data(), SIGNAL(stateChanged(bool)), SLOT(onAccountStateChanged(bool))); connect(mAccount.data(), SIGNAL(connectionChanged(const Tp::ConnectionPtr&)), SLOT(onAccountConnectionChanged(const Tp::ConnectionPtr&))); // and make sure it is enabled and connected if (!mAccount->isEnabled()) { ensureAccountEnabled(); } else { ensureAccountConnected(); } } void TelepathyHelper::ensureAccountEnabled() { mAccount->setConnectsAutomatically(true); connect(mAccount->setEnabled(true), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountEnabled(Tp::PendingOperation*))); } void TelepathyHelper::ensureAccountConnected() { // if the account is not connected, request it to connect if (!mAccount->connection() || mAccount->connectionStatus() != Tp::ConnectionStatusConnected) { Tp::Presence presence(Tp::ConnectionPresenceTypeAvailable, "available", "online"); mAccount->setRequestedPresence(presence); } else { watchSelfContactPresence(); } if (mFirstTime) { Q_EMIT accountReady(); mFirstTime = false; } } void TelepathyHelper::watchSelfContactPresence() { if (mAccount.isNull() || mAccount->connection().isNull()) { return; } connect(mAccount->connection()->selfContact().data(), SIGNAL(presenceChanged(Tp::Presence)), SLOT(onPresenceChanged(Tp::Presence))); onPresenceChanged(mAccount->connection()->selfContact()->presence()); } void TelepathyHelper::registerClient(Tp::AbstractClient *client, QString name) { Tp::AbstractClientPtr clientPtr(client); bool succeeded = mClientRegistrar->registerClient(clientPtr, name); if (!succeeded) { name.append("%1"); int count = 0; // limit the number of registered clients to 20, that should be a safe margin while (!succeeded && count < 20) { succeeded = mClientRegistrar->registerClient(clientPtr, name.arg(++count)); if (succeeded) { name = name.arg(count); } } } if (succeeded) { QObject *object = dynamic_cast(client); if (object) { object->setProperty("clientName", TP_QT_IFACE_CLIENT + "." + name ); } } } void TelepathyHelper::onAccountManagerReady(Tp::PendingOperation *op) { Q_UNUSED(op) Tp::AccountSetPtr accountSet; // try to find an account of the one of supported protocols Q_FOREACH(const QString &protocol, supportedProtocols()) { accountSet = mAccountManager->accountsByProtocol(protocol); if (accountSet->accounts().count() > 0) { break; } } if (accountSet->accounts().count() == 0) { qCritical() << "No compatible telepathy account found!"; return; } mAccount = accountSet->accounts()[0]; // in case we have more than one account, the first one to show on the list is going to be used if (accountSet->accounts().count() > 1) { qWarning() << "There are more than just one account of type" << mAccount->protocolName(); } Q_EMIT accountIdChanged(); initializeAccount(); } void TelepathyHelper::onAccountEnabled(Tp::PendingOperation *op) { // we might need to do more stuff once the account is enabled, but making sure it is connected is a good start ensureAccountConnected(); } void TelepathyHelper::onAccountStateChanged(bool enabled) { if (!enabled) { ensureAccountEnabled(); } } void TelepathyHelper::onAccountConnectionChanged(const Tp::ConnectionPtr &connection) { if (connection.isNull()) { ensureAccountConnected(); } else { watchSelfContactPresence(); } Q_EMIT connectionChanged(); } void TelepathyHelper::onPresenceChanged(const Tp::Presence &presence) { mConnected = (presence.type() == Tp::ConnectionPresenceTypeAvailable); Q_EMIT connectedChanged(); } history-service-0.1+14.04.20140407/daemon/tests/mockcontroller.h0000644000015301777760000000307612320627220024725 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #ifndef MOCKCONTROLLER_H #define MOCKCONTROLLER_H #include #include class MockController : public QObject { Q_OBJECT public: static MockController *instance(); Q_SIGNALS: void MessageSent(const QString &message, const QVariantMap &properties); public Q_SLOTS: void placeIncomingMessage(const QString &message, const QVariantMap &properties); void placeCall(const QVariantMap &properties); void hangupCall(const QString &callerId); void setCallState(const QString &phoneNumber, const QString &state); void sendDeliveryReport(const QString &phoneNumber, const QString &messageId, const QString &status); private: explicit MockController(QObject *parent = 0); QDBusInterface mMockInterface; }; #endif // MOCKCONTROLLER_H history-service-0.1+14.04.20140407/daemon/tests/telepathyhelper.h0000644000015301777760000000602712320627220025066 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #ifndef TELEPATHYHELPER_H #define TELEPATHYHELPER_H #include #include #include #include #include #include //#include "channelobserver.h" #define CANONICAL_TELEPHONY_VOICEMAIL_IFACE "com.canonical.Telephony.Voicemail" #define CANONICAL_TELEPHONY_SPEAKER_IFACE "com.canonical.Telephony.Speaker" class TelepathyHelper : public QObject { Q_OBJECT Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) Q_PROPERTY(QString accountId READ accountId NOTIFY accountIdChanged) public: ~TelepathyHelper(); static TelepathyHelper *instance(); Tp::AccountPtr account() const; //ChannelObserver *channelObserver() const; bool connected() const; QString accountId() const; void registerClient(Tp::AbstractClient *client, QString name); Q_SIGNALS: //void channelObserverCreated(ChannelObserver *observer); //void channelObserverUnregistered(); void accountReady(); void connectionChanged(); void connectedChanged(); void accountIdChanged(); public Q_SLOTS: //Q_INVOKABLE void registerChannelObserver(const QString &observerName = QString::null); //Q_INVOKABLE void unregisterChannelObserver(); protected: QStringList supportedProtocols() const; void initializeAccount(); void ensureAccountEnabled(); void ensureAccountConnected(); void watchSelfContactPresence(); private Q_SLOTS: void onAccountManagerReady(Tp::PendingOperation *op); void onAccountEnabled(Tp::PendingOperation *op); void onAccountStateChanged(bool enabled); void onAccountConnectionChanged(const Tp::ConnectionPtr &connection); void onPresenceChanged(const Tp::Presence &presence); private: explicit TelepathyHelper(QObject *parent = 0); Tp::AccountManagerPtr mAccountManager; Tp::Features mAccountManagerFeatures; Tp::Features mAccountFeatures; Tp::Features mContactFeatures; Tp::Features mConnectionFeatures; Tp::ClientRegistrarPtr mClientRegistrar; Tp::AccountPtr mAccount; //ChannelObserver *mChannelObserver; bool mFirstTime; bool mConnected; QDBusInterface *mHandlerInterface; }; #endif // TELEPATHYHELPER_H history-service-0.1+14.04.20140407/daemon/tests/DaemonTest.cpp0000644000015301777760000003673412320627220024275 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann */ #include #include #include #include #include #include #include #include #include #include #include "telepathyhelper.h" #include "mockcontroller.h" #include "handler.h" #include "approver.h" #include "manager.h" #include "thread.h" #include "textevent.h" #include "voiceevent.h" Q_DECLARE_METATYPE(Tp::CallChannelPtr) Q_DECLARE_METATYPE(Tp::TextChannelPtr) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(History::Threads) Q_DECLARE_METATYPE(History::Events) Q_DECLARE_METATYPE(History::MessageStatus) class DaemonTest : public QObject { Q_OBJECT Q_SIGNALS: void contactsReceived(QList contacts); private Q_SLOTS: void initTestCase(); void testMessageReceived(); void testMessageSent(); void testMissedCall(); void testOutgoingCall(); void testDeliveryReport_data(); void testDeliveryReport(); // helper slots void onPendingContactsFinished(Tp::PendingOperation*); private: Approver *mApprover; Handler *mHandler; }; void DaemonTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType >(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); TelepathyHelper::instance(); QSignalSpy spy(TelepathyHelper::instance(), SIGNAL(accountReady())); QTRY_COMPARE(spy.count(), 1); QTRY_VERIFY(TelepathyHelper::instance()->connected()); mHandler = new Handler(this); TelepathyHelper::instance()->registerClient(mHandler, "HistoryTestHandler"); QTRY_VERIFY(mHandler->isRegistered()); // register the approver mApprover = new Approver(this); TelepathyHelper::instance()->registerClient(mApprover, "HistoryTestApprover"); // Tp-qt does not set registered status to approvers QTRY_VERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered(TELEPHONY_SERVICE_APPROVER)); // we need to wait in order to give telepathy time to notify about the approver and handler QTest::qWait(3000); } void DaemonTest::testMessageReceived() { QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads))); QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads))); QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events))); QVariantMap properties; QString sender = "123456789"; QString message = "Test message"; QDateTime sentTime = QDateTime::currentDateTime(); properties["Sender"] = sender; properties["SentTime"] = sentTime.toString(Qt::ISODate); QSignalSpy handlerSpy(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr))); MockController::instance()->placeIncomingMessage(message, properties); QTRY_COMPARE(threadsAddedSpy.count(), 1); History::Threads threads = threadsAddedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread thread = threads.first(); QCOMPARE(thread.participants().count(), 1); QCOMPARE(thread.participants().first(), sender); QTRY_COMPARE(threadsModifiedSpy.count(), 1); threads = threadsModifiedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread modifiedThread = threads.first(); QVERIFY(modifiedThread == thread); QTRY_COMPARE(eventsAddedSpy.count(), 1); History::Events events = eventsAddedSpy.first().first().value(); QCOMPARE(events.count(), 1); History::TextEvent event = events.first(); QCOMPARE(event.senderId(), sender); QCOMPARE(event.threadId(), modifiedThread.threadId()); QVERIFY(modifiedThread.lastEvent() == event); QCOMPARE(event.timestamp().toString(Qt::ISODate), sentTime.toString(Qt::ISODate)); QCOMPARE(event.message(), message); QTRY_COMPARE(handlerSpy.count(), 1); Tp::TextChannelPtr channel = handlerSpy.first().first().value(); QVERIFY(channel); channel->requestClose(); } void DaemonTest::testMessageSent() { // Request the contact to start chatting to Tp::AccountPtr account = TelepathyHelper::instance()->account(); QSignalSpy spy(this, SIGNAL(contactsReceived(QList))); QString recipient = "987654321"; connect(account->connection()->contactManager()->contactsForIdentifiers(QStringList() << recipient), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onPendingContactsFinished(Tp::PendingOperation*))); QTRY_COMPARE(spy.count(), 1); QList contacts = spy.first().first().value >(); QCOMPARE(contacts.count(), 1); QCOMPARE(contacts.first()->id(), recipient); QSignalSpy spyTextChannel(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr))); Q_FOREACH(Tp::ContactPtr contact, contacts) { account->ensureTextChat(contact, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler"); } QTRY_COMPARE(spyTextChannel.count(), 1); Tp::TextChannelPtr channel = spyTextChannel.first().first().value(); QVERIFY(channel); QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads))); QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads))); QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events))); QString messageText = "Hello, big world!"; Tp::PendingSendMessage *message = channel->send(messageText); QTRY_COMPARE(threadsAddedSpy.count(), 1); History::Threads threads = threadsAddedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread thread = threads.first(); QCOMPARE(thread.participants().count(), 1); QCOMPARE(thread.participants().first(), recipient); QTRY_COMPARE(threadsModifiedSpy.count(), 1); threads = threadsModifiedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread modifiedThread = threads.first(); QVERIFY(modifiedThread == thread); QTRY_COMPARE(eventsAddedSpy.count(), 1); History::Events events = eventsAddedSpy.first().first().value(); QCOMPARE(events.count(), 1); History::TextEvent event = events.first(); QCOMPARE(event.senderId(), QString("self")); QCOMPARE(event.threadId(), modifiedThread.threadId()); QVERIFY(modifiedThread.lastEvent() == event); QCOMPARE(event.message(), messageText); channel->requestClose(); } void DaemonTest::testMissedCall() { QSignalSpy newCallSpy(mApprover, SIGNAL(newCall())); // create an incoming call QString callerId = "33333333"; QVariantMap properties; properties["Caller"] = callerId; properties["State"] = "incoming"; MockController::instance()->placeCall(properties); QTRY_COMPARE(newCallSpy.count(), 1); QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads))); QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads))); QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events))); // now hangup the call and check that the event was added to the database MockController::instance()->hangupCall(callerId); QTRY_COMPARE(threadsAddedSpy.count(), 1); History::Threads threads = threadsAddedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread thread = threads.first(); QCOMPARE(thread.participants().count(), 1); QCOMPARE(thread.participants().first(), callerId); QTRY_COMPARE(threadsModifiedSpy.count(), 1); threads = threadsModifiedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread modifiedThread = threads.first(); QVERIFY(modifiedThread == thread); QTRY_COMPARE(eventsAddedSpy.count(), 1); History::Events events = eventsAddedSpy.first().first().value(); QCOMPARE(events.count(), 1); History::VoiceEvent event = events.first(); QCOMPARE(event.senderId(), callerId); QCOMPARE(event.threadId(), modifiedThread.threadId()); QVERIFY(modifiedThread.lastEvent() == event); QCOMPARE(event.missed(), true); } void DaemonTest::testOutgoingCall() { // Request the contact to start chatting to Tp::AccountPtr account = TelepathyHelper::instance()->account(); QString phoneNumber = "44444444"; QSignalSpy spy(this, SIGNAL(contactsReceived(QList))); connect(account->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onPendingContactsFinished(Tp::PendingOperation*))); QTRY_COMPARE(spy.count(), 1); QList contacts = spy.first().first().value >(); QCOMPARE(contacts.count(), 1); QCOMPARE(contacts.first()->id(), phoneNumber); QSignalSpy spyCallChannel(mHandler, SIGNAL(callChannelAvailable(Tp::CallChannelPtr))); Q_FOREACH(Tp::ContactPtr contact, contacts) { account->ensureAudioCall(contact, "audio", QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler"); } QTRY_COMPARE(spyCallChannel.count(), 1); Tp::CallChannelPtr channel = spyCallChannel.first().first().value(); QVERIFY(channel); MockController::instance()->setCallState(phoneNumber, "alerting"); QTRY_COMPARE(channel->callState(), Tp::CallStateInitialised); MockController::instance()->setCallState(phoneNumber, "active"); QTRY_COMPARE(channel->callState(), Tp::CallStateActive); QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads))); QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads))); QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events))); MockController::instance()->setCallState(phoneNumber, "disconnected"); QTRY_COMPARE(channel->callState(), Tp::CallStateEnded); QTRY_COMPARE(threadsAddedSpy.count(), 1); History::Threads threads = threadsAddedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread thread = threads.first(); QCOMPARE(thread.participants().count(), 1); QCOMPARE(thread.participants().first(), phoneNumber); QTRY_COMPARE(threadsModifiedSpy.count(), 1); threads = threadsModifiedSpy.first().first().value(); QCOMPARE(threads.count(), 1); History::Thread modifiedThread = threads.first(); QVERIFY(modifiedThread == thread); QTRY_COMPARE(eventsAddedSpy.count(), 1); History::Events events = eventsAddedSpy.first().first().value(); QCOMPARE(events.count(), 1); History::VoiceEvent event = events.first(); QCOMPARE(event.senderId(), QString("self")); QCOMPARE(event.threadId(), modifiedThread.threadId()); QVERIFY(modifiedThread.lastEvent() == event); QCOMPARE(event.missed(), false); QVERIFY(event.duration().isValid()); } void DaemonTest::testDeliveryReport_data() { QTest::addColumn("phoneNumber"); QTest::addColumn("deliveryStatus"); QTest::addColumn("messageStatus"); QTest::newRow("delivered status") << "11112222" << "delivered" << History::MessageStatusDelivered; QTest::newRow("temporarily failed") << "11113333" << "temporarily_failed" << History::MessageStatusTemporarilyFailed; QTest::newRow("permanently failed") << "11114444" << "permanently_failed" << History::MessageStatusPermanentlyFailed; QTest::newRow("accepted status") << "11115555" << "accepted" << History::MessageStatusAccepted; QTest::newRow("read status") << "11116666" << "read" << History::MessageStatusRead; QTest::newRow("deleted") << "11117777" << "deleted" << History::MessageStatusDeleted; QTest::newRow("unknown") << "11118888" << "unknown" << History::MessageStatusUnknown; } void DaemonTest::testDeliveryReport() { QFETCH(QString, phoneNumber); QFETCH(QString, deliveryStatus); QFETCH(History::MessageStatus, messageStatus); // Request the contact to start chatting to Tp::AccountPtr account = TelepathyHelper::instance()->account(); QSignalSpy spy(this, SIGNAL(contactsReceived(QList))); connect(account->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onPendingContactsFinished(Tp::PendingOperation*))); QTRY_COMPARE(spy.count(), 1); QList contacts = spy.first().first().value >(); QCOMPARE(contacts.count(), 1); QCOMPARE(contacts.first()->id(), phoneNumber); QSignalSpy spyTextChannel(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr))); Q_FOREACH(Tp::ContactPtr contact, contacts) { account->ensureTextChat(contact, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler"); } QTRY_COMPARE(spyTextChannel.count(), 1); Tp::TextChannelPtr channel = spyTextChannel.first().first().value(); QVERIFY(channel); QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events))); QString messageText = "Hello, big world!"; Tp::PendingSendMessage *message = channel->send(messageText); QTRY_COMPARE(eventsAddedSpy.count(), 1); History::Events events = eventsAddedSpy.first().first().value(); QCOMPARE(events.count(), 1); History::TextEvent event = events.first(); // now send a delivery report for this text and make sure the event gets updated QSignalSpy eventsModifiedSpy(History::Manager::instance(), SIGNAL(eventsModified(History::Events))); MockController::instance()->sendDeliveryReport(phoneNumber, event.eventId(), deliveryStatus); QTRY_COMPARE(eventsModifiedSpy.count(), 1); events = eventsModifiedSpy.first().first().value(); QCOMPARE(events.count(), 1); event = events.first(); QCOMPARE(event.messageStatus(), messageStatus); channel->requestClose(); } void DaemonTest::onPendingContactsFinished(Tp::PendingOperation *op) { Tp::PendingContacts *pc = qobject_cast(op); if (!pc) { return; } Q_EMIT contactsReceived(pc->contacts()); } QTEST_MAIN(DaemonTest) #include "DaemonTest.moc" history-service-0.1+14.04.20140407/daemon/tests/handler.cpp0000644000015301777760000001163612320627220023641 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #include "handler.h" #include "telepathyhelper.h" #include #include #include #include #include Handler::Handler(QObject *parent) : QObject(parent), Tp::AbstractClientHandler(channelFilters()), mBypassApproval(false) { } void Handler::setBypassApproval(bool bypass) { mBypassApproval = bypass; } bool Handler::bypassApproval() const { return mBypassApproval; } void Handler::handleChannels(const Tp::MethodInvocationContextPtr<> &context, const Tp::AccountPtr &account, const Tp::ConnectionPtr &connection, const QList &channels, const QList &requestsSatisfied, const QDateTime &userActionTime, const Tp::AbstractClientHandler::HandlerInfo &handlerInfo) { Q_UNUSED(account) Q_UNUSED(connection) Q_UNUSED(requestsSatisfied) Q_UNUSED(userActionTime) Q_UNUSED(handlerInfo) Q_FOREACH(const Tp::ChannelPtr channel, channels) { Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); if (textChannel) { Tp::PendingReady *pr = textChannel->becomeReady(Tp::Features() << Tp::TextChannel::FeatureCore << Tp::TextChannel::FeatureChatState << Tp::TextChannel::FeatureMessageCapabilities << Tp::TextChannel::FeatureMessageQueue << Tp::TextChannel::FeatureMessageSentSignal); connect(pr, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onTextChannelReady(Tp::PendingOperation*))); mReadyRequests[pr] = textChannel; continue; } Tp::CallChannelPtr callChannel = Tp::CallChannelPtr::dynamicCast(channel); if (callChannel) { Tp::PendingReady *pr = callChannel->becomeReady(Tp::Features() << Tp::CallChannel::FeatureCore << Tp::CallChannel::FeatureCallState << Tp::CallChannel::FeatureContents << Tp::CallChannel::FeatureLocalHoldState); connect(pr, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onCallChannelReady(Tp::PendingOperation*))); mReadyRequests[pr] = callChannel; continue; } } context->setFinished(); } Tp::ChannelClassSpecList Handler::channelFilters() { Tp::ChannelClassSpecList specList; specList << Tp::ChannelClassSpec::audioCall(); specList << Tp::ChannelClassSpec::textChat(); return specList; } void Handler::onTextChannelReady(Tp::PendingOperation *op) { Tp::PendingReady *pr = qobject_cast(op); if (!pr) { qCritical() << "The pending object is not a Tp::PendingReady"; return; } Tp::ChannelPtr channel = mReadyRequests[pr]; Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); if(!textChannel) { qCritical() << "The saved channel is not a Tp::TextChannel"; return; } mReadyRequests.remove(pr); Q_EMIT textChannelAvailable(textChannel); } void Handler::onCallChannelReady(Tp::PendingOperation *op) { Tp::PendingReady *pr = qobject_cast(op); if (!pr) { qCritical() << "The pending object is not a Tp::PendingReady"; return; } Tp::ChannelPtr channel = mReadyRequests[pr]; Tp::CallChannelPtr callChannel = Tp::CallChannelPtr::dynamicCast(channel); if(!callChannel) { qCritical() << "The saved channel is not a Tp::CallChannel"; return; } mReadyRequests.remove(pr); Q_EMIT callChannelAvailable(callChannel); } history-service-0.1+14.04.20140407/daemon/tests/CMakeLists.txt0000644000015301777760000000432012320627220024250 0ustar pbusernogroup00000000000000include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR} ) macro(generate_test TESTNAME USE_DBUS) add_executable(${TESTNAME} ${ARGN} ${TESTNAME}.cpp) qt5_use_modules(${TESTNAME} Core DBus Test) set(TEST_COMMAND ) if (${USE_DBUS}) set(TEST_COMMAND -p ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -p -xunitxml -p -o -p ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml) add_test(${TESTNAME} ${DBUS_RUNNER} --keep-env --task ${CMAKE_CURRENT_BINARY_DIR}/mock/telepathy-mock --ignore-return --task ${CMAKE_BINARY_DIR}/daemon/history-daemon --ignore-return --task ${CMAKE_CURRENT_BINARY_DIR}/dbus-test-wrapper.sh ${TEST_COMMAND}) else (${USE_DBUS}) add_test(${TESTNAME} ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -xunitxml -o ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml) endif(${USE_DBUS}) # force telepathy not to use system approvers when available, # also force usage of memory backend in history-service set(TMPDIR "/tmp/history_test_home") set(TEST_ENVIRONMENT "HOME=${TMPDIR};" "HISTORY_SQLITE_DBPATH=:memory:;" "HISTORY_PLUGIN_PATH=${CMAKE_BINARY_DIR}/plugins/sqlite;" "XDG_CONFIG_HOME=${TMPDIR}; XDG_DATA_HOME=${TMPDIR}; XDG_CACHE_DIR=${TMPDIR}; XDG_CACHE_HOME=${TMPDIR}; XDG_DATA_DIRS=${TMPDIR}; MC_ACCOUNT_DIR=${TMPDIR}; MC_MANAGER_DIR=${TMPDIR}") set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT}" TIMEOUT 30) target_link_libraries(${TESTNAME} ${TP_QT5_LIBRARIES} historyservice ) endmacro(generate_test) configure_file(dbus-test-wrapper.sh.in ${CMAKE_CURRENT_BINARY_DIR}/dbus-test-wrapper.sh) if (DBUS_RUNNER) generate_test(DaemonTest True telepathyhelper.cpp mockcontroller.cpp handler.cpp approver.cpp) endif(DBUS_RUNNER) add_subdirectory(mock) history-service-0.1+14.04.20140407/daemon/tests/handler.h0000644000015301777760000000415312320627220023302 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #ifndef HANDLER_H #define HANDLER_H #include #include #include #include #include class Handler : public QObject, public Tp::AbstractClientHandler { Q_OBJECT public: Handler(QObject *parent = 0); ~Handler() { } bool bypassApproval() const; void handleChannels(const Tp::MethodInvocationContextPtr<> &context, const Tp::AccountPtr &account, const Tp::ConnectionPtr &connection, const QList &channels, const QList &requestsSatisfied, const QDateTime &userActionTime, const Tp::AbstractClientHandler::HandlerInfo &handlerInfo); Tp::ChannelClassSpecList channelFilters(); void setBypassApproval(bool bypass); Q_SIGNALS: void textChannelAvailable(Tp::TextChannelPtr textChannel); void callChannelAvailable(Tp::CallChannelPtr callChannel); private Q_SLOTS: void onTextChannelReady(Tp::PendingOperation *op); void onCallChannelReady(Tp::PendingOperation *op); private: QMap mReadyRequests; bool mBypassApproval; }; #endif // HANDLER_H history-service-0.1+14.04.20140407/daemon/tests/dbus-test-wrapper.sh.in0000755000015301777760000000060312320627220026044 0ustar pbusernogroup00000000000000#!/bin/sh # export the home folder to somewhere in /tmp TMPDIR=/tmp/history_test_home rm -rf $TMPDIR export HOME=$TMPDIR # now run gnome-keyring gnome-keyring-daemon -r -d # we need to set this otherwise mission-control doesn't work properly dconf write /org/gnome/empathy/use-conn false mc-tool add mock/mock account0 mc-tool enable mock/mock/account0 $@ RESULT=$? return $RESULT history-service-0.1+14.04.20140407/daemon/tests/mockcontroller.cpp0000644000015301777760000000415312320627220025255 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko */ #include "mockcontroller.h" #define MOCK_SERVICE "com.canonical.MockConnection" #define MOCK_OBJECT "/com/canonical/MockConnection" #define MOCK_INTERFACE "com.canonical.MockConnection" MockController::MockController(QObject *parent) : QObject(parent), mMockInterface(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE) { QDBusConnection::sessionBus().connect(MOCK_SERVICE, MOCK_OBJECT, MOCK_INTERFACE, "MessageSent", this, SIGNAL(MessageSent(QString, QVariantMap))); } MockController *MockController::instance() { static MockController *self = new MockController(); return self; } void MockController::placeIncomingMessage(const QString &message, const QVariantMap &properties) { mMockInterface.call("PlaceIncomingMessage", message, properties); } void MockController::placeCall(const QVariantMap &properties) { mMockInterface.call("PlaceCall", properties); } void MockController::hangupCall(const QString &callerId) { mMockInterface.call("HangupCall", callerId); } void MockController::setCallState(const QString &phoneNumber, const QString &state) { mMockInterface.call("SetCallState", phoneNumber, state); } void MockController::sendDeliveryReport(const QString &phoneNumber, const QString &messageId, const QString &status) { mMockInterface.call("SendDeliveryReport", phoneNumber, messageId, status); } history-service-0.1+14.04.20140407/daemon/tests/approver.cpp0000644000015301777760000001425612320627220024063 0ustar pbusernogroup00000000000000/** * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Authors: Tiago Salem Herrmann */ #include "approver.h" #include #include #include #include #include #include Approver::Approver(QObject* parent) : QObject(parent), Tp::AbstractClientApprover(channelFilters()) { } Approver::~Approver() { } Tp::ChannelClassSpecList Approver::channelFilters() const { Tp::ChannelClassSpecList specList; specList << Tp::ChannelClassSpec::textChat() << Tp::ChannelClassSpec::audioCall(); return specList; } void Approver::addDispatchOperation(const Tp::MethodInvocationContextPtr<> &context, const Tp::ChannelDispatchOperationPtr &dispatchOperation) { bool willHandle = false; QList channels = dispatchOperation->channels(); Q_FOREACH (Tp::ChannelPtr channel, channels) { // Text Channel Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); if (!textChannel.isNull()) { // right now we are not using any of the text channel's features in the approver // so no need to call becomeReady() on it. willHandle = true; continue; } // Call Channel Tp::CallChannelPtr callChannel = Tp::CallChannelPtr::dynamicCast(channel); if (!callChannel.isNull()) { Tp::PendingReady *pr = callChannel->becomeReady(Tp::Features() << Tp::CallChannel::FeatureCore << Tp::CallChannel::FeatureCallState << Tp::CallChannel::FeatureLocalHoldState); mChannels[pr] = callChannel; connect(pr, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onChannelReady(Tp::PendingOperation*))); callChannel->setProperty("accountId", QVariant(dispatchOperation->account()->uniqueIdentifier())); willHandle = true; continue; } } if (willHandle) { mDispatchOps.append(dispatchOperation); } context->setFinished(); // check if we need to approve channels already or if we should wait. processChannels(); } void Approver::processChannels() { Q_FOREACH (Tp::ChannelDispatchOperationPtr dispatchOperation, mDispatchOps) { QList channels = dispatchOperation->channels(); Q_FOREACH (Tp::ChannelPtr channel, channels) { // approve only text channels Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); if (textChannel.isNull()) { continue; } if (dispatchOperation->possibleHandlers().contains(TELEPHONY_SERVICE_HANDLER)) { dispatchOperation->handleWith(TELEPHONY_SERVICE_HANDLER); mDispatchOps.removeAll(dispatchOperation); } // FIXME: this shouldn't happen, but in any case, we need to check what to do when // the phone app client is not available } } } void Approver::acceptCall() { Q_FOREACH (Tp::ChannelDispatchOperationPtr dispatchOperation, mDispatchOps) { QList channels = dispatchOperation->channels(); Q_FOREACH (Tp::ChannelPtr channel, channels) { if (dispatchOperation->possibleHandlers().contains(TELEPHONY_SERVICE_HANDLER)) { dispatchOperation->handleWith(TELEPHONY_SERVICE_HANDLER); mDispatchOps.removeAll(dispatchOperation); } } } } void Approver::rejectCall() { Q_FOREACH (Tp::ChannelDispatchOperationPtr dispatchOperation, mDispatchOps) { QList channels = dispatchOperation->channels(); Q_FOREACH (Tp::ChannelPtr channel, channels) { if (dispatchOperation->possibleHandlers().contains(TELEPHONY_SERVICE_HANDLER)) { Tp::PendingOperation *claimop = dispatchOperation->claim(); mChannels[claimop] = dispatchOperation->channels().first(); connect(claimop, SIGNAL(finished(Tp::PendingOperation*)), this, SLOT(onClaimFinished(Tp::PendingOperation*))); } } } } void Approver::onClaimFinished(Tp::PendingOperation* op) { if(!op || op->isError()) { return; } Tp::CallChannelPtr callChannel = Tp::CallChannelPtr::dynamicCast(mChannels[op]); if (callChannel) { Tp::PendingOperation *hangupop = callChannel->hangup(Tp::CallStateChangeReasonUserRequested, TP_QT_ERROR_REJECTED, QString()); mChannels[hangupop] = callChannel; connect(hangupop, SIGNAL(finished(Tp::PendingOperation*)), this, SLOT(onHangupFinished(Tp::PendingOperation*))); } } void Approver::onHangupFinished(Tp::PendingOperation* op) { if(!op || op->isError()) { return; } mDispatchOps.removeAll(dispatchOperation(op)); mChannels.remove(op); } Tp::ChannelDispatchOperationPtr Approver::dispatchOperation(Tp::PendingOperation *op) { Tp::ChannelPtr channel = mChannels[op]; QString accountId = channel->property("accountId").toString(); Q_FOREACH (Tp::ChannelDispatchOperationPtr dispatchOperation, mDispatchOps) { if (dispatchOperation->account()->uniqueIdentifier() == accountId) { return dispatchOperation; } } return Tp::ChannelDispatchOperationPtr(); } void Approver::onChannelReady(Tp::PendingOperation *op) { Q_EMIT newCall(); } history-service-0.1+14.04.20140407/daemon/telepathyhelper.cpp0000644000015301777760000000735112320627220024260 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "telepathyhelper.h" #include TelepathyHelper::TelepathyHelper(QObject *parent) : QObject(parent), mChannelObserver(0) { mAccountFeatures << Tp::Account::FeatureCore; mContactFeatures << Tp::Contact::FeatureAlias << Tp::Contact::FeatureAvatarData << Tp::Contact::FeatureAvatarToken << Tp::Contact::FeatureCapabilities << Tp::Contact::FeatureSimplePresence; mConnectionFeatures << Tp::Connection::FeatureCore << Tp::Connection::FeatureSelfContact << Tp::Connection::FeatureSimplePresence; Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus()); channelFactory->addCommonFeatures(Tp::Channel::FeatureCore); mAccountManager = Tp::AccountManager::create( Tp::AccountFactory::create(QDBusConnection::sessionBus(), mAccountFeatures), Tp::ConnectionFactory::create(QDBusConnection::sessionBus(), mConnectionFeatures), channelFactory, Tp::ContactFactory::create(mContactFeatures)); connect(mAccountManager->becomeReady(Tp::AccountManager::FeatureCore), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountManagerReady(Tp::PendingOperation*))); mClientRegistrar = Tp::ClientRegistrar::create(mAccountManager); } TelepathyHelper::~TelepathyHelper() { } TelepathyHelper *TelepathyHelper::instance() { static TelepathyHelper* helper = new TelepathyHelper(); return helper; } ChannelObserver *TelepathyHelper::channelObserver() const { return mChannelObserver; } void TelepathyHelper::registerChannelObserver() { // check if this instance is running on the main phone application // or if it is just the plugin imported somewhere else QString observerName = "HistoryDaemonObserver"; mChannelObserver = new ChannelObserver(this); registerClient(mChannelObserver, observerName); Q_EMIT channelObserverCreated(mChannelObserver); } void TelepathyHelper::registerClient(Tp::AbstractClient *client, QString name) { Tp::AbstractClientPtr clientPtr(client); bool succeeded = mClientRegistrar->registerClient(clientPtr, name); if (!succeeded) { name.append("%1"); int count = 0; // limit the number of registered clients to 20, that should be a safe margin while (!succeeded && count < 20) { succeeded = mClientRegistrar->registerClient(clientPtr, name.arg(++count)); if (succeeded) { name = name.arg(count); } } } if (succeeded) { QObject *object = dynamic_cast(client); if (object) { object->setProperty("clientName", TP_QT_IFACE_CLIENT + "." + name ); } } } void TelepathyHelper::onAccountManagerReady(Tp::PendingOperation *op) { Q_UNUSED(op) registerChannelObserver(); } history-service-0.1+14.04.20140407/daemon/HistoryService.xml0000644000015301777760000001626512320627220024065 0ustar pbusernogroup00000000000000 An interface to the history service history-service-0.1+14.04.20140407/daemon/HistoryDaemonObserver.client0000644000015301777760000000137612320627220026053 0ustar pbusernogroup00000000000000[org.freedesktop.Telepathy.Client] Interfaces=org.freedesktop.Telepathy.Client.Observer; [org.freedesktop.Telepathy.Client.Observer.ObserverChannelFilter 0] org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call1 org.freedesktop.Telepathy.Channel.TargetHandleType u=1 org.freedesktop.Telepathy.Channel.Type.Call1.InitialAudio b=true [org.freedesktop.Telepathy.Client.Observer.ObserverChannelFilter 1] org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Text [org.freedesktop.Telepathy.Client.Observer.Capabilities] org.freedesktop.Telepathy.Channel.Type.Call1/audio=true org.freedesktop.Telepathy.Channel.Type.Call1/audio/speex=true [org.freedesktop.Telepathy.Client.Observer] Recover=true history-service-0.1+14.04.20140407/daemon/callchannelobserver.h0000644000015301777760000000242112320627220024533 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef CALLCHANNELOBSERVER_H #define CALLCHANNELOBSERVER_H #include #include class CallChannelObserver : public QObject { Q_OBJECT public: explicit CallChannelObserver(QObject *parent = 0); public Q_SLOTS: void onCallChannelAvailable(Tp::CallChannelPtr callChannel); Q_SIGNALS: void callEnded(Tp::CallChannelPtr callChannel); protected Q_SLOTS: void onCallStateChanged(Tp::CallState state); private: QList mChannels; }; #endif // CALLCHANNELOBSERVER_H history-service-0.1+14.04.20140407/daemon/pluginmanager.h0000644000015301777760000000225212320627220023352 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_PLUGINMANAGER_H #define HISTORY_PLUGINMANAGER_H #include #include "types.h" namespace History { class Plugin; class PluginManager : public QObject { Q_OBJECT public: ~PluginManager(); static PluginManager *instance(); Plugins plugins(); protected: void loadPlugins(); private: explicit PluginManager(QObject *parent = 0); Plugins mPlugins; }; } #endif // HISTORY_PLUGINMANAGER_H history-service-0.1+14.04.20140407/daemon/historydaemon.cpp0000644000015301777760000005366712320627220023761 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historydaemon.h" #include "telepathyhelper.h" #include "filter.h" #include "sort.h" #include "pluginmanager.h" #include "plugin.h" #include "pluginthreadview.h" #include "plugineventview.h" #include #include #include HistoryDaemon::HistoryDaemon(QObject *parent) : QObject(parent), mCallObserver(this), mTextObserver(this) { // get the first plugin if (!History::PluginManager::instance()->plugins().isEmpty()) { mBackend = History::PluginManager::instance()->plugins().first(); } connect(TelepathyHelper::instance(), SIGNAL(channelObserverCreated(ChannelObserver*)), SLOT(onObserverCreated())); connect(&mCallObserver, SIGNAL(callEnded(Tp::CallChannelPtr)), SLOT(onCallEnded(Tp::CallChannelPtr))); connect(&mTextObserver, SIGNAL(messageReceived(Tp::TextChannelPtr,Tp::ReceivedMessage)), SLOT(onMessageReceived(Tp::TextChannelPtr,Tp::ReceivedMessage))); connect(&mTextObserver, SIGNAL(messageSent(Tp::TextChannelPtr,Tp::Message,QString)), SLOT(onMessageSent(Tp::TextChannelPtr,Tp::Message,QString))); connect(&mTextObserver, SIGNAL(messageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)), SLOT(onMessageRead(Tp::TextChannelPtr,Tp::ReceivedMessage))); // FIXME: we need to do this in a better way, but for now this should do mProtocolFlags["ofono"] = History::MatchPhoneNumber; mDBus.connectToBus(); } HistoryDaemon::~HistoryDaemon() { } HistoryDaemon *HistoryDaemon::instance() { static HistoryDaemon *self = new HistoryDaemon(); return self; } QVariantMap HistoryDaemon::threadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants, History::MatchFlags matchFlags, bool create) { if (!mBackend) { return QVariantMap(); } QVariantMap thread = mBackend->threadForParticipants(accountId, type, participants, matchFlags); if (thread.isEmpty() && create) { thread = mBackend->createThreadForParticipants(accountId, type, participants); if (!thread.isEmpty()) { mDBus.notifyThreadsAdded(QList() << thread); } } return thread; } QString HistoryDaemon::queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter) { if (!mBackend) { return QString::null; } History::Sort theSort = History::Sort::fromProperties(sort); History::Filter theFilter = History::Filter::fromProperties(filter); History::PluginThreadView *view = mBackend->queryThreads((History::EventType)type, theSort, theFilter); if (!view) { return QString::null; } // FIXME: maybe we should keep a list of views to manually remove them at some point? view->setParent(this); return view->objectPath(); } QString HistoryDaemon::queryEvents(int type, const QVariantMap &sort, const QVariantMap &filter) { if (!mBackend) { return QString::null; } History::Sort theSort = History::Sort::fromProperties(sort); History::Filter theFilter = History::Filter::fromProperties(filter); History::PluginEventView *view = mBackend->queryEvents((History::EventType)type, theSort, theFilter); if (!view) { return QString::null; } // FIXME: maybe we should keep a list of views to manually remove them at some point? view->setParent(this); return view->objectPath(); } QVariantMap HistoryDaemon::getSingleThread(int type, const QString &accountId, const QString &threadId) { if (!mBackend) { return QVariantMap(); } return mBackend->getSingleThread((History::EventType)type, accountId, threadId); } QVariantMap HistoryDaemon::getSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId) { if (!mBackend) { return QVariantMap(); } return mBackend->getSingleEvent((History::EventType)type, accountId, threadId, eventId); } bool HistoryDaemon::writeEvents(const QList &events) { if (!mBackend) { return false; } QList newEvents; QList modifiedEvents; QMap threads; mBackend->beginBatchOperation(); Q_FOREACH(const QVariantMap &event, events) { History::EventType type = (History::EventType) event[History::FieldType].toInt(); History::EventWriteResult result; // get the threads for the events to notify their modifications QString accountId = event[History::FieldAccountId].toString(); QString threadId = event[History::FieldThreadId].toString(); QVariantMap savedEvent = event; // and finally write the event switch (type) { case History::EventTypeText: result = mBackend->writeTextEvent(savedEvent); break; case History::EventTypeVoice: result = mBackend->writeVoiceEvent(savedEvent); break; } // only get the thread AFTER the event is written to make sure it is up-to-date QVariantMap thread = getSingleThread(type, accountId, threadId); QString hash = hashThread(thread); threads[hash] = thread; // set the participants field in the event savedEvent[History::FieldParticipants] = thread[History::FieldParticipants]; // check if the event was a new one or a modification to an existing one switch (result) { case History::EventWriteCreated: newEvents << savedEvent; break; case History::EventWriteModified: modifiedEvents << savedEvent; break; case History::EventWriteError: mBackend->rollbackBatchOperation(); return false; } } mBackend->endBatchOperation(); // and last but not least, notify the results if (!newEvents.isEmpty()) { mDBus.notifyEventsAdded(newEvents); } if (!modifiedEvents.isEmpty()) { mDBus.notifyEventsModified(modifiedEvents); } if (!threads.isEmpty()) { mDBus.notifyThreadsModified(threads.values()); } return true; } bool HistoryDaemon::removeEvents(const QList &events) { qDebug() << __PRETTY_FUNCTION__; if (!mBackend) { return false; } mBackend->beginBatchOperation(); Q_FOREACH(const QVariantMap &event, events) { History::EventType type = (History::EventType) event[History::FieldType].toInt(); bool success = true; switch (type) { case History::EventTypeText: success = mBackend->removeTextEvent(event); break; case History::EventTypeVoice: success = mBackend->removeVoiceEvent(event); break; } if (!success) { mBackend->rollbackBatchOperation(); return false; } } // now we need to get all the threads that were affected by the removal of events // this loop needs to be separate from the item removal loop because we rely on the // count property of threads to decide if they were just modified or if they need to // be removed. QMap removedThreads; QMap modifiedThreads; Q_FOREACH(const QVariantMap &event, events) { History::EventType type = (History::EventType) event[History::FieldType].toInt(); QString accountId = event[History::FieldAccountId].toString(); QString threadId = event[History::FieldThreadId].toString(); QVariantMap thread = mBackend->getSingleThread(type, accountId, threadId); if (thread.isEmpty()) { continue; } QString hash = hashThread(thread); if (thread[History::FieldCount].toInt() > 0) { // the thread still has items and we should notify it was modified modifiedThreads[hash] = thread; } else { removedThreads[hash] = thread; } } // finally remove the threads that are now empty Q_FOREACH(const QVariantMap &thread, removedThreads.values()) { // the thread is now empty and needs to be removed if (!mBackend->removeThread(thread)) { mBackend->rollbackBatchOperation(); return false; } } mBackend->endBatchOperation(); mDBus.notifyEventsRemoved(events); if (!removedThreads.isEmpty()) { mDBus.notifyThreadsRemoved(removedThreads.values()); } if (!modifiedThreads.isEmpty()) { mDBus.notifyThreadsModified(modifiedThreads.values()); } return true; } bool HistoryDaemon::removeThreads(const QList &threads) { qDebug() << __PRETTY_FUNCTION__; if (!mBackend) { return false; } // In order to remove a thread all we have to do is to remove all its items // then it is going to be removed by removeEvents() once it detects the thread is // empty. QList events; Q_FOREACH(const QVariantMap &thread, threads) { events += mBackend->eventsForThread(thread); } return removeEvents(events); } void HistoryDaemon::onObserverCreated() { qDebug() << __PRETTY_FUNCTION__; ChannelObserver *observer = TelepathyHelper::instance()->channelObserver(); connect(observer, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), &mCallObserver, SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); connect(observer, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)), &mTextObserver, SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); } void HistoryDaemon::onCallEnded(const Tp::CallChannelPtr &channel) { qDebug() << __PRETTY_FUNCTION__; QStringList participants; Q_FOREACH(const Tp::ContactPtr contact, channel->remoteMembers()) { participants << contact->id(); } QString accountId = channel->property(History::FieldAccountId).toString(); QVariantMap thread = threadForParticipants(accountId, History::EventTypeVoice, participants, matchFlagsForChannel(channel), true); // fill the call info QDateTime timestamp = channel->property(History::FieldTimestamp).toDateTime(); // FIXME: check if checking for isRequested() is enough bool incoming = !channel->isRequested(); int duration; bool missed = incoming && channel->callStateReason().reason == Tp::CallStateChangeReasonNoAnswer; if (!missed) { QDateTime activeTime = channel->property("activeTimestamp").toDateTime(); duration = activeTime.secsTo(QDateTime::currentDateTime()); } QString eventId = QString("%1:%2").arg(thread[History::FieldThreadId].toString()).arg(timestamp.toString()); QVariantMap event; event[History::FieldType] = History::EventTypeVoice; event[History::FieldAccountId] = thread[History::FieldAccountId]; event[History::FieldThreadId] = thread[History::FieldThreadId]; event[History::FieldEventId] = eventId; event[History::FieldSenderId] = incoming ? channel->initiatorContact()->id() : "self"; event[History::FieldTimestamp] = timestamp.toString(Qt::ISODate); event[History::FieldNewEvent] = missed; // only mark as a new (unseen) event if it is a missed call event[History::FieldMissed] = missed; event[History::FieldDuration] = duration; writeEvents(QList() << event); } void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message) { qDebug() << __PRETTY_FUNCTION__; // ignore delivery reports for now. // FIXME: maybe we should set the readTimestamp when a delivery report is received if (message.isRescued() || message.isScrollback()) { return; } if (message.isDeliveryReport() && message.deliveryDetails().hasOriginalToken()) { // at this point we assume the delivery report is for a message that was already // sent and properly saved at our database, so we can safely get it here to update QStringList participants; Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) { participants << contact->id(); } QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(), History::EventTypeText, participants, matchFlagsForChannel(textChannel), false); if (thread.isEmpty()) { qWarning() << "Cound not find the thread related to this delivery report."; return; } QVariantMap textEvent = getSingleEvent((int)History::EventTypeText, textChannel->property(History::FieldAccountId).toString(), thread[History::FieldThreadId].toString(), message.deliveryDetails().originalToken()); if (textEvent.isEmpty()) { qWarning() << "Cound not find the original event to update with delivery details."; return; } History::MessageStatus status; switch (message.deliveryDetails().status()) { case Tp::DeliveryStatusAccepted: status = History::MessageStatusAccepted; break; case Tp::DeliveryStatusDeleted: status = History::MessageStatusDeleted; break; case Tp::DeliveryStatusDelivered: status = History::MessageStatusDelivered; break; case Tp::DeliveryStatusPermanentlyFailed: status = History::MessageStatusPermanentlyFailed; break; case Tp::DeliveryStatusRead: status = History::MessageStatusRead; break; case Tp::DeliveryStatusTemporarilyFailed: status = History::MessageStatusTemporarilyFailed; break; case Tp::DeliveryStatusUnknown: status = History::MessageStatusUnknown; break; } textEvent[History::FieldMessageStatus] = (int) status; if (!writeEvents(QList() << textEvent)) { qWarning() << "Failed to save the new message status!"; } return; } QStringList participants; Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) { participants << contact->id(); } QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(), History::EventTypeText, participants, matchFlagsForChannel(textChannel), true); int count = 1; QList attachments; History::MessageType type = History::MessageTypeText; QString subject; if (message.hasNonTextContent()) { QString normalizedAccountId = QString(QCryptographicHash::hash(thread[History::FieldAccountId].toString().toLatin1(), QCryptographicHash::Md5).toHex()); QString normalizedThreadId = QString(QCryptographicHash::hash(thread[History::FieldThreadId].toString().toLatin1(), QCryptographicHash::Md5).toHex()); QString normalizedEventId = QString(QCryptographicHash::hash(message.messageToken().toLatin1(), QCryptographicHash::Md5).toHex()); QString mmsStoragePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); type = History::MessageTypeMultiParty; subject = message.header()["subject"].variant().toString(); QDir dir(mmsStoragePath); if (!dir.exists("history-service") && !dir.mkpath("history-service")) { qDebug() << "Failed to create dir"; return; } dir.cd("history-service"); Q_FOREACH(const Tp::MessagePart &part, message.parts()) { // ignore the header part if (part["content-type"].variant().toString().isEmpty()) { continue; } mmsStoragePath = dir.absoluteFilePath(QString("attachments/%1/%2/%3/"). arg(normalizedAccountId, normalizedThreadId, normalizedEventId)); QFile file(mmsStoragePath+QString::number(count++)); if (!dir.mkpath(mmsStoragePath) || !file.open(QIODevice::WriteOnly)) { qWarning() << "Failed to save attachment"; continue; } file.write(part["content"].variant().toByteArray()); file.close(); QVariantMap attachment; attachment[History::FieldAccountId] = thread[History::FieldAccountId]; attachment[History::FieldThreadId] = thread[History::FieldThreadId]; attachment[History::FieldEventId] = message.messageToken(); attachment[History::FieldAttachmentId] = part["identifier"].variant(); attachment[History::FieldContentType] = part["content-type"].variant(); attachment[History::FieldFilePath] = file.fileName(); attachment[History::FieldStatus] = (int) History::AttachmentDownloaded; attachments << attachment; } } QVariantMap event; event[History::FieldType] = History::EventTypeText; event[History::FieldAccountId] = thread[History::FieldAccountId]; event[History::FieldThreadId] = thread[History::FieldThreadId]; event[History::FieldEventId] = message.messageToken(); event[History::FieldSenderId] = message.sender()->id(); event[History::FieldTimestamp] = message.received().toString(Qt::ISODate); event[History::FieldNewEvent] = true; // message is always unread until it reaches HistoryDaemon::onMessageRead event[History::FieldMessage] = message.text(); event[History::FieldMessageType] = (int)type; event[History::FieldMessageStatus] = (int)History::MessageStatusUnknown; event[History::FieldReadTimestamp] = QDateTime().toString(Qt::ISODate); event[History::FieldSubject] = subject; event[History::FieldAttachments] = QVariant::fromValue(attachments); writeEvents(QList() << event); } void HistoryDaemon::onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message) { qDebug() << __PRETTY_FUNCTION__; // FIXME: implement } void HistoryDaemon::onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken) { qDebug() << __PRETTY_FUNCTION__; QStringList participants; Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) { participants << contact->id(); } QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(), History::EventTypeText, participants, matchFlagsForChannel(textChannel), true); QVariantMap event; event[History::FieldType] = History::EventTypeText; event[History::FieldAccountId] = thread[History::FieldAccountId]; event[History::FieldThreadId] = thread[History::FieldThreadId]; event[History::FieldEventId] = messageToken; event[History::FieldSenderId] = "self"; event[History::FieldTimestamp] = QDateTime::currentDateTime().toString(Qt::ISODate); // FIXME: check why message.sent() is empty event[History::FieldNewEvent] = false; // outgoing messages are never new (unseen) event[History::FieldMessage] = message.text(); event[History::FieldMessageType] = (int)History::MessageTypeText; // FIXME: add support for MMS event[History::FieldMessageStatus] = (int)History::MessageStatusUnknown; event[History::FieldReadTimestamp] = QDateTime().toString(Qt::ISODate); event[History::FieldSubject] = ""; writeEvents(QList() << event); } History::MatchFlags HistoryDaemon::matchFlagsForChannel(const Tp::ChannelPtr &channel) { QString protocol = channel->connection()->protocolName(); if (mProtocolFlags.contains(protocol)) { return mProtocolFlags[protocol]; } // default to this value return History::MatchCaseSensitive; } QString HistoryDaemon::hashThread(const QVariantMap &thread) { QString hash = QString::number(thread[History::FieldType].toInt()); hash += "#-#" + thread[History::FieldAccountId].toString(); hash += "#-#" + thread[History::FieldThreadId].toString(); return hash; } history-service-0.1+14.04.20140407/daemon/channelobserver.h0000644000015301777760000000426612320627220023710 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef CHANNELOBSERVER_H #define CHANNELOBSERVER_H #include #include #include #include class ChannelObserver : public QObject, public Tp::AbstractClientObserver { Q_OBJECT public: explicit ChannelObserver(QObject *parent = 0); Tp::ChannelClassSpecList channelFilters() const; void observeChannels(const Tp::MethodInvocationContextPtr<> &context, const Tp::AccountPtr &account, const Tp::ConnectionPtr &connection, const QList &channels, const Tp::ChannelDispatchOperationPtr &dispatchOperation, const QList &requestsSatisfied, const Tp::AbstractClientObserver::ObserverInfo &observerInfo); Q_SIGNALS: void textChannelAvailable(const Tp::TextChannelPtr &channel); void callChannelAvailable(const Tp::CallChannelPtr &channel); protected Q_SLOTS: void onChannelInvalidated(); void onCallChannelReady(Tp::PendingOperation *op); void onTextChannelReady(Tp::PendingOperation *op); protected: void checkContextFinished(Tp::Channel *channel); private: QMap > mContexts; QMap mReadyMap; QList mChannels; }; #endif // CHANNELOBSERVER_H history-service-0.1+14.04.20140407/daemon/channelobserver.cpp0000644000015301777760000001564712320627230024251 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "channelobserver.h" #include "types.h" #include #include #include #include #include ChannelObserver::ChannelObserver(QObject *parent) : QObject(parent), Tp::AbstractClientObserver(channelFilters(), true) { } Tp::ChannelClassSpecList ChannelObserver::channelFilters() const { Tp::ChannelClassSpecList specList; specList << Tp::ChannelClassSpec::audioCall(); specList << Tp::ChannelClassSpec::textChat(); return specList; } void ChannelObserver::observeChannels(const Tp::MethodInvocationContextPtr<> &context, const Tp::AccountPtr &account, const Tp::ConnectionPtr &connection, const QList &channels, const Tp::ChannelDispatchOperationPtr &dispatchOperation, const QList &requestsSatisfied, const Tp::AbstractClientObserver::ObserverInfo &observerInfo) { Q_UNUSED(account) Q_UNUSED(connection) Q_UNUSED(dispatchOperation) Q_UNUSED(requestsSatisfied) Q_UNUSED(observerInfo) qDebug() << __PRETTY_FUNCTION__; Q_FOREACH (Tp::ChannelPtr channel, channels) { // tp-qt has not support for the SMS interface if (channel->immutableProperties().contains(TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash"))) { if (channel->immutableProperties()[TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash")].toBool()) { continue; } } mContexts[channel.data()] = context; mChannels.append(channel); connect(channel.data(), SIGNAL(invalidated(Tp::DBusProxy*,const QString&, const QString&)), SLOT(onChannelInvalidated())); channel->setProperty(History::FieldAccountId, account->uniqueIdentifier()); qDebug() << "Saving account id:" << account->uniqueIdentifier(); Tp::CallChannelPtr callChannel = Tp::CallChannelPtr::dynamicCast(channel); if (callChannel) { Tp::PendingReady *ready = callChannel->becomeReady(Tp::Features() << Tp::CallChannel::FeatureCore << Tp::CallChannel::FeatureCallMembers << Tp::CallChannel::FeatureCallState << Tp::CallChannel::FeatureContents << Tp::CallChannel::FeatureLocalHoldState); connect(ready, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onCallChannelReady(Tp::PendingOperation*))); mReadyMap[ready] = callChannel; } Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); if (textChannel) { Tp::PendingReady *ready = textChannel->becomeReady(Tp::Features() << Tp::TextChannel::FeatureCore << Tp::TextChannel::FeatureChatState << Tp::TextChannel::FeatureMessageCapabilities << Tp::TextChannel::FeatureMessageQueue << Tp::TextChannel::FeatureMessageSentSignal); connect(ready, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onTextChannelReady(Tp::PendingOperation*))); mReadyMap[ready] = textChannel; } } } void ChannelObserver::onCallChannelReady(Tp::PendingOperation *op) { Tp::PendingReady *ready = qobject_cast(op); if (!ready) { qCritical() << "Pending operation is not a pending ready:" << op; return; } if (!mReadyMap.contains(ready)) { qWarning() << "Pending ready finished but not on the map:" << ready; return; } Tp::CallChannelPtr callChannel = Tp::CallChannelPtr::dynamicCast(mReadyMap[ready]); mReadyMap.remove(ready); if (!callChannel) { qWarning() << "Ready channel is not a call channel:" << callChannel; return; } Q_EMIT callChannelAvailable(callChannel); checkContextFinished(callChannel.data()); } void ChannelObserver::onChannelInvalidated() { Tp::ChannelPtr channel(qobject_cast(sender())); mChannels.removeAll(channel); } void ChannelObserver::onTextChannelReady(Tp::PendingOperation *op) { Tp::PendingReady *ready = qobject_cast(op); if (!ready) { qCritical() << "Pending operation is not a pending ready:" << op; return; } if (!mReadyMap.contains(ready)) { qWarning() << "Pending ready finished but not on the map:" << ready; return; } Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(mReadyMap[ready]); mReadyMap.remove(ready); if (!textChannel) { qWarning() << "Ready channel is not a call channel:" << textChannel; return; } Q_EMIT textChannelAvailable(textChannel); checkContextFinished(textChannel.data()); } void ChannelObserver::checkContextFinished(Tp::Channel *channel) { if (!mContexts.contains(channel)) { qWarning() << "Context for channel not available:" << channel; return; } Tp::MethodInvocationContextPtr<> context = mContexts[channel]; mContexts.remove(channel); // check if this is the last channel from the context Q_FOREACH(Tp::MethodInvocationContextPtr<> otherContext, mContexts.values()) { // if we find the context, just return from the function. We need to wait // for the other channels to become ready before setting the context finished if (otherContext == context) { return; } } context->setFinished(); } history-service-0.1+14.04.20140407/daemon/telepathyhelper.h0000644000015301777760000000355712320627220023731 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Tiago Salem Herrmann * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef TELEPATHYHELPER_H #define TELEPATHYHELPER_H #include #include #include #include #include #include #include "channelobserver.h" class TelepathyHelper : public QObject { Q_OBJECT public: ~TelepathyHelper(); static TelepathyHelper *instance(); ChannelObserver *channelObserver() const; void registerClient(Tp::AbstractClient *client, QString name); Q_SIGNALS: void channelObserverCreated(ChannelObserver *observer); public Q_SLOTS: void registerChannelObserver(); private Q_SLOTS: void onAccountManagerReady(Tp::PendingOperation *op); private: explicit TelepathyHelper(QObject *parent = 0); Tp::AccountManagerPtr mAccountManager; Tp::Features mAccountManagerFeatures; Tp::Features mAccountFeatures; Tp::Features mContactFeatures; Tp::Features mConnectionFeatures; Tp::ClientRegistrarPtr mClientRegistrar; ChannelObserver *mChannelObserver; }; #endif // TELEPATHYHELPER_H history-service-0.1+14.04.20140407/daemon/textchannelobserver.h0000644000015301777760000000367512320627220024620 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef TEXTCHANNELOBSERVER_H #define TEXTCHANNELOBSERVER_H #include #include #include class TextChannelObserver : public QObject { Q_OBJECT public: explicit TextChannelObserver(QObject *parent = 0); public Q_SLOTS: void onTextChannelAvailable(Tp::TextChannelPtr textChannel); Q_SIGNALS: void messageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); void messageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); void messageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken); protected: void showNotificationForMessage(const Tp::ReceivedMessage &message); Tp::TextChannelPtr channelFromPath(const QString &path); protected Q_SLOTS: void onTextChannelInvalidated(); void onMessageReceived(const Tp::ReceivedMessage &message); void onMessageSent(const Tp::Message &message, Tp::MessageSendingFlags flags, const QString &sentMessageToken); void onPendingMessageRemoved(const Tp::ReceivedMessage &message); private: QList mChannels; }; #endif // TEXTCHANNELOBSERVER_H history-service-0.1+14.04.20140407/daemon/historyservicedbus.h0000644000015301777760000000544712320627220024472 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYSERVICEDBUS_H #define HISTORYSERVICEDBUS_H #include #include #include "types.h" class HistoryServiceAdaptor; class HistoryServiceDBus : public QObject, public QDBusContext { Q_OBJECT public: explicit HistoryServiceDBus(QObject *parent = 0); bool connectToBus(); void notifyThreadsAdded(const QList &threads); void notifyThreadsModified(const QList &threads); void notifyThreadsRemoved(const QList &threads); void notifyEventsAdded(const QList &events); void notifyEventsModified(const QList &events); void notifyEventsRemoved(const QList &events); // functions exposed on DBUS QVariantMap ThreadForParticipants(const QString &accountId, int type, const QStringList &participants, int matchFlags, bool create); bool WriteEvents(const QList &events); bool RemoveThreads(const QList &threads); bool RemoveEvents(const QList &events); // views QString QueryThreads(int type, const QVariantMap &sort, const QVariantMap &filter); QString QueryEvents(int type, const QVariantMap &sort, const QVariantMap &filter); QVariantMap GetSingleThread(int type, const QString &accountId, const QString &threadId); QVariantMap GetSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId); Q_SIGNALS: // signals that will be relayed into the bus void ThreadsAdded(const QList &threads); void ThreadsModified(const QList &threads); void ThreadsRemoved(const QList &threads); void EventsAdded(const QList &events); void EventsModified(const QList &events); void EventsRemoved(const QList &events); private: HistoryServiceAdaptor *mAdaptor; }; #endif // HISTORYSERVICEDBUS_H ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000history-service-0.1+14.04.20140407/daemon/org.freedesktop.Telepathy.Client.HistoryDaemonObserver.service.inhistory-service-0.1+14.04.20140407/daemon/org.freedesktop.Telepathy.Client.HistoryDaemonObserver.ser0000644000015301777760000000021612320627220033631 0ustar pbusernogroup00000000000000[D-BUS Service] Name=org.freedesktop.Telepathy.Client.HistoryDaemonObserver Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/history-daemon history-service-0.1+14.04.20140407/daemon/historyservicedbus.cpp0000644000015301777760000001040012320627220025006 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historydaemon.h" #include "historyservicedbus.h" #include "historyserviceadaptor.h" #include "types.h" Q_DECLARE_METATYPE(QList< QVariantMap >) HistoryServiceDBus::HistoryServiceDBus(QObject *parent) : QObject(parent), mAdaptor(0) { qDBusRegisterMetaType >(); connectToBus(); } bool HistoryServiceDBus::connectToBus() { bool ok = QDBusConnection::sessionBus().registerService(History::DBusService); if (!ok) { return false; } if (!mAdaptor) { mAdaptor = new HistoryServiceAdaptor(this); } return QDBusConnection::sessionBus().registerObject(History::DBusObjectPath, this); } void HistoryServiceDBus::notifyThreadsAdded(const QList &threads) { Q_EMIT ThreadsAdded(threads); } void HistoryServiceDBus::notifyThreadsModified(const QList &threads) { Q_EMIT ThreadsModified(threads); } void HistoryServiceDBus::notifyThreadsRemoved(const QList &threads) { Q_EMIT ThreadsRemoved(threads); } void HistoryServiceDBus::notifyEventsAdded(const QList &events) { Q_EMIT EventsAdded(events); } void HistoryServiceDBus::notifyEventsModified(const QList &events) { Q_EMIT EventsModified(events); } void HistoryServiceDBus::notifyEventsRemoved(const QList &events) { Q_EMIT EventsRemoved(events); } QVariantMap HistoryServiceDBus::ThreadForParticipants(const QString &accountId, int type, const QStringList &participants, int matchFlags, bool create) { return HistoryDaemon::instance()->threadForParticipants(accountId, (History::EventType) type, participants, (History::MatchFlags) matchFlags, create); } bool HistoryServiceDBus::WriteEvents(const QList &events) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->writeEvents(events); } bool HistoryServiceDBus::RemoveThreads(const QList &threads) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->removeThreads(threads); } bool HistoryServiceDBus::RemoveEvents(const QList &events) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->removeEvents(events); } QString HistoryServiceDBus::QueryThreads(int type, const QVariantMap &sort, const QVariantMap &filter) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->queryThreads(type, sort, filter); } QString HistoryServiceDBus::QueryEvents(int type, const QVariantMap &sort, const QVariantMap &filter) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->queryEvents(type, sort, filter); } QVariantMap HistoryServiceDBus::GetSingleThread(int type, const QString &accountId, const QString &threadId) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->getSingleThread(type, accountId, threadId); } QVariantMap HistoryServiceDBus::GetSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId) { qDebug() << __PRETTY_FUNCTION__; return HistoryDaemon::instance()->getSingleEvent(type, accountId, threadId, eventId); } history-service-0.1+14.04.20140407/daemon/main.cpp0000644000015301777760000000250212320627220021776 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "historydaemon.h" #include #include bool checkApplicationRunning() { bool result = false; QDBusReply reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(History::DBusService); if (reply.isValid()) { result = reply.value(); } return result; } int main(int argc, char **argv) { Tp::registerTypes(); Tp::enableWarnings(true); QCoreApplication app(argc, argv); if (checkApplicationRunning()) { return 1; } HistoryDaemon::instance(); return app.exec(); } history-service-0.1+14.04.20140407/daemon/CMakeLists.txt0000644000015301777760000000356612320627220023121 0ustar pbusernogroup00000000000000 set(qt_SRCS callchannelobserver.cpp channelobserver.cpp historydaemon.cpp historyservicedbus.cpp pluginmanager.cpp telepathyhelper.cpp textchannelobserver.cpp ) set(daemon_SRCS main.cpp ${qt_SRCS}) include_directories( ${TP_QT5_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) qt5_add_dbus_adaptor(daemon_SRCS HistoryService.xml historyservicedbus.h HistoryServiceDBus) add_executable(history-daemon ${daemon_SRCS} ${daemon_HDRS}) qt5_use_modules(history-daemon Core DBus) target_link_libraries(history-daemon ${TP_QT5_LIBRARIES} historyservice ) # Handle i18n in desktop files set(desktop_FILES history-daemon.desktop) foreach(DESKTOP_FILE ${desktop_FILES}) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE}) file(STRINGS ${DESKTOP_FILE}.in DESKTOP_FILE_CONTENTS) foreach(LINE ${DESKTOP_FILE_CONTENTS}) string(REGEX REPLACE "tr\\\(\"(.*)\"\\\)" "\\1" LINE "${LINE}") file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE} "${LINE}\n") endforeach(LINE) endforeach(DESKTOP_FILE) configure_file(org.freedesktop.Telepathy.Client.HistoryDaemonObserver.service.in org.freedesktop.Telepathy.Client.HistoryDaemonObserver.service) configure_file(com.canonical.HistoryService.service.in com.canonical.HistoryService.service) install(TARGETS history-daemon RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Telepathy.Client.HistoryDaemonObserver.service ${CMAKE_CURRENT_BINARY_DIR}/com.canonical.HistoryService.service DESTINATION share/dbus-1/services ) install(FILES HistoryDaemonObserver.client DESTINATION share/telepathy/clients) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/history-daemon.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) add_subdirectory(tests) history-service-0.1+14.04.20140407/daemon/historydaemon.h0000644000015301777760000000541112320627220023406 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORYDAEMON_H #define HISTORYDAEMON_H #include #include #include "types.h" #include "textchannelobserver.h" #include "callchannelobserver.h" #include "historyservicedbus.h" class HistoryDaemon : public QObject { Q_OBJECT public: ~HistoryDaemon(); static HistoryDaemon *instance(); QVariantMap threadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants, History::MatchFlags matchFlags = History::MatchCaseSensitive, bool create = true); QString queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter); QString queryEvents(int type, const QVariantMap &sort, const QVariantMap &filter); QVariantMap getSingleThread(int type, const QString &accountId, const QString &threadId); QVariantMap getSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId); bool writeEvents(const QList &events); bool removeEvents(const QList &events); bool removeThreads(const QList &threads); private Q_SLOTS: void onObserverCreated(); void onCallEnded(const Tp::CallChannelPtr &channel); void onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); void onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); void onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken); protected: History::MatchFlags matchFlagsForChannel(const Tp::ChannelPtr &channel); QString hashThread(const QVariantMap &thread); private: HistoryDaemon(QObject *parent = 0); CallChannelObserver mCallObserver; TextChannelObserver mTextObserver; QMap mProtocolFlags; History::PluginPtr mBackend; HistoryServiceDBus mDBus; }; #endif history-service-0.1+14.04.20140407/daemon/textchannelobserver.cpp0000644000015301777760000000570612320627220025150 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "textchannelobserver.h" #include #include TextChannelObserver::TextChannelObserver(QObject *parent) : QObject(parent) { } void TextChannelObserver::onTextChannelAvailable(Tp::TextChannelPtr textChannel) { connect(textChannel.data(), SIGNAL(invalidated(Tp::DBusProxy*,const QString&, const QString&)), SLOT(onTextChannelInvalidated())); connect(textChannel.data(), SIGNAL(messageReceived(const Tp::ReceivedMessage&)), SLOT(onMessageReceived(const Tp::ReceivedMessage&))); connect(textChannel.data(), SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)), SLOT(onMessageSent(Tp::Message,Tp::MessageSendingFlags,QString))); connect(textChannel.data(), SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)), SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&))); // process the messages that are already pending in the channel Q_FOREACH(const Tp::ReceivedMessage &message, textChannel->messageQueue()) { Q_EMIT messageReceived(textChannel, message); } mChannels.append(textChannel); } void TextChannelObserver::onTextChannelInvalidated() { Tp::TextChannelPtr textChannel(qobject_cast(sender())); mChannels.removeAll(textChannel); } void TextChannelObserver::onMessageReceived(const Tp::ReceivedMessage &message) { Tp::TextChannelPtr textChannel(qobject_cast(sender())); if (textChannel.isNull()) { return; } Q_EMIT messageReceived(textChannel, message); } void TextChannelObserver::onMessageSent(const Tp::Message &message, Tp::MessageSendingFlags flags, const QString &sentMessageToken) { Tp::TextChannelPtr textChannel(qobject_cast(sender())); if (textChannel.isNull()) { return; } Q_EMIT messageSent(textChannel, message, sentMessageToken); } void TextChannelObserver::onPendingMessageRemoved(const Tp::ReceivedMessage &message) { Tp::TextChannelPtr textChannel(qobject_cast(sender())); if (textChannel.isNull()) { return; } Q_EMIT messageRead(textChannel, message); } history-service-0.1+14.04.20140407/daemon/pluginmanager.cpp0000644000015301777760000000326412320627220023711 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "pluginmanager.h" #include "config.h" #include "plugin.h" #include #include #include namespace History { PluginManager::PluginManager(QObject *parent) : QObject(parent) { loadPlugins(); } PluginManager::~PluginManager() { } PluginManager *PluginManager::instance() { static PluginManager *self = new PluginManager(); return self; } Plugins PluginManager::plugins() { return mPlugins; } void PluginManager::loadPlugins() { QString pluginPath = qgetenv("HISTORY_PLUGIN_PATH"); if (pluginPath.isEmpty()) { pluginPath = HISTORY_PLUGIN_PATH; } QDir dir(pluginPath); Q_FOREACH (QString fileName, dir.entryList(QStringList() << "*.so", QDir::Files)) { QPluginLoader loader(dir.absoluteFilePath(fileName)); Plugin *plugin = qobject_cast(loader.instance()); if (plugin) { mPlugins.append(PluginPtr(plugin)); } } } } history-service-0.1+14.04.20140407/plugins/0000755000015301777760000000000012320627745020601 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/plugins/sqlite/0000755000015301777760000000000012320627745022102 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/plugins/sqlite/sqlitehistorythreadview.cpp0000644000015301777760000000654512320627220027612 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "sqlitehistorythreadview.h" #include "sqlitedatabase.h" #include "sqlitehistoryplugin.h" #include "sort.h" #include #include #include SQLiteHistoryThreadView::SQLiteHistoryThreadView(SQLiteHistoryPlugin *plugin, History::EventType type, const History::Sort &sort, const History::Filter &filter) : History::PluginThreadView(), mPlugin(plugin), mType(type), mSort(sort), mFilter(filter), mPageSize(15), mQuery(SQLiteDatabase::instance()->database()), mOffset(0), mValid(true) { qDebug() << __PRETTY_FUNCTION__; mTemporaryTable = QString("threadview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz")); mQuery.setForwardOnly(true); // FIXME: validate the filter QString condition = filter.toString(); QString order; if (!sort.sortField().isNull()) { order = QString("ORDER BY %1 %2").arg(sort.sortField(), sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); // FIXME: check case sensitiviy } QString queryText = QString("CREATE TEMP TABLE %1 AS ").arg(mTemporaryTable); queryText += mPlugin->sqlQueryForThreads(type, condition, order); // create the temporary table if (!mQuery.exec(queryText)) { qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); mValid = false; Q_EMIT Invalidated(); return; } mQuery.exec(QString("SELECT count(*) FROM %1").arg(mTemporaryTable)); mQuery.next(); } SQLiteHistoryThreadView::~SQLiteHistoryThreadView() { if (!mQuery.exec(QString("DROP TABLE IF EXISTS %1").arg(mTemporaryTable))) { qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); return; } } QList SQLiteHistoryThreadView::NextPage() { qDebug() << __PRETTY_FUNCTION__; QList threads; // now prepare for selecting from it mQuery.prepare(QString("SELECT * FROM %1 LIMIT %2 OFFSET %3").arg(mTemporaryTable, QString::number(mPageSize), QString::number(mOffset))); if (!mQuery.exec()) { qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); mValid = false; Q_EMIT Invalidated(); return threads; } threads = mPlugin->parseThreadResults(mType, mQuery); mOffset += mPageSize; mQuery.clear(); return threads; } bool SQLiteHistoryThreadView::IsValid() const { return mValid; } history-service-0.1+14.04.20140407/plugins/sqlite/sqlitehistorythreadview.h0000644000015301777760000000314612320627220027251 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef SQLITEHISTORYTHREADVIEW_H #define SQLITEHISTORYTHREADVIEW_H #include "pluginthreadview.h" #include "filter.h" #include "types.h" #include "sort.h" #include class SQLiteHistoryPlugin; class SQLiteHistoryThreadView : public History::PluginThreadView { Q_OBJECT public: SQLiteHistoryThreadView(SQLiteHistoryPlugin *plugin, History::EventType type, const History::Sort &sort, const History::Filter &filter); ~SQLiteHistoryThreadView(); QList NextPage(); bool IsValid() const; private: History::EventType mType; History::Sort mSort; History::Filter mFilter; QSqlQuery mQuery; int mPageSize; SQLiteHistoryPlugin *mPlugin; QString mTemporaryTable; int mOffset; bool mValid; }; #endif // SQLITEHISTORYTHREADVIEW_H history-service-0.1+14.04.20140407/plugins/sqlite/tests/0000755000015301777760000000000012320627745023244 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/plugins/sqlite/tests/SqliteEventViewTest.cpp0000644000015301777760000001544112320627220027677 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "sqlitehistoryplugin.h" #include "sqlitedatabase.h" #include "sqlitehistoryeventview.h" #include "textevent.h" #include "voiceevent.h" #include "intersectionfilter.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::MatchFlags) #define EVENT_COUNT 50 class SqliteEventViewTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testNextPage(); void testFilter(); void testSort(); private: SQLiteHistoryPlugin *mPlugin; void populateDatabase(); }; void SqliteEventViewTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qputenv("HISTORY_SQLITE_DBPATH", ":memory:"); mPlugin = new SQLiteHistoryPlugin(this); populateDatabase(); } void SqliteEventViewTest::testNextPage() { // create a view to return all text threads and check that the right number of items get returned History::PluginEventView *view = mPlugin->queryEvents(History::EventTypeText); QVERIFY(view->IsValid()); QList events = view->NextPage(); QList allEvents; while (events.count() > 0) { allEvents << events; events = view->NextPage(); } QCOMPARE(allEvents.count(), EVENT_COUNT * 2); Q_FOREACH(const QVariantMap &event, events) { QCOMPARE(event[History::FieldType].toInt(), (int) History::EventTypeText); } delete view; } void SqliteEventViewTest::testFilter() { History::IntersectionFilter filter; filter.append(History::Filter(History::FieldAccountId, "account0")); filter.append(History::Filter(History::FieldThreadId, "participant0")); filter.append(History::Filter(History::FieldEventId, "event21")); History::PluginEventView *view = mPlugin->queryEvents(History::EventTypeVoice, History::Sort(History::FieldAccountId), filter); QVERIFY(view->IsValid()); QList events = view->NextPage(); QCOMPARE(events.count(), 1); QVariantMap event = events.first(); QCOMPARE(event[History::FieldAccountId].toString(), QString("account0")); QCOMPARE(event[History::FieldType].toInt(), (int) History::EventTypeVoice); QCOMPARE(event[History::FieldThreadId].toString(), QString("participant0")); QCOMPARE(event[History::FieldEventId].toString(), QString("event21")); // make sure no more items are returned QVERIFY(view->NextPage().isEmpty()); delete view; } void SqliteEventViewTest::testSort() { History::Sort ascendingSort(History::FieldEventId, Qt::AscendingOrder); History::PluginEventView *view = mPlugin->queryEvents(History::EventTypeText, ascendingSort); QVERIFY(view->IsValid()); QList allEvents; QList events = view->NextPage(); while (!events.isEmpty()) { allEvents << events; events = view->NextPage(); } QCOMPARE(allEvents.first()[History::FieldEventId].toString(), QString("event00")); QCOMPARE(allEvents.last()[History::FieldEventId].toString(), QString("event%1").arg(EVENT_COUNT-1)); delete view; History::Sort descendingSort(History::FieldEventId, Qt::DescendingOrder); allEvents.clear(); view = mPlugin->queryEvents(History::EventTypeVoice, descendingSort); QVERIFY(view->IsValid()); events = view->NextPage(); while (!events.isEmpty()) { allEvents << events; events = view->NextPage(); } QCOMPARE(allEvents.first()[History::FieldEventId].toString(), QString("event%1").arg(EVENT_COUNT-1)); QCOMPARE(allEvents.last()[History::FieldEventId].toString(), QString("event00")); delete view; } void SqliteEventViewTest::populateDatabase() { mPlugin->beginBatchOperation(); // create two threads of each type for (int i = 0; i < 2; ++i) { QVariantMap voiceThread = mPlugin->createThreadForParticipants(QString("account%1").arg(i), History::EventTypeVoice, QStringList() << QString("participant%1").arg(i)); // now create some events for this thread for (int j = 0; j < EVENT_COUNT; ++j) { History::VoiceEvent voiceEvent(voiceThread[History::FieldAccountId].toString(), voiceThread[History::FieldThreadId].toString(), QString("event%1").arg(j, 2, 10, QChar('0')), j % 2 ? "self" : QString("participant%1").arg(i), QDateTime::currentDateTime(), j % 2, j % 2, j % 2 ? QTime(i, j, 0) : QTime()); QCOMPARE(mPlugin->writeVoiceEvent(voiceEvent.properties()), History::EventWriteCreated); } QVariantMap textThread = mPlugin->createThreadForParticipants(QString("account%1").arg(i), History::EventTypeText, QStringList() << QString("participant%1").arg(i)); for (int j = 0; j < EVENT_COUNT; ++j) { History::TextEvent textEvent(textThread[History::FieldAccountId].toString(), textThread[History::FieldThreadId].toString(), QString("event%1").arg(j, 2, 10, QChar('0')), j % 2 ? "self" : QString("participant%1").arg(i), QDateTime::currentDateTime(), j % 2, QString("Hello %1").arg(j), History::MessageTypeText, History::MessageStatusDelivered); QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated); } } mPlugin->endBatchOperation(); } QTEST_MAIN(SqliteEventViewTest) #include "SqliteEventViewTest.moc" history-service-0.1+14.04.20140407/plugins/sqlite/tests/SqliteThreadViewTest.cpp0000644000015301777760000001220712320627220030022 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "sqlitehistoryplugin.h" #include "sqlitehistorythreadview.h" #include "sqlitedatabase.h" #include "textevent.h" #include "voiceevent.h" #include "unionfilter.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::MatchFlags) #define THREAD_COUNT 50 class SqliteThreadViewTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testNextPage(); void testFilter(); void testSort(); private: SQLiteHistoryPlugin *mPlugin; void populateDatabase(); }; void SqliteThreadViewTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qputenv("HISTORY_SQLITE_DBPATH", ":memory:"); mPlugin = new SQLiteHistoryPlugin(this); populateDatabase(); } void SqliteThreadViewTest::testNextPage() { // create a view to return all text threads and check that the right number of items get returned History::PluginThreadView *view = mPlugin->queryThreads(History::EventTypeText); QVERIFY(view->IsValid()); QList threads = view->NextPage(); QList allThreads; while (threads.count() > 0) { allThreads << threads; threads = view->NextPage(); } QCOMPARE(allThreads.count(), THREAD_COUNT); Q_FOREACH(const QVariantMap &thread, threads) { QCOMPARE(thread[History::FieldType].toInt(), (int) History::EventTypeText); } delete view; } void SqliteThreadViewTest::testFilter() { History::UnionFilter filter; filter.append(History::Filter(History::FieldAccountId, "account10")); filter.append(History::Filter(History::FieldAccountId, "account35")); History::PluginThreadView *view = mPlugin->queryThreads(History::EventTypeVoice, History::Sort(History::FieldAccountId), filter); QVERIFY(view->IsValid()); QList threads = view->NextPage(); QCOMPARE(threads.count(), 2); QCOMPARE(threads.first()[History::FieldAccountId].toString(), QString("account10")); QCOMPARE(threads.first()[History::FieldType].toInt(), (int) History::EventTypeVoice); QCOMPARE(threads.last()[History::FieldAccountId].toString(), QString("account35")); QCOMPARE(threads.last()[History::FieldType].toInt(), (int) History::EventTypeVoice); // make sure no more items are returned QVERIFY(view->NextPage().isEmpty()); delete view; } void SqliteThreadViewTest::testSort() { History::Sort ascendingSort(History::FieldAccountId, Qt::AscendingOrder); History::PluginThreadView *view = mPlugin->queryThreads(History::EventTypeText, ascendingSort); QVERIFY(view->IsValid()); QList allThreads; QList threads = view->NextPage(); while (!threads.isEmpty()) { allThreads << threads; threads = view->NextPage(); } QCOMPARE(allThreads.first()[History::FieldAccountId].toString(), QString("account00")); QCOMPARE(allThreads.last()[History::FieldAccountId].toString(), QString("account%1").arg(THREAD_COUNT-1)); delete view; History::Sort descendingSort(History::FieldAccountId, Qt::DescendingOrder); allThreads.clear(); view = mPlugin->queryThreads(History::EventTypeVoice, descendingSort); QVERIFY(view->IsValid()); threads = view->NextPage(); while (!threads.isEmpty()) { allThreads << threads; threads = view->NextPage(); } QCOMPARE(allThreads.first()[History::FieldAccountId].toString(), QString("account%1").arg(THREAD_COUNT-1)); QCOMPARE(allThreads.last()[History::FieldAccountId].toString(), QString("account00")); delete view; } void SqliteThreadViewTest::populateDatabase() { mPlugin->beginBatchOperation(); // create voice threads for (int i = 0; i < THREAD_COUNT; ++i) { mPlugin->createThreadForParticipants(QString("account%1").arg(i, 2, 10, QChar('0')), History::EventTypeVoice, QStringList() << QString("participant%1").arg(i, 2, 10, QChar('0'))); } // and the text threads for (int i = 0; i < THREAD_COUNT; ++i) { mPlugin->createThreadForParticipants(QString("account%1").arg(i, 2, 10, QChar('0')), History::EventTypeText, QStringList() << QString("participant%1").arg(i, 2, 10, QChar('0'))); } mPlugin->endBatchOperation(); } QTEST_MAIN(SqliteThreadViewTest) #include "SqliteThreadViewTest.moc" history-service-0.1+14.04.20140407/plugins/sqlite/tests/CMakeLists.txt0000644000015301777760000000145112320627220025771 0ustar pbusernogroup00000000000000include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) macro(generate_tests) foreach(test ${ARGN}) add_executable(${test} ${test}.cpp) qt5_use_modules(${test} Core DBus Test Sql) target_link_libraries(${test} historyservice sqlitehistoryplugin ) add_test(${test} ${CMAKE_CURRENT_BINARY_DIR}/${test} -xunitxml -o ${CMAKE_BINARY_DIR}/test_${test}.xml) set(TEST_ENVIRONMENT "HISTORY_SQLITE_DBPATH=:memory:") set_tests_properties(${test} PROPERTIES ENVIRONMENT ${TEST_ENVIRONMENT}) endforeach(test) endmacro(generate_tests) generate_tests( SqlitePluginTest SqliteThreadViewTest SqliteEventViewTest ) history-service-0.1+14.04.20140407/plugins/sqlite/tests/SqlitePluginTest.cpp0000644000015301777760000010750312320627220027222 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "sqlitehistoryplugin.h" #include "sqlitedatabase.h" #include "sqlitehistorythreadview.h" #include "sqlitehistoryeventview.h" #include "textevent.h" #include "texteventattachment.h" #include "voiceevent.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::MatchFlags) class SqlitePluginTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testCreateThread_data(); void testCreateThread(); void testThreadForParticipants_data(); void testThreadForParticipants(); void testEmptyThreadForParticipants(); void testGetSingleThread(); void testRemoveThread(); void testBatchOperation(); void testRollback(); void testQueryThreads(); void testQueryEvents(); void testWriteTextEvent_data(); void testWriteTextEvent(); void testModifyTextEvent(); void testRemoveTextEvent(); void testWriteVoiceEvent_data(); void testWriteVoiceEvent(); void testModifyVoiceEvent(); void testRemoveVoiceEvent(); void testEventsForThread(); void testGetSingleEvent_data(); void testGetSingleEvent(); private: SQLiteHistoryPlugin *mPlugin; }; void SqlitePluginTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qputenv("HISTORY_SQLITE_DBPATH", ":memory:"); mPlugin = new SQLiteHistoryPlugin(this); } void SqlitePluginTest::testCreateThread_data() { QTest::addColumn("accountId"); QTest::addColumn("eventType"); QTest::addColumn("participants"); QTest::newRow("voice thread with one participant") << "oneAccount" << History::EventTypeVoice << (QStringList() << "oneParticipant"); QTest::newRow("text thread with multiple participants") << "oneAccount" << History::EventTypeText << (QStringList() << "first" << "second"); } void SqlitePluginTest::testCreateThread() { QFETCH(QString, accountId); QFETCH(History::EventType, eventType); QFETCH(QStringList, participants); SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants(accountId, eventType, participants); // check that the variant map is properly filled QCOMPARE(thread[History::FieldAccountId].toString(), accountId); QCOMPARE(thread[History::FieldType].toInt(), (int) eventType); QCOMPARE(thread[History::FieldParticipants].toStringList(), participants); QVERIFY(!thread[History::FieldThreadId].toString().isEmpty()); // now check that the thread is properly saved in the database QSqlQuery query(SQLiteDatabase::instance()->database()); query.prepare("SELECT * FROM threads WHERE accountId=:accountId AND type=:type"); query.bindValue(":accountId", accountId); query.bindValue(":type", (int) eventType); QVERIFY(query.exec()); int count = 0; while (query.next()) { count++; QCOMPARE(query.value("accountId"), thread[History::FieldAccountId]); QCOMPARE(query.value("threadId"), thread[History::FieldThreadId]); QCOMPARE(query.value("type"), thread[History::FieldType]); QCOMPARE(query.value("count").toInt(), 0); QCOMPARE(query.value("unreadCount").toInt(), 0); } QCOMPARE(count, 1); // and make sure all the participants are saved correctly query.prepare("SELECT * FROM thread_participants"); QVERIFY(query.exec()); count = 0; while (query.next()) { count++; QCOMPARE(query.value("accountId"), thread[History::FieldAccountId]); QCOMPARE(query.value("threadId"), thread[History::FieldThreadId]); QCOMPARE(query.value("type"), thread[History::FieldType]); QVERIFY(participants.contains(query.value("participantId").toString())); } QCOMPARE(count, participants.count()); } void SqlitePluginTest::testThreadForParticipants_data() { QTest::addColumn("accountId"); QTest::addColumn("eventType"); QTest::addColumn("participants"); QTest::addColumn("matchFlags"); QTest::addColumn("participantsToMatch"); QTest::newRow("voice thread with one participant") << "oneAccount" << History::EventTypeVoice << (QStringList() << "oneParticipant") << History::MatchFlags(History::MatchCaseSensitive) << (QStringList() << "oneParticipant"); QTest::newRow("text thread with multiple participants") << "oneAccount" << History::EventTypeText << (QStringList() << "first" << "second" << "third") << History::MatchFlags(History::MatchCaseSensitive) << (QStringList() << "second" << "first" << "third"); QTest::newRow("phone number match with one participant") << "thePhoneAccount" << History::EventTypeVoice << (QStringList() << "+1234567890") << History::MatchFlags(History::MatchPhoneNumber) << (QStringList() << "234567890"); QTest::newRow("phone number match with multiple participants") << "phoneAccount" << History::EventTypeText << (QStringList() << "1234567" << "+19999999999") << History::MatchFlags(History::MatchPhoneNumber) << (QStringList() << "+55411234567" << "99999999"); } void SqlitePluginTest::testThreadForParticipants() { QFETCH(QString, accountId); QFETCH(History::EventType, eventType); QFETCH(QStringList, participants); QFETCH(History::MatchFlags, matchFlags); QFETCH(QStringList, participantsToMatch); SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants(accountId, eventType, participants); // there is no need to check the results of createThreadForParticipants, they are tested in another function // just check if the resulting thread is not empty QVERIFY(!thread.isEmpty()); // now try to fetch the thread for the given participants QVariantMap retrievedThread = mPlugin->threadForParticipants(accountId, eventType, participantsToMatch, matchFlags); QVERIFY(!retrievedThread.isEmpty()); QCOMPARE(retrievedThread[History::FieldAccountId], thread[History::FieldAccountId]); QCOMPARE(retrievedThread[History::FieldThreadId], thread[History::FieldThreadId]); QCOMPARE(retrievedThread[History::FieldType], thread[History::FieldType]); QCOMPARE(retrievedThread[History::FieldCount], thread[History::FieldCount]); QCOMPARE(retrievedThread[History::FieldUnreadCount], thread[History::FieldUnreadCount]); QCOMPARE(retrievedThread[History::FieldParticipants], thread[History::FieldParticipants]); } void SqlitePluginTest::testEmptyThreadForParticipants() { QVariantMap thread = mPlugin->threadForParticipants("randomAccount", History::EventTypeText, QStringList()); QVERIFY(thread.isEmpty()); } void SqlitePluginTest::testGetSingleThread() { // reset the database SQLiteDatabase::instance()->reopen(); // create the thread that we will retrieve later QVariantMap thread = mPlugin->createThreadForParticipants("theAccountId", History::EventTypeText, QStringList() << "theParticipant"); QVERIFY(!thread.isEmpty()); // now create some other threads just to make sure the correct one is retrieved mPlugin->createThreadForParticipants("theAccountId", History::EventTypeText, QStringList() << "otherParticipant"); mPlugin->createThreadForParticipants("theAccountId", History::EventTypeVoice, QStringList() << "theParticipant"); mPlugin->createThreadForParticipants("otherAccount", History::EventTypeText, QStringList() << "theParticipant"); // and now retrieve the thread QVariantMap retrievedThread = mPlugin->getSingleThread(History::EventTypeText, "theAccountId", thread[History::FieldThreadId].toString()); QCOMPARE(retrievedThread[History::FieldAccountId], thread[History::FieldAccountId]); QCOMPARE(retrievedThread[History::FieldThreadId], thread[History::FieldThreadId]); QCOMPARE(retrievedThread[History::FieldType], thread[History::FieldType]); QCOMPARE(retrievedThread[History::FieldCount], thread[History::FieldCount]); QCOMPARE(retrievedThread[History::FieldUnreadCount], thread[History::FieldUnreadCount]); QCOMPARE(retrievedThread[History::FieldParticipants], thread[History::FieldParticipants]); // FIXME: check that the last event data is also present } void SqlitePluginTest::testRemoveThread() { // reset the database SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants("oneAccountId", History::EventTypeText, QStringList() << "oneParticipant"); QVERIFY(!thread.isEmpty()); QVERIFY(mPlugin->removeThread(thread)); // now check that the thread was really removed QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT count(*) FROM threads")); QVERIFY(query.next()); QCOMPARE(query.value(0).toInt(), 0); // and check that the participants were also removed QVERIFY(query.exec("SELECT count(*) FROM thread_participants")); QVERIFY(query.next()); QCOMPARE(query.value(0).toInt(), 0); } void SqlitePluginTest::testBatchOperation() { // clear the database SQLiteDatabase::instance()->reopen(); QVERIFY(mPlugin->beginBatchOperation()); mPlugin->createThreadForParticipants("accountOne", History::EventTypeText, QStringList() << "participantOne"); mPlugin->createThreadForParticipants("accountTwo", History::EventTypeText, QStringList() << "participantTwo"); mPlugin->createThreadForParticipants("accountThree", History::EventTypeText, QStringList() << "participantThree"); QVERIFY(mPlugin->endBatchOperation()); // check that the data was actually written QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT count(*) FROM threads")); QVERIFY(query.next()); QCOMPARE(query.value(0).toInt(), 3); } void SqlitePluginTest::testRollback() { // clear the database SQLiteDatabase::instance()->reopen(); QVERIFY(mPlugin->beginBatchOperation()); mPlugin->createThreadForParticipants("accountOne", History::EventTypeText, QStringList() << "participantOne"); mPlugin->createThreadForParticipants("accountTwo", History::EventTypeText, QStringList() << "participantTwo"); mPlugin->createThreadForParticipants("accountThree", History::EventTypeText, QStringList() << "participantThree"); QVERIFY(mPlugin->rollbackBatchOperation()); // check that the steps were reverted QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT count(*) FROM threads")); QVERIFY(query.next()); QCOMPARE(query.value(0).toInt(), 0); } void SqlitePluginTest::testQueryThreads() { // just make sure the returned view is of the correct type. The views are going to be tested in their own tests History::PluginThreadView *view = mPlugin->queryThreads(History::EventTypeVoice); QVERIFY(view); QVERIFY(dynamic_cast(view)); view->deleteLater(); } void SqlitePluginTest::testQueryEvents() { // just make sure the returned view is of the correct type. The views are going to be tested in their own tests History::PluginEventView *view = mPlugin->queryEvents(History::EventTypeText); QVERIFY(view); QVERIFY(dynamic_cast(view)); view->deleteLater(); } void SqlitePluginTest::testWriteTextEvent_data() { QTest::addColumn("event"); // for test purposes, the threadId == senderId to make it easier QTest::newRow("new text event with pending flag") << History::TextEvent("oneAccountId", "theSender", "oneEventId", "theSender", QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeText, History::MessageStatusPending).properties(); QTest::newRow("text event with valid read timestamp") << History::TextEvent("otherAccountId", "otherSender", "otherEventId", "otherSender", QDateTime::currentDateTime(), false, "Hi Again!", History::MessageTypeText, History::MessageStatusDelivered, QDateTime::currentDateTime()).properties(); History::TextEventAttachments attachments; attachments << History::TextEventAttachment("mmsAccountId", "mmsSender", "mmsEventId", "mmsAttachment1", "text/plain", "/the/file/path", History::AttachmentDownloaded); QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender", QDateTime::currentDateTime(), false, "Hello with attachments", History::MessageTypeMultiParty, History::MessageStatusDelivered, QDateTime::currentDateTime(), "The Subject", attachments).properties(); } void SqlitePluginTest::testWriteTextEvent() { QFETCH(QVariantMap, event); // clear the database SQLiteDatabase::instance()->reopen(); // create the thread QVariantMap thread = mPlugin->createThreadForParticipants(event[History::FieldAccountId].toString(), History::EventTypeText, QStringList() << event[History::FieldSenderId].toString()); QVERIFY(!thread.isEmpty()); // write the text event History::EventWriteResult result = mPlugin->writeTextEvent(event); QCOMPARE(result, History::EventWriteCreated); // check that the event is properly written to the database QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT * FROM text_events")); int count = 0; while(query.next()) { count++; QCOMPARE(query.value("accountId"), event[History::FieldAccountId]); QCOMPARE(query.value("threadId"), event[History::FieldThreadId]); QCOMPARE(query.value("eventId"), event[History::FieldEventId]); QCOMPARE(query.value("senderId"), event[History::FieldSenderId]); QCOMPARE(query.value("timestamp"), event[History::FieldTimestamp]); QCOMPARE(query.value("newEvent"), event[History::FieldNewEvent]); QCOMPARE(query.value("message"), event[History::FieldMessage]); QCOMPARE(query.value("messageType"), event[History::FieldMessageType]); QCOMPARE(query.value("messageStatus"), event[History::FieldMessageStatus]); QCOMPARE(query.value("readTimestamp"), event[History::FieldReadTimestamp]); QCOMPARE(query.value("subject"), event[History::FieldSubject]); } // check that only one event got written QCOMPARE(count, 1); // check that the attachments got saved, if any if (event[History::FieldMessageType].toInt() == History::MessageTypeMultiParty) { QVariantMap attachment = event[History::FieldAttachments].value >()[0]; QVERIFY(query.exec("SELECT * FROM text_event_attachments")); int count = 0; while(query.next()) { count++; QCOMPARE(query.value("accountId"), attachment[History::FieldAccountId]); QCOMPARE(query.value("threadId"), attachment[History::FieldThreadId]); QCOMPARE(query.value("eventId"), attachment[History::FieldEventId]); QCOMPARE(query.value("attachmentId"), attachment[History::FieldAttachmentId]); QCOMPARE(query.value("contentType"), attachment[History::FieldContentType]); QCOMPARE(query.value("filePath"), attachment[History::FieldFilePath]); QCOMPARE(query.value("status"), attachment[History::FieldStatus]); } QCOMPARE(count, 1); } // and check that the thread's last item got updated thread = mPlugin->getSingleThread(History::EventTypeText, thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString()); QCOMPARE(thread[History::FieldAccountId], event[History::FieldAccountId]); QCOMPARE(thread[History::FieldThreadId], event[History::FieldThreadId]); QCOMPARE(thread[History::FieldEventId], event[History::FieldEventId]); QCOMPARE(thread[History::FieldSenderId], event[History::FieldSenderId]); QCOMPARE(thread[History::FieldTimestamp], event[History::FieldTimestamp]); QCOMPARE(thread[History::FieldNewEvent], event[History::FieldNewEvent]); QCOMPARE(thread[History::FieldMessage], event[History::FieldMessage]); QCOMPARE(thread[History::FieldMessageType], event[History::FieldMessageType]); QCOMPARE(thread[History::FieldMessageStatus], event[History::FieldMessageStatus]); QCOMPARE(thread[History::FieldReadTimestamp], event[History::FieldReadTimestamp]); } void SqlitePluginTest::testModifyTextEvent() { // clear the database SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants("theAccountId", History::EventTypeText, QStringList() << "theParticipant"); QVERIFY(!thread.isEmpty()); History::TextEventAttachment attachment(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), thread[History::FieldEventId].toString(), "theAttachmentId", "text/plain", "/file/path"); History::TextEvent textEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theEventId", "theParticipant", QDateTime::currentDateTime(), true, "Hi there!", History::MessageTypeMultiParty, History::MessageStatusPending, QDateTime::currentDateTime(), "theSubject", History::TextEventAttachments() << attachment); QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated); // now modify the fields that can be modified in the class textEvent.setNewEvent(false); textEvent.setMessageStatus(History::MessageStatusDelivered); textEvent.setReadTimestamp(QDateTime::currentDateTime()); QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteModified); // and check that the data is actually up-to-date in the database QVariantMap event = textEvent.properties(); QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT * FROM text_events")); int count = 0; while(query.next()) { count++; QCOMPARE(query.value("accountId"), event[History::FieldAccountId]); QCOMPARE(query.value("threadId"), event[History::FieldThreadId]); QCOMPARE(query.value("eventId"), event[History::FieldEventId]); QCOMPARE(query.value("senderId"), event[History::FieldSenderId]); QCOMPARE(query.value("timestamp"), event[History::FieldTimestamp]); QCOMPARE(query.value("newEvent"), event[History::FieldNewEvent]); QCOMPARE(query.value("message"), event[History::FieldMessage]); QCOMPARE(query.value("messageType"), event[History::FieldMessageType]); QCOMPARE(query.value("messageStatus"), event[History::FieldMessageStatus]); QCOMPARE(query.value("readTimestamp"), event[History::FieldReadTimestamp]); QCOMPARE(query.value("subject"), event[History::FieldSubject]); } // check that only one event got written QCOMPARE(count, 1); } void SqlitePluginTest::testRemoveTextEvent() { // clear the database SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants("theAccountId", History::EventTypeText, QStringList() << "theParticipant"); QVERIFY(!thread.isEmpty()); History::TextEvent textEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theSenderId", "theEventId", QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeText, History::MessageStatusPending); QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated); // now remove the item and check that it is really removed from the database QVERIFY(mPlugin->removeTextEvent(textEvent.properties())); // check that the event was removed from the database QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT count(*) FROM text_events")); QVERIFY(query.next()); QCOMPARE(query.value(0).toInt(), 0); } void SqlitePluginTest::testWriteVoiceEvent_data() { QTest::addColumn("event"); // for test purposes, the threadId == senderId to make it easier QTest::newRow("missed call") << History::VoiceEvent("theAccountId", "theSenderId", "theEventId", "theSenderId", QDateTime::currentDateTime(), true, true).properties(); QTest::newRow("incoming call") << History::VoiceEvent("otherAccountId", "otherSenderId", "otherEventId", "otherSenderId", QDateTime::currentDateTime(), false, false, QTime(0,10,30)).properties(); } void SqlitePluginTest::testWriteVoiceEvent() { QFETCH(QVariantMap, event); // clear the database SQLiteDatabase::instance()->reopen(); // create the thread QVariantMap thread = mPlugin->createThreadForParticipants(event[History::FieldAccountId].toString(), History::EventTypeVoice, QStringList() << event[History::FieldSenderId].toString()); QVERIFY(!thread.isEmpty()); // write the voice event History::EventWriteResult result = mPlugin->writeVoiceEvent(event); QCOMPARE(result, History::EventWriteCreated); // check that the event is properly written to the database QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT * FROM voice_events")); int count = 0; while(query.next()) { count++; QCOMPARE(query.value("accountId"), event[History::FieldAccountId]); QCOMPARE(query.value("threadId"), event[History::FieldThreadId]); QCOMPARE(query.value("eventId"), event[History::FieldEventId]); QCOMPARE(query.value("senderId"), event[History::FieldSenderId]); QCOMPARE(query.value("timestamp"), event[History::FieldTimestamp]); QCOMPARE(query.value("newEvent"), event[History::FieldNewEvent]); QCOMPARE(query.value("missed"), event[History::FieldMissed]); QCOMPARE(query.value("duration"), event[History::FieldDuration]); } // check that only one event got written QCOMPARE(count, 1); // and check that the thread's last item got updated thread = mPlugin->getSingleThread(History::EventTypeVoice, thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString()); QCOMPARE(thread[History::FieldAccountId], event[History::FieldAccountId]); QCOMPARE(thread[History::FieldThreadId], event[History::FieldThreadId]); QCOMPARE(thread[History::FieldEventId], event[History::FieldEventId]); QCOMPARE(thread[History::FieldSenderId], event[History::FieldSenderId]); QCOMPARE(thread[History::FieldTimestamp], event[History::FieldTimestamp]); QCOMPARE(thread[History::FieldNewEvent], event[History::FieldNewEvent]); QCOMPARE(thread[History::FieldMissed], event[History::FieldMissed]); QCOMPARE(thread[History::FieldDuration], event[History::FieldDuration]); } void SqlitePluginTest::testModifyVoiceEvent() { // clear the database SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants("theAccountId", History::EventTypeVoice, QStringList() << "theParticipant"); QVERIFY(!thread.isEmpty()); History::VoiceEvent voiceEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theEventId", "theParticipant", QDateTime::currentDateTime(), true, true, QTime(0, 1, 2)); QCOMPARE(mPlugin->writeVoiceEvent(voiceEvent.properties()), History::EventWriteCreated); // now modify the fields that can be modified in the class voiceEvent.setNewEvent(false); QCOMPARE(mPlugin->writeVoiceEvent(voiceEvent.properties()), History::EventWriteModified); // and check that the data is actually up-to-date in the database QVariantMap event = voiceEvent.properties(); QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT * FROM voice_events")); int count = 0; while(query.next()) { count++; QCOMPARE(query.value("accountId"), event[History::FieldAccountId]); QCOMPARE(query.value("threadId"), event[History::FieldThreadId]); QCOMPARE(query.value("eventId"), event[History::FieldEventId]); QCOMPARE(query.value("senderId"), event[History::FieldSenderId]); QCOMPARE(query.value("timestamp"), event[History::FieldTimestamp]); QCOMPARE(query.value("newEvent"), event[History::FieldNewEvent]); QCOMPARE(query.value("missed"), event[History::FieldMissed]); QCOMPARE(query.value("duration"), event[History::FieldDuration]); } // check that only one event got written QCOMPARE(count, 1); } void SqlitePluginTest::testRemoveVoiceEvent() { // clear the database SQLiteDatabase::instance()->reopen(); QVariantMap thread = mPlugin->createThreadForParticipants("theAccountId", History::EventTypeVoice, QStringList() << "theParticipant"); QVERIFY(!thread.isEmpty()); History::VoiceEvent voiceEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theSenderId", "theEventId", QDateTime::currentDateTime(), true, true, QTime(0, 5, 10)); QCOMPARE(mPlugin->writeVoiceEvent(voiceEvent.properties()), History::EventWriteCreated); // now remove the item and check that it is really removed from the database QVERIFY(mPlugin->removeVoiceEvent(voiceEvent.properties())); // check that the event was removed from the database QSqlQuery query(SQLiteDatabase::instance()->database()); QVERIFY(query.exec("SELECT count(*) FROM voice_events")); QVERIFY(query.next()); QCOMPARE(query.value(0).toInt(), 0); } void SqlitePluginTest::testEventsForThread() { // clear the database SQLiteDatabase::instance()->reopen(); // test text events QVariantMap textThread = mPlugin->createThreadForParticipants("textAccountId", History::EventTypeText, QStringList() << "textParticipant"); QVERIFY(!textThread.isEmpty()); // now insert 50 events for (int i = 0; i < 50; ++i) { History::TextEventAttachment attachment(textThread[History::FieldAccountId].toString(), textThread[History::FieldThreadId].toString(), QString("textEventId%1").arg(QString::number(i)), QString("attachment%1").arg(i), "text/plain", "/some/file/path"); History::TextEvent textEvent(textThread[History::FieldAccountId].toString(), textThread[History::FieldThreadId].toString(), QString("textEventId%1").arg(QString::number(i)), "textParticipant", QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeMultiParty, History::MessageStatusPending, QDateTime::currentDateTime(), "theSubject", History::TextEventAttachments() << attachment); QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated); } QList textEvents = mPlugin->eventsForThread(textThread); QCOMPARE(textEvents.count(), 50); Q_FOREACH(const QVariantMap &textEvent, textEvents) { QCOMPARE(textEvent[History::FieldAccountId], textThread[History::FieldAccountId]); QCOMPARE(textEvent[History::FieldThreadId], textThread[History::FieldThreadId]); QCOMPARE(textEvent[History::FieldType], textThread[History::FieldType]); } // test voice events QVariantMap voiceThread = mPlugin->createThreadForParticipants("voiceAccountId", History::EventTypeVoice, QStringList() << "voiceParticipant"); QVERIFY(!voiceThread.isEmpty()); // now insert 50 events for (int i = 0; i < 50; ++i) { History::VoiceEvent voiceEvent(voiceThread[History::FieldAccountId].toString(), voiceThread[History::FieldThreadId].toString(), QString("voiceEventId%1").arg(QString::number(i)), "voiceParticipant", QDateTime::currentDateTime(), true, false, QTime(0, i, i)); QCOMPARE(mPlugin->writeVoiceEvent(voiceEvent.properties()), History::EventWriteCreated); } QList voiceEvents = mPlugin->eventsForThread(voiceThread); QCOMPARE(voiceEvents.count(), 50); Q_FOREACH(const QVariantMap &voiceEvent, voiceEvents) { QCOMPARE(voiceEvent[History::FieldAccountId], voiceThread[History::FieldAccountId]); QCOMPARE(voiceEvent[History::FieldThreadId], voiceThread[History::FieldThreadId]); QCOMPARE(voiceEvent[History::FieldType], voiceThread[History::FieldType]); } } void SqlitePluginTest::testGetSingleEvent_data() { QTest::addColumn("event"); // for test purposes, the threadId == senderId to make it easier QTest::newRow("new text event with pending flag") << History::TextEvent("oneAccountId", "theSender", "oneEventId", "theSender", QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeText, History::MessageStatusPending).properties(); QTest::newRow("text event with valid read timestamp") << History::TextEvent("otherAccountId", "otherSender", "otherEventId", "otherSender", QDateTime::currentDateTime(), false, "Hi Again!", History::MessageTypeText, History::MessageStatusDelivered, QDateTime::currentDateTime()).properties(); History::TextEventAttachments attachments; attachments << History::TextEventAttachment("mmsAccountId", "mmsSender", "mmsEventId", "mmsAttachment1", "text/plain", "/the/file/path", History::AttachmentDownloaded); QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender", QDateTime::currentDateTime(), false, "Hello with attachments", History::MessageTypeMultiParty, History::MessageStatusDelivered, QDateTime::currentDateTime(), "The Subject", attachments).properties(); QTest::newRow("missed call") << History::VoiceEvent("theAccountId", "theSenderId", "theEventId", "theSenderId", QDateTime::currentDateTime(), true, true).properties(); QTest::newRow("incoming call") << History::VoiceEvent("otherAccountId", "otherSenderId", "otherEventId", "otherSenderId", QDateTime::currentDateTime(), false, false, QTime(0,10,30)).properties(); } void SqlitePluginTest::testGetSingleEvent() { QFETCH(QVariantMap, event); // clear the database SQLiteDatabase::instance()->reopen(); History::EventType type = (History::EventType) event[History::FieldType].toInt(); // create the thread QVariantMap thread = mPlugin->createThreadForParticipants(event[History::FieldAccountId].toString(), type, QStringList() << event[History::FieldSenderId].toString()); QVERIFY(!thread.isEmpty()); // write the event switch (type) { case History::EventTypeText: QCOMPARE(mPlugin->writeTextEvent(event), History::EventWriteCreated); break; case History::EventTypeVoice: QCOMPARE(mPlugin->writeVoiceEvent(event), History::EventWriteCreated); break; } QVariantMap retrievedEvent = mPlugin->getSingleEvent(type, event[History::FieldAccountId].toString(), event[History::FieldThreadId].toString(), event[History::FieldEventId].toString()); QCOMPARE(retrievedEvent[History::FieldAccountId], event[History::FieldAccountId]); QCOMPARE(retrievedEvent[History::FieldThreadId], event[History::FieldThreadId]); QCOMPARE(retrievedEvent[History::FieldEventId], event[History::FieldEventId]); QCOMPARE(retrievedEvent[History::FieldSenderId], event[History::FieldSenderId]); QCOMPARE(retrievedEvent[History::FieldTimestamp], event[History::FieldTimestamp]); QCOMPARE(retrievedEvent[History::FieldNewEvent], event[History::FieldNewEvent]); switch (type) { case History::EventTypeText: QCOMPARE(retrievedEvent[History::FieldMessage], event[History::FieldMessage]); QCOMPARE(retrievedEvent[History::FieldMessageType], event[History::FieldMessageType]); QCOMPARE(retrievedEvent[History::FieldMessageStatus], event[History::FieldMessageStatus]); QCOMPARE(retrievedEvent[History::FieldReadTimestamp], event[History::FieldReadTimestamp]); break; case History::EventTypeVoice: QCOMPARE(retrievedEvent[History::FieldMissed], event[History::FieldMissed]); QCOMPARE(retrievedEvent[History::FieldDuration], event[History::FieldDuration]); break; } } QTEST_MAIN(SqlitePluginTest) #include "SqlitePluginTest.moc" history-service-0.1+14.04.20140407/plugins/sqlite/schema/0000755000015301777760000000000012320627745023342 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/plugins/sqlite/schema/v2.sql0000644000015301777760000000427212320627220024403 0ustar pbusernogroup00000000000000DROP TRIGGER voice_events_delete_trigger; CREATE TRIGGER voice_events_delete_trigger AFTER DELETE ON voice_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM voice_events WHERE accountId=old.accountId AND threadId=old.threadId) WHERE accountId=old.accountId AND threadId=old.threadId AND type=1; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM voice_events WHERE accountId=old.accountId AND threadId=old.threadId AND newEvent='true') WHERE accountId=old.accountId AND threadId=old.threadId AND type=1; UPDATE threads SET lastEventId=(SELECT eventId FROM voice_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=1; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM voice_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=1; END; DROP TRIGGER text_events_delete_trigger; CREATE TRIGGER text_events_delete_trigger AFTER DELETE ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId AND newEvent='true') WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; END; history-service-0.1+14.04.20140407/plugins/sqlite/schema/v6.sql0000644000015301777760000001030212320627220024376 0ustar pbusernogroup00000000000000DROP TRIGGER text_events_insert_trigger; DROP TRIGGER text_events_update_trigger; DROP TRIGGER text_events_delete_trigger; ALTER TABLE text_events RENAME TO text_events_old; CREATE TABLE text_events ( accountId varchar(255), threadId varchar(255), eventId varchar(255), senderId varchar(255), timestamp datetime, newEvent bool, message varchar(512), messageType tinyint, messageStatus tinyint, readTimestamp datetime, subject varchar(256) ); INSERT INTO text_events ( accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject) SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageFlags, readTimestamp, subject FROM text_events_old; DROP TABLE text_events_old; CREATE TRIGGER text_events_insert_trigger AFTER INSERT ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; END; CREATE TRIGGER text_events_update_trigger AFTER UPDATE ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; END; CREATE TRIGGER text_events_delete_trigger AFTER DELETE ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId AND newEvent='true') WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; DELETE from text_event_attachments WHERE accountId=old.accountId AND threadId=old.threadId AND eventId=old.eventId; END; history-service-0.1+14.04.20140407/plugins/sqlite/schema/v1.sql0000644000015301777760000001626512320627220024407 0ustar pbusernogroup00000000000000CREATE TABLE schema_version ( version int ); CREATE TABLE threads ( accountId varchar(255), threadId varchar(255), type tinyint, lastEventId varchar(255), lastEventTimestamp datetime, count int, unreadCount int ); CREATE TABLE thread_participants ( accountId varchar(255), threadId varchar(255), type tinyint, participantId varchar(255) ); CREATE TABLE voice_events ( accountId varchar(255), threadId varchar(255), eventId varchar(255), senderId varchar(255), timestamp datetime, newEvent bool, duration int, missed bool ); CREATE TABLE text_events ( accountId varchar(255), threadId varchar(255), eventId varchar(255), senderId varchar(255), timestamp datetime, newEvent bool, message varchar(512), messageType tinyint, messageFlags tinyint, readTimestamp datetime ); CREATE TRIGGER voice_events_insert_trigger AFTER INSERT ON voice_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET lastEventId=(SELECT eventId FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; END; CREATE TRIGGER voice_events_update_trigger AFTER UPDATE ON voice_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET lastEventId=(SELECT eventId FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; END; CREATE TRIGGER voice_events_delete_trigger AFTER DELETE ON voice_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET lastEventId=(SELECT eventId FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM voice_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=1; END; CREATE TRIGGER text_events_insert_trigger AFTER INSERT ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; END; CREATE TRIGGER text_events_update_trigger AFTER UPDATE ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; END; CREATE TRIGGER text_events_delete_trigger AFTER DELETE ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId AND newEvent='true') WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=new.accountId AND threadId=new.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; END; history-service-0.1+14.04.20140407/plugins/sqlite/schema/CMakeLists.txt0000644000015301777760000000077712320627220026101 0ustar pbusernogroup00000000000000file(GLOB SCHEMA_FILES ${CMAKE_CURRENT_SOURCE_DIR}/v*.sql) set(SCHEMA_FILE ${CMAKE_CURRENT_SOURCE_DIR}/schema.sql) set(VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/version.info) add_custom_command( OUTPUT ${SCHEMA_FILE} ${VERSION_FILE} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/update_schema.sh ${CMAKE_CURRENT_SOURCE_DIR} ${SCHEMA_FILE} ${VERSION_FILE} WORKING DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${SCHEMA_FILES} ) add_custom_target(schema_update DEPENDS ${SCHEMA_FILE} ${VERSION_FILE}) history-service-0.1+14.04.20140407/plugins/sqlite/schema/v5.sql0000644000015301777760000000020312320627220024374 0ustar pbusernogroup00000000000000DELETE FROM thread_participants WHERE (SELECT count(threadId) FROM threads WHERE threads.threadId=thread_participants.threadId)=0; history-service-0.1+14.04.20140407/plugins/sqlite/schema/v3.sql0000644000015301777760000000302712320627220024401 0ustar pbusernogroup00000000000000ALTER TABLE text_events ADD COLUMN subject varchar(256); CREATE TABLE IF NOT EXISTS text_event_attachments ( accountId varchar(255), threadId varchar(255), eventId varchar(255), attachmentId varchar(255), contentType varchar(255), filePath varchar(255), status tinyint ); DROP TRIGGER text_events_delete_trigger; CREATE TRIGGER text_events_delete_trigger AFTER DELETE ON text_events FOR EACH ROW BEGIN UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId AND newEvent='true') WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE accountId=old.accountId AND threadId=old.threadId ORDER BY timestamp DESC LIMIT 1) WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; DELETE from text_event_attachments WHERE accountId=old.accountId AND threadId=old.threadId AND eventId=old.eventId; END; history-service-0.1+14.04.20140407/plugins/sqlite/schema/v7.sql0000644000015301777760000000007612320627220024406 0ustar pbusernogroup00000000000000UPDATE text_events SET messageStatus=4 WHERE messageStatus=0; history-service-0.1+14.04.20140407/plugins/sqlite/schema/v4.sql0000644000015301777760000000033612320627220024402 0ustar pbusernogroup00000000000000CREATE TRIGGER threads_delete_trigger AFTER DELETE ON threads FOR EACH ROW BEGIN DELETE FROM thread_participants WHERE accountId=old.accountId AND threadId=old.threadId AND type=old.type; END; history-service-0.1+14.04.20140407/plugins/sqlite/schema/update_schema.sh0000755000015301777760000000100212320627220026460 0ustar pbusernogroup00000000000000#!/bin/sh if [ $# -lt 3 ]; then echo "Usage: $0 " fi SOURCE_DIR=$1 TARGET_FILE=$2 VERSION_FILE=$3 VERSION="1" LATEST_VERSION="1" TMPFILE=`tempfile` SCHEMA_FILE="$SOURCE_DIR/v${VERSION}.sql" while [ -e $SCHEMA_FILE ]; do cat $SCHEMA_FILE >> $TMPFILE LATEST_VERSION=$VERSION VERSION=$(($VERSION+1)) SCHEMA_FILE="$SOURCE_DIR/v${VERSION}.sql" done sqlite3 -init $TMPFILE :memory: .schema > $TARGET_FILE echo $LATEST_VERSION > $VERSION_FILE history-service-0.1+14.04.20140407/plugins/sqlite/sqlitehistoryeventview.cpp0000644000015301777760000000621312320627220027454 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "sqlitehistoryeventview.h" #include "sqlitedatabase.h" #include "sqlitehistoryplugin.h" #include "sort.h" #include #include #include SQLiteHistoryEventView::SQLiteHistoryEventView(SQLiteHistoryPlugin *plugin, History::EventType type, const History::Sort &sort, const History::Filter &filter) : History::PluginEventView(), mType(type), mSort(sort), mFilter(filter), mQuery(SQLiteDatabase::instance()->database()), mPageSize(15), mPlugin(plugin), mOffset(0), mValid(true) { mTemporaryTable = QString("eventview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz")); mQuery.setForwardOnly(true); // FIXME: validate the filter QString condition = filter.toString(); QString order; if (!sort.sortField().isNull()) { order = QString("ORDER BY %1 %2").arg(sort.sortField(), sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); // FIXME: check case sensitiviy } QString queryText = QString("CREATE TEMP TABLE %1 AS ").arg(mTemporaryTable); queryText += mPlugin->sqlQueryForEvents(type, condition, order); if (!mQuery.exec(queryText)) { mValid = false; Q_EMIT Invalidated(); qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); return; } } SQLiteHistoryEventView::~SQLiteHistoryEventView() { if (!mQuery.exec(QString("DROP TABLE IF EXISTS %1").arg(mTemporaryTable))) { qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); return; } } QList SQLiteHistoryEventView::NextPage() { QList events; // now prepare for selecting from it mQuery.prepare(QString("SELECT * FROM %1 LIMIT %2 OFFSET %3").arg(mTemporaryTable, QString::number(mPageSize), QString::number(mOffset))); if (!mQuery.exec()) { mValid = false; Q_EMIT Invalidated(); qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); return events; } events = mPlugin->parseEventResults(mType, mQuery); mOffset += mPageSize; mQuery.clear(); return events; } bool SQLiteHistoryEventView::IsValid() const { return mQuery.isActive(); } history-service-0.1+14.04.20140407/plugins/sqlite/sqlitehistoryeventview.h0000644000015301777760000000314612320627220027123 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef SQLITEHISTORYEVENTVIEW_H #define SQLITEHISTORYEVENTVIEW_H #include "plugineventview.h" #include "filter.h" #include "types.h" #include "sort.h" #include class SQLiteHistoryPlugin; class SQLiteHistoryEventView : public History::PluginEventView { Q_OBJECT public: SQLiteHistoryEventView(SQLiteHistoryPlugin *plugin, History::EventType type, const History::Sort &sort, const History::Filter &filter); ~SQLiteHistoryEventView(); QList NextPage(); bool IsValid() const; protected: private: History::EventType mType; History::Sort mSort; History::Filter mFilter; QSqlQuery mQuery; int mPageSize; SQLiteHistoryPlugin *mPlugin; QString mTemporaryTable; int mOffset; bool mValid; }; #endif // SQLITEHISTORYEVENTVIEW_H history-service-0.1+14.04.20140407/plugins/sqlite/sqlitehistoryplugin.h0000644000015301777760000000661512320627220026411 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef SQLITEHISTORYPLUGIN_H #define SQLITEHISTORYPLUGIN_H #include "plugin.h" #include #include class SQLiteHistoryReader; class SQLiteHistoryWriter; typedef QSharedPointer SQLiteHistoryReaderPtr; typedef QSharedPointer SQLiteHistoryWriterPtr; class SQLiteHistoryPlugin : public QObject, History::Plugin { Q_OBJECT Q_PLUGIN_METADATA(IID "com.canonical.historyservice.Plugin") Q_INTERFACES(History::Plugin) public: explicit SQLiteHistoryPlugin(QObject *parent = 0); // Reader part of the plugin History::PluginThreadView* queryThreads(History::EventType type, const History::Sort &sort = History::Sort(), const History::Filter &filter = History::Filter()); History::PluginEventView* queryEvents(History::EventType type, const History::Sort &sort = History::Sort(), const History::Filter &filter = History::Filter()); QVariantMap threadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants, History::MatchFlags matchFlags = History::MatchCaseSensitive); QList eventsForThread(const QVariantMap &thread); QVariantMap getSingleThread(History::EventType type, const QString &accountId, const QString &threadId); QVariantMap getSingleEvent(History::EventType type, const QString &accountId, const QString &threadId, const QString &eventId); // Writer part of the plugin QVariantMap createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants); bool removeThread(const QVariantMap &thread); History::EventWriteResult writeTextEvent(const QVariantMap &event); bool removeTextEvent(const QVariantMap &event); History::EventWriteResult writeVoiceEvent(const QVariantMap &event); bool removeVoiceEvent(const QVariantMap &event); bool beginBatchOperation(); bool endBatchOperation(); bool rollbackBatchOperation(); // functions to be used internally QString sqlQueryForThreads(History::EventType type, const QString &condition, const QString &order); QList parseThreadResults(History::EventType type, QSqlQuery &query); QString sqlQueryForEvents(History::EventType type, const QString &condition, const QString &order); QList parseEventResults(History::EventType type, QSqlQuery &query); }; #endif // SQLITEHISTORYPLUGIN_H history-service-0.1+14.04.20140407/plugins/sqlite/update_qrc.sh0000755000015301777760000000057312320627220024561 0ustar pbusernogroup00000000000000#!/bin/sh QRC_FILE=$1 FILES=`ls schema/*.sql schema/*.info` # clear the file if [ -e $QRC_FILE ]; then rm -f $QRC_FILE fi # and print the contents echo '' >> $QRC_FILE echo ' ' >> $QRC_FILE for file in $FILES; do echo " $file" >> $QRC_FILE done echo ' ' >> $QRC_FILE echo '' >> $QRC_FILE history-service-0.1+14.04.20140407/plugins/sqlite/CMakeLists.txt0000644000015301777760000000246312320627220024633 0ustar pbusernogroup00000000000000# SQLite Plugin set(plugin_SRCS sqlitedatabase.cpp sqlitehistoryeventview.cpp sqlitehistorythreadview.cpp sqlitehistoryplugin.cpp ) set (plugin_HDRS sqlitedatabase.h sqlitehistoryeventview.h sqlitehistorythreadview.h sqlitehistoryplugin.h ) include_directories( ${CMAKE_SOURCE_DIR}/src ${SQLITE3_INCLUDE_DIRS} ) qt5_add_resources(plugin_RES sqlitehistoryplugin.qrc) add_library(sqlitehistoryplugin SHARED ${plugin_SRCS} ${plugin_HDRS} ${plugin_RES}) qt5_use_modules(sqlitehistoryplugin Core DBus Sql) # update the .qrc file automatically when there are new schema files file(GLOB QRC_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/schema/*.sql ${CMAKE_CURRENT_SOURCE_DIR}/schema/*.info) set(QRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/sqlitehistoryplugin.qrc) add_custom_command( OUTPUT ${QRC_FILE} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/update_qrc.sh ${QRC_FILE} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${QRC_RESOURCE_FILES} ) add_custom_target(qrc_update DEPENDS ${QRC_FILE} schema_update) add_dependencies(sqlitehistoryplugin schema_update qrc_update) target_link_libraries(sqlitehistoryplugin historyservice ${SQLITE3_LIBRARIES}) install(TARGETS sqlitehistoryplugin DESTINATION ${HISTORY_PLUGIN_PATH}) add_subdirectory(schema) add_subdirectory(tests) history-service-0.1+14.04.20140407/plugins/sqlite/sqlitedatabase.h0000644000015301777760000000263312320627220025231 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef SQLITEDATABASE_H #define SQLITEDATABASE_H #include #include class SQLiteDatabase : public QObject { Q_OBJECT public: static SQLiteDatabase *instance(); bool initializeDatabase(); QSqlDatabase database() const; bool beginTransation(); bool finishTransaction(); bool rollbackTransaction(); bool reopen(); protected: bool createOrUpdateDatabase(); QStringList parseSchemaFile(const QString &fileName); void parseVersionInfo(); private: explicit SQLiteDatabase(QObject *parent = 0); QString mDatabasePath; QSqlDatabase mDatabase; int mSchemaVersion; }; #endif // SQLITEDATABASE_H history-service-0.1+14.04.20140407/plugins/sqlite/sqlitedatabase.cpp0000644000015301777760000001551112320627220025563 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "phoneutils_p.h" #include "sqlite3.h" #include "sqlitedatabase.h" #include #include #include #include #include #include #include Q_DECLARE_OPAQUE_POINTER(sqlite3*) Q_DECLARE_METATYPE(sqlite3*) // custom sqlite function "comparePhoneNumbers" used to compare IDs if necessary void comparePhoneNumbers(sqlite3_context *context, int argc, sqlite3_value **argv) { QString arg1((const char*)sqlite3_value_text(argv[0])); QString arg2((const char*)sqlite3_value_text(argv[1])); sqlite3_result_int(context, (int)PhoneUtils::comparePhoneNumbers(arg1, arg2)); } SQLiteDatabase::SQLiteDatabase(QObject *parent) : QObject(parent), mSchemaVersion(0) { initializeDatabase(); } SQLiteDatabase *SQLiteDatabase::instance() { static SQLiteDatabase *self = new SQLiteDatabase(); return self; } bool SQLiteDatabase::initializeDatabase() { mDatabasePath = qgetenv("HISTORY_SQLITE_DBPATH"); if (mDatabasePath.isEmpty()) { mDatabasePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); QDir dir(mDatabasePath); if (!dir.exists("history-service") && !dir.mkpath("history-service")) { qCritical() << "Failed to create dir"; return false; } dir.cd("history-service"); mDatabasePath = dir.absoluteFilePath("history.sqlite"); } mDatabase = QSqlDatabase::addDatabase("QSQLITE"); mDatabase.setDatabaseName(mDatabasePath); // always run the createDatabase function at least during the development if (!createOrUpdateDatabase()) { qCritical() << "Failed to create or update the database"; return false; } return true; } QSqlDatabase SQLiteDatabase::database() const { return mDatabase; } bool SQLiteDatabase::beginTransation() { return mDatabase.transaction(); } bool SQLiteDatabase::finishTransaction() { return mDatabase.commit(); } bool SQLiteDatabase::rollbackTransaction() { return mDatabase.rollback(); } /// this method is to be used mainly by unit tests in order to clean up the database between /// tests. bool SQLiteDatabase::reopen() { mDatabase.close(); mDatabase.open(); // make sure the database is up-to-date after reopening. // this is mainly required for the memory backend used for testing createOrUpdateDatabase(); } bool SQLiteDatabase::createOrUpdateDatabase() { bool create = !QFile(mDatabasePath).exists(); if (!mDatabase.open()) { return false; } // create the comparePhoneNumbers custom sqlite function sqlite3 *handle = database().driver()->handle().value(); sqlite3_create_function(handle, "comparePhoneNumbers", 2, SQLITE_ANY, NULL, &comparePhoneNumbers, NULL, NULL); parseVersionInfo(); QSqlQuery query(mDatabase); QStringList statements; if (create) { statements = parseSchemaFile(":/database/schema/schema.sql"); } else { // if the database already exists, we don´t need to create the tables // only check if an update is needed query.exec("SELECT * FROM schema_version"); if (!query.exec() || !query.next()) { return false; } int upgradeToVersion = query.value(0).toInt() + 1; while (upgradeToVersion <= mSchemaVersion) { statements += parseSchemaFile(QString(":/database/schema/v%1.sql").arg(QString::number(upgradeToVersion))); ++upgradeToVersion; } } // if at this point needsUpdate is still false, it means the database is up-to-date if (statements.isEmpty()) { return true; } beginTransation(); Q_FOREACH(const QString &statement, statements) { if (!query.exec(statement)) { qCritical() << "Failed to create or update database. SQL Statements:" << query.lastQuery() << "Error:" << query.lastError(); rollbackTransaction(); return false; } } // now set the new database schema version if (!query.exec("DELETE FROM schema_version")) { qCritical() << "Failed to remove previous schema versions. SQL Statement:" << query.lastQuery() << "Error:" << query.lastError(); rollbackTransaction(); return false; } if (!query.exec(QString("INSERT INTO schema_version VALUES (%1)").arg(mSchemaVersion))) { qCritical() << "Failed to insert new schema version. SQL Statement:" << query.lastQuery() << "Error:" << query.lastError(); rollbackTransaction(); return false; } finishTransaction(); return true; } QStringList SQLiteDatabase::parseSchemaFile(const QString &fileName) { QFile schema(fileName); if (!schema.open(QFile::ReadOnly)) { qCritical() << "Failed to open " << fileName; return QStringList(); } bool parsingBlock = false; QString statement; QStringList statements; // FIXME: this parser is very basic, it needs to be improved in the future // it does a lot of assumptions based on the structure of the schema.sql file QTextStream stream(&schema); while (!stream.atEnd()) { QString line = stream.readLine(); bool statementEnded = false; statement += line; // check if we are parsing a trigger command if (line.trimmed().startsWith("CREATE TRIGGER", Qt::CaseInsensitive)) { parsingBlock = true; } else if (parsingBlock) { if (line.contains("END;")) { parsingBlock = false; statementEnded = true; } } else if (statement.contains(";")) { statementEnded = true; } statement += "\n"; if (statementEnded) { statements.append(statement); statement.clear(); } } return statements; } void SQLiteDatabase::parseVersionInfo() { QFile schema(":/database/schema/version.info"); if (!schema.open(QFile::ReadOnly)) { qDebug() << schema.error(); qCritical() << "Failed to get database version"; } QString version = schema.readAll(); mSchemaVersion = version.toInt(); } history-service-0.1+14.04.20140407/plugins/sqlite/sqlitehistoryplugin.cpp0000644000015301777760000006440612320627220026746 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "sqlitehistoryplugin.h" #include "phoneutils_p.h" #include "sqlitedatabase.h" #include "sqlitehistoryeventview.h" #include "sqlitehistorythreadview.h" #include #include #include #include SQLiteHistoryPlugin::SQLiteHistoryPlugin(QObject *parent) : QObject(parent) { // just trigger the database creation or update SQLiteDatabase::instance(); } // Reader History::PluginThreadView *SQLiteHistoryPlugin::queryThreads(History::EventType type, const History::Sort &sort, const History::Filter &filter) { return new SQLiteHistoryThreadView(this, type, sort, filter); } History::PluginEventView *SQLiteHistoryPlugin::queryEvents(History::EventType type, const History::Sort &sort, const History::Filter &filter) { return new SQLiteHistoryEventView(this, type, sort, filter); } QVariantMap SQLiteHistoryPlugin::threadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants, History::MatchFlags matchFlags) { if (participants.isEmpty()) { return QVariantMap(); } QSqlQuery query(SQLiteDatabase::instance()->database()); // select all the threads the first participant is listed in, and from that list // check if any of the threads has all the other participants listed // FIXME: find a better way to do this QString queryString("SELECT threadId FROM thread_participants WHERE %1 AND type=:type AND accountId=:accountId"); // FIXME: for now we just compare differently when using MatchPhoneNumber if (matchFlags & History::MatchPhoneNumber) { queryString = queryString.arg("comparePhoneNumbers(participantId, :participantId)"); } else { queryString = queryString.arg("participantId=:participantId"); } query.prepare(queryString); query.bindValue(":participantId", participants[0]); query.bindValue(":type", type); query.bindValue(":accountId", accountId); if (!query.exec()) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return QVariantMap(); } QStringList threadIds; while (query.next()) { threadIds << query.value(0).toString(); } QString existingThread; // now for each threadId, check if all the other participants are listed Q_FOREACH(const QString &threadId, threadIds) { query.prepare("SELECT participantId FROM thread_participants WHERE " "threadId=:threadId AND type=:type AND accountId=:accountId"); query.bindValue(":threadId", threadId); query.bindValue(":type", type); query.bindValue(":accountId", accountId); if (!query.exec()) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return QVariantMap(); } QStringList threadParticipants; while (query.next()) { threadParticipants << query.value(0).toString(); } if (threadParticipants.count() != participants.count()) { continue; } // and now compare the lists bool found = true; Q_FOREACH(const QString &participant, participants) { if (matchFlags & History::MatchPhoneNumber) { // we need to iterate the list and call the phone number comparing function for // each participant from the given thread bool inList = false; Q_FOREACH(const QString &threadParticipant, threadParticipants) { if (PhoneUtils::comparePhoneNumbers(threadParticipant, participant)) { inList = true; break; } } if (!inList) { found = false; break; } } else if (!threadParticipants.contains(participant)) { found = false; break; } } if (found) { existingThread = threadId; break; } } return getSingleThread(type, accountId, existingThread); } QList SQLiteHistoryPlugin::eventsForThread(const QVariantMap &thread) { QList results; QString accountId = thread[History::FieldAccountId].toString(); QString threadId = thread[History::FieldThreadId].toString(); History::EventType type = (History::EventType) thread[History::FieldType].toInt(); QString condition = QString("accountId=\"%1\" AND threadId=\"%2\"").arg(accountId, threadId); QString queryText = sqlQueryForEvents(type, condition, ""); QSqlQuery query(SQLiteDatabase::instance()->database()); if (!query.exec(queryText)) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return results; } results = parseEventResults(type, query); return results; } QVariantMap SQLiteHistoryPlugin::getSingleThread(History::EventType type, const QString &accountId, const QString &threadId) { QVariantMap result; QString condition = QString("accountId=\"%1\" AND threadId=\"%2\"").arg(accountId, threadId); QString queryText = sqlQueryForThreads(type, condition, QString::null); queryText += " LIMIT 1"; QSqlQuery query(SQLiteDatabase::instance()->database()); if (!query.exec(queryText)) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return result; } QList results = parseThreadResults(type, query); query.clear(); if (!results.isEmpty()) { result = results.first(); } return result; } QVariantMap SQLiteHistoryPlugin::getSingleEvent(History::EventType type, const QString &accountId, const QString &threadId, const QString &eventId) { QVariantMap result; QString condition = QString("accountId=\"%1\" AND threadId=\"%2\" AND eventId=\"%3\"").arg(accountId, threadId, eventId); QString queryText = sqlQueryForEvents(type, condition, QString::null); queryText += " LIMIT 1"; QSqlQuery query(SQLiteDatabase::instance()->database()); if (!query.exec(queryText)) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return result; } QList results = parseEventResults(type, query); query.clear(); if (!results.isEmpty()) { result = results.first(); } return result; } // Writer QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants) { // WARNING: this function does NOT test to check if the thread is already created, you should check using HistoryReader::threadForParticipants() QVariantMap thread; // Create a new thread // FIXME: define what the threadId will be QString threadId = participants.join("%"); QSqlQuery query(SQLiteDatabase::instance()->database()); query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount)" "VALUES (:accountId, :threadId, :type, :count, :unreadCount)"); query.bindValue(":accountId", accountId); query.bindValue(":threadId", threadId); query.bindValue(":type", (int) type); query.bindValue(":count", 0); query.bindValue(":unreadCount", 0); if (!query.exec()) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return QVariantMap(); } // and insert the participants Q_FOREACH(const QString &participant, participants) { query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId)" "VALUES (:accountId, :threadId, :type, :participantId)"); query.bindValue(":accountId", accountId); query.bindValue(":threadId", threadId); query.bindValue(":type", type); query.bindValue(":participantId", participant); if (!query.exec()) { qCritical() << "Error:" << query.lastError() << query.lastQuery(); return QVariantMap(); } } // and finally create the thread thread[History::FieldAccountId] = accountId; thread[History::FieldThreadId] = threadId; thread[History::FieldType] = (int) type; thread[History::FieldParticipants] = participants; thread[History::FieldCount] = 0; thread[History::FieldUnreadCount] = 0; return thread; } bool SQLiteHistoryPlugin::removeThread(const QVariantMap &thread) { QSqlQuery query(SQLiteDatabase::instance()->database()); query.prepare("DELETE FROM threads WHERE accountId=:accountId AND threadId=:threadId AND type=:type"); query.bindValue(":accountId", thread[History::FieldAccountId]); query.bindValue(":threadId", thread[History::FieldThreadId]); query.bindValue(":type", thread[History::FieldType]); if (!query.exec()) { qCritical() << "Failed to remove the thread: Error:" << query.lastError() << query.lastQuery(); return false; } return true; } History::EventWriteResult SQLiteHistoryPlugin::writeTextEvent(const QVariantMap &event) { QSqlQuery query(SQLiteDatabase::instance()->database()); // check if the event exists QVariantMap existingEvent = getSingleEvent((History::EventType) event[History::FieldType].toInt(), event[History::FieldAccountId].toString(), event[History::FieldThreadId].toString(), event[History::FieldEventId].toString()); History::EventWriteResult result; if (existingEvent.isEmpty()) { // create new query.prepare("INSERT INTO text_events (accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject) " "VALUES (:accountId, :threadId, :eventId, :senderId, :timestamp, :newEvent, :message, :messageType, :messageStatus, :readTimestamp, :subject)"); result = History::EventWriteCreated; } else { // update existing event query.prepare("UPDATE text_events SET senderId=:senderId, timestamp=:timestamp, newEvent=:newEvent, message=:message, messageType=:messageType," "messageStatus=:messageStatus, readTimestamp=:readTimestamp, subject=:subject WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId"); result = History::EventWriteModified; } query.bindValue(":accountId", event[History::FieldAccountId]); query.bindValue(":threadId", event[History::FieldThreadId]); query.bindValue(":eventId", event[History::FieldEventId]); query.bindValue(":senderId", event[History::FieldSenderId]); query.bindValue(":timestamp", event[History::FieldTimestamp]); query.bindValue(":newEvent", event[History::FieldNewEvent]); query.bindValue(":message", event[History::FieldMessage]); query.bindValue(":messageType", event[History::FieldMessageType]); query.bindValue(":messageStatus", event[History::FieldMessageStatus]); query.bindValue(":readTimestamp", event[History::FieldReadTimestamp]); query.bindValue(":subject", event[History::FieldSubject].toString()); if (!query.exec()) { qCritical() << "Failed to save the text event: Error:" << query.lastError() << query.lastQuery(); return History::EventWriteError; } History::MessageType messageType = (History::MessageType) event[History::FieldMessageType].toInt(); if (messageType == History::MessageTypeMultiParty) { // if the writing is an update, we need to remove the previous attachments if (result == History::EventWriteModified) { query.prepare("DELETE FROM text_event_attachments WHERE accountId=:accountId AND threadId=:threadId " "AND eventId=:eventId"); query.bindValue(":accountId", event[History::FieldAccountId]); query.bindValue(":threadId", event[History::FieldThreadId]); query.bindValue(":eventId", event[History::FieldEventId]); if (!query.exec()) { qCritical() << "Could not erase previous attachments. Error:" << query.lastError() << query.lastQuery(); return History::EventWriteError; } } // save the attachments QList attachments = event[History::FieldAttachments].value >(); Q_FOREACH(const QVariantMap &attachment, attachments) { query.prepare("INSERT INTO text_event_attachments VALUES (:accountId, :threadId, :eventId, :attachmentId, :contentType, :filePath, :status)"); query.bindValue(":accountId", attachment[History::FieldAccountId]); query.bindValue(":threadId", attachment[History::FieldThreadId]); query.bindValue(":eventId", attachment[History::FieldEventId]); query.bindValue(":attachmentId", attachment[History::FieldAttachmentId]); query.bindValue(":contentType", attachment[History::FieldContentType]); query.bindValue(":filePath", attachment[History::FieldFilePath]); query.bindValue(":status", attachment[History::FieldStatus]); if (!query.exec()) { qCritical() << "Failed to save attachment to database" << query.lastError() << attachment; return History::EventWriteError; } } } return result; } bool SQLiteHistoryPlugin::removeTextEvent(const QVariantMap &event) { QSqlQuery query(SQLiteDatabase::instance()->database()); query.prepare("DELETE FROM text_events WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId"); query.bindValue(":accountId", event[History::FieldAccountId]); query.bindValue(":threadId", event[History::FieldThreadId]); query.bindValue(":eventId", event[History::FieldEventId]); if (!query.exec()) { qCritical() << "Failed to save the voice event: Error:" << query.lastError() << query.lastQuery(); return false; } return true; } History::EventWriteResult SQLiteHistoryPlugin::writeVoiceEvent(const QVariantMap &event) { QSqlQuery query(SQLiteDatabase::instance()->database()); // check if the event exists QVariantMap existingEvent = getSingleEvent((History::EventType) event[History::FieldType].toInt(), event[History::FieldAccountId].toString(), event[History::FieldThreadId].toString(), event[History::FieldEventId].toString()); History::EventWriteResult result; if (existingEvent.isEmpty()) { // create new query.prepare("INSERT INTO voice_events (accountId, threadId, eventId, senderId, timestamp, newEvent, duration, missed) " "VALUES (:accountId, :threadId, :eventId, :senderId, :timestamp, :newEvent, :duration, :missed)"); result = History::EventWriteCreated; } else { // update existing event query.prepare("UPDATE voice_events SET senderId=:senderId, timestamp=:timestamp, newEvent=:newEvent, duration=:duration, " "missed=:missed WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId"); result = History::EventWriteModified; } query.bindValue(":accountId", event[History::FieldAccountId]); query.bindValue(":threadId", event[History::FieldThreadId]); query.bindValue(":eventId", event[History::FieldEventId]); query.bindValue(":senderId", event[History::FieldSenderId]); query.bindValue(":timestamp", event[History::FieldTimestamp]); query.bindValue(":newEvent", event[History::FieldNewEvent]); query.bindValue(":duration", event[History::FieldDuration]); query.bindValue(":missed", event[History::FieldMissed]); if (!query.exec()) { qCritical() << "Failed to save the voice event: Error:" << query.lastError() << query.lastQuery(); result = History::EventWriteError; } return result; } bool SQLiteHistoryPlugin::removeVoiceEvent(const QVariantMap &event) { QSqlQuery query(SQLiteDatabase::instance()->database()); query.prepare("DELETE FROM voice_events WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId"); query.bindValue(":accountId", event[History::FieldAccountId]); query.bindValue(":threadId", event[History::FieldThreadId]); query.bindValue(":eventId", event[History::FieldEventId]); if (!query.exec()) { qCritical() << "Failed to remove the voice event: Error:" << query.lastError() << query.lastQuery(); return false; } return true; } bool SQLiteHistoryPlugin::beginBatchOperation() { return SQLiteDatabase::instance()->beginTransation(); } bool SQLiteHistoryPlugin::endBatchOperation() { return SQLiteDatabase::instance()->finishTransaction(); } bool SQLiteHistoryPlugin::rollbackBatchOperation() { return SQLiteDatabase::instance()->rollbackTransaction(); } QString SQLiteHistoryPlugin::sqlQueryForThreads(History::EventType type, const QString &condition, const QString &order) { QString modifiedCondition = condition; if (!modifiedCondition.isEmpty()) { modifiedCondition.prepend(" AND "); // FIXME: the filters should be implemented in a better way modifiedCondition.replace("accountId=", "threads.accountId="); modifiedCondition.replace("threadId=", "threads.threadId="); modifiedCondition.replace("count=", "threads.count="); modifiedCondition.replace("unreadCount=", "threads.unreadCount="); } QString modifiedOrder = order; if (!modifiedOrder.isEmpty()) { modifiedOrder.replace(" accountId", " threads.accountId"); modifiedOrder.replace(" threadId", " threads.threadId"); modifiedOrder.replace(" count", " threads.count"); modifiedOrder.replace(" unreadCount", " threads.unreadCount"); } QStringList fields; fields << "threads.accountId" << "threads.threadId" << "threads.lastEventId" << "threads.count" << "threads.unreadCount"; // get the participants in the query already fields << "(SELECT group_concat(thread_participants.participantId, \"|,|\") " "FROM thread_participants WHERE thread_participants.accountId=threads.accountId " "AND thread_participants.threadId=threads.threadId " "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as participants"; QStringList extraFields; QString table; switch (type) { case History::EventTypeText: table = "text_events"; extraFields << "text_events.message" << "text_events.messageType" << "text_events.messageStatus" << "text_events.readTimestamp"; break; case History::EventTypeVoice: table = "voice_events"; extraFields << "voice_events.duration" << "voice_events.missed"; break; } fields << QString("%1.senderId").arg(table) << QString("%1.timestamp").arg(table) << QString("%1.newEvent").arg(table); fields << extraFields; QString queryText = QString("SELECT %1 FROM threads LEFT JOIN %2 ON threads.threadId=%2.threadId AND " "threads.accountId=%2.accountId AND threads.lastEventId=%2.eventId WHERE threads.type=%3 %4 %5") .arg(fields.join(", "), table, QString::number((int)type), modifiedCondition, modifiedOrder); return queryText; } QList SQLiteHistoryPlugin::parseThreadResults(History::EventType type, QSqlQuery &query) { QList threads; while (query.next()) { QVariantMap thread; thread[History::FieldType] = (int) type; thread[History::FieldAccountId] = query.value(0); thread[History::FieldThreadId] = query.value(1); thread[History::FieldEventId] = query.value(2); thread[History::FieldCount] = query.value(3); thread[History::FieldUnreadCount] = query.value(4); thread[History::FieldParticipants] = query.value(5).toString().split("|,|"); // the generic event fields thread[History::FieldSenderId] = query.value(6); thread[History::FieldTimestamp] = query.value(7); thread[History::FieldNewEvent] = query.value(8); // the next step is to get the last event switch (type) { case History::EventTypeText: thread[History::FieldMessage] = query.value(9); thread[History::FieldMessageType] = query.value(10); thread[History::FieldMessageStatus] = query.value(11); thread[History::FieldReadTimestamp] = query.value(12); break; case History::EventTypeVoice: thread[History::FieldMissed] = query.value(10); thread[History::FieldDuration] = query.value(9); break; } threads << thread; } return threads; } QString SQLiteHistoryPlugin::sqlQueryForEvents(History::EventType type, const QString &condition, const QString &order) { QString modifiedCondition = condition; if (!modifiedCondition.isEmpty()) { modifiedCondition.prepend(" WHERE "); } QString participantsField = "(SELECT group_concat(thread_participants.participantId, \"|,|\") " "FROM thread_participants WHERE thread_participants.accountId=%1.accountId " "AND thread_participants.threadId=%1.threadId " "AND thread_participants.type=%2 GROUP BY accountId,threadId,type) as participants"; QString queryText; switch (type) { case History::EventTypeText: participantsField = participantsField.arg("text_events", QString::number(type)); queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, " "message, messageType, messageStatus, readTimestamp, subject FROM text_events %2 %3").arg(participantsField, modifiedCondition, order); break; case History::EventTypeVoice: participantsField = participantsField.arg("voice_events", QString::number(type)); queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, " "duration, missed FROM voice_events %2 %3").arg(participantsField, modifiedCondition, order); break; } return queryText; } QList SQLiteHistoryPlugin::parseEventResults(History::EventType type, QSqlQuery &query) { QList events; while (query.next()) { QVariantMap event; History::MessageType messageType; QString accountId = query.value(0).toString(); QString threadId = query.value(1).toString(); QString eventId = query.value(2).toString(); event[History::FieldType] = (int) type; event[History::FieldAccountId] = accountId; event[History::FieldThreadId] = threadId; event[History::FieldEventId] = eventId; event[History::FieldSenderId] = query.value(3); event[History::FieldTimestamp] = query.value(4); event[History::FieldNewEvent] = query.value(5); event[History::FieldParticipants] = query.value(6).toString().split("|,|"); switch (type) { case History::EventTypeText: messageType = (History::MessageType) query.value(8).toInt(); if (messageType == History::MessageTypeMultiParty) { QSqlQuery attachmentsQuery(SQLiteDatabase::instance()->database()); attachmentsQuery.prepare("SELECT attachmentId, contentType, filePath, status FROM text_event_attachments " "WHERE accountId=:accountId and threadId=:threadId and eventId=:eventId"); attachmentsQuery.bindValue(":accountId", accountId); attachmentsQuery.bindValue(":threadId", threadId); attachmentsQuery.bindValue(":eventId", eventId); if (!attachmentsQuery.exec()) { qCritical() << "Error:" << attachmentsQuery.lastError() << attachmentsQuery.lastQuery(); } QList attachments; while (attachmentsQuery.next()) { QVariantMap attachment; attachment[History::FieldAccountId] = accountId; attachment[History::FieldThreadId] = threadId; attachment[History::FieldEventId] = eventId; attachment[History::FieldAttachmentId] = attachmentsQuery.value(0); attachment[History::FieldContentType] = attachmentsQuery.value(1); attachment[History::FieldFilePath] = attachmentsQuery.value(2); attachment[History::FieldStatus] = attachmentsQuery.value(3); attachments << attachment; } attachmentsQuery.clear(); event[History::FieldAttachments] = QVariant::fromValue(attachments); } event[History::FieldMessage] = query.value(7); event[History::FieldMessageType] = query.value(8); event[History::FieldMessageStatus] = query.value(9); event[History::FieldReadTimestamp] = query.value(10); break; case History::EventTypeVoice: event[History::FieldDuration] = query.value(7).toInt(); event[History::FieldMissed] = query.value(8); break; } events << event; } return events; } history-service-0.1+14.04.20140407/plugins/CMakeLists.txt0000644000015301777760000000003112320627220023317 0ustar pbusernogroup00000000000000add_subdirectory(sqlite) history-service-0.1+14.04.20140407/config.h.in0000644000015301777760000000141512320627220021130 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of phone-app. * * phone-app 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; version 3. * * phone-app 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 . */ #ifndef CONFIG_H #define CONFIG_H #define HISTORY_PLUGIN_PATH "@CMAKE_INSTALL_PREFIX@/@HISTORY_PLUGIN_PATH@" #endif history-service-0.1+14.04.20140407/TODO0000644000015301777760000000005712320627220017576 0ustar pbusernogroup00000000000000- Use implicit sharing for Threads and Events. history-service-0.1+14.04.20140407/CMakeLists.txt0000644000015301777760000000453112320627220021647 0ustar pbusernogroup00000000000000project(history-service) cmake_minimum_required(VERSION 2.8) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) # Standard install paths include(GNUInstallDirs) # Check for include files include(CheckIncludeFileCXX) include(CheckIncludeFile) include(EnableCoverageReport) ##################################################################### # Enable code coverage calculation with gcov/gcovr/lcov # Usage: # * Switch build type to coverage (use ccmake or cmake-gui) # * Invoke make, make test, make coverage # * Find html report in subdir coveragereport # * Find xml report feasible for jenkins in coverage.xml ##################################################################### IF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE]) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs" ) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs" ) SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -coverage" ) SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -coverage" ) ENABLE_COVERAGE_REPORT(TARGETS ${PHONE_APP}) ENDIF(CMAKE_BUILD_TYPE MATCHES [cC][oO][vV][eE][rR][aA][gG][eE]) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) find_package(Qt5Core) find_package(Qt5DBus) find_package(Qt5Qml) find_package(Qt5Quick) find_package(Qt5Test) execute_process( COMMAND qmake -query QT_INSTALL_QML OUTPUT_VARIABLE QT_INSTALL_QML OUTPUT_STRIP_TRAILING_WHITESPACE ) find_package(PkgConfig REQUIRED) pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) pkg_check_modules(SQLITE3 REQUIRED sqlite3) pkg_check_modules(TPL_QT5 TelepathyLoggerQt5) pkg_check_modules(QTGLIB QtGLib-2.0) find_program(DBUS_RUNNER dbus-test-runner) add_definitions(-DQT_NO_KEYWORDS) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) set(HISTORY_PLUGIN_PATH ${CMAKE_INSTALL_LIBDIR}/history-service/plugins) configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) enable_testing() # Define the version to be used in the library set(HISTORY_VERSION_MAJOR 0) set(HISTORY_VERSION_MINOR 0) set(HISTORY_VERSION_PATCH 0) set(PACKAGE_VERSION ${HISTORY_VERSION_MAJOR}.${HISTORY_VERSION_MINOR}.${HISTORY_VERSION_PATCH}) add_subdirectory(src) add_subdirectory(daemon) add_subdirectory(plugins) add_subdirectory(tools) add_subdirectory(Ubuntu) history-service-0.1+14.04.20140407/COPYING0000644000015301777760000010451312320627220020143 0ustar pbusernogroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . history-service-0.1+14.04.20140407/src/0000755000015301777760000000000012320627745017707 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/src/PluginEventView0000644000015301777760000000015712320627220022714 0ustar pbusernogroup00000000000000#ifndef PLUGINEVENTVIEW_H #define PLUGINEVENTVIEW_H #include "plugineventview.h" #endif // PLUGINEVENTVIEW_H history-service-0.1+14.04.20140407/src/eventview_p.h0000644000015301777760000000315612320627220022404 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef EVENTVIEW_P_H #define EVENTVIEW_P_H #include "types.h" #include "filter.h" #include "sort.h" #include namespace History { class EventView; class EventViewPrivate { Q_DECLARE_PUBLIC(EventView) public: EventViewPrivate(History::EventType theType, const History::Sort &theSort, const History::Filter &theFilter); EventType type; Sort sort; Filter filter; QString objectPath; bool valid; QDBusInterface *dbus; Events filteredEvents(const Events &events); // private slots void _d_eventsAdded(const History::Events &events); void _d_eventsModified(const History::Events &events); void _d_eventsRemoved(const History::Events &events); EventView *q_ptr; }; } #endif // EVENTVIEW_P_H history-service-0.1+14.04.20140407/src/plugin.h0000644000015301777760000000613712320627220021351 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_PLUGIN_H #define HISTORY_PLUGIN_H #include #include "filter.h" #include "types.h" #include "sort.h" #include namespace History { class PluginThreadView; class PluginEventView; class Plugin { public: virtual ~Plugin() {} // Reader part of the plugin virtual PluginThreadView* queryThreads(EventType type, const Sort &sort = Sort(), const Filter &filter = Filter()) = 0; virtual PluginEventView* queryEvents(EventType type, const Sort &sort = Sort(), const Filter &filter = Filter()) = 0; virtual QVariantMap getSingleThread(EventType type, const QString &accountId, const QString &threadId) = 0; virtual QVariantMap getSingleEvent(EventType type, const QString &accountId, const QString &threadId, const QString &eventId) = 0; virtual QVariantMap threadForParticipants(const QString &accountId, EventType type, const QStringList &participants, History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0; virtual QList eventsForThread(const QVariantMap &thread) = 0; // Writer part of the plugin virtual QVariantMap createThreadForParticipants(const QString &accountId, EventType type, const QStringList &participants) { return QVariantMap(); } virtual bool removeThread(const QVariantMap &thread) { return false; } virtual EventWriteResult writeTextEvent(const QVariantMap &event) { return EventWriteError; } virtual bool removeTextEvent(const QVariantMap &event) { return false; } virtual EventWriteResult writeVoiceEvent(const QVariantMap &event) { return EventWriteError; } virtual bool removeVoiceEvent(const QVariantMap &event) { return false; } virtual bool beginBatchOperation() {} virtual bool endBatchOperation() {} virtual bool rollbackBatchOperation() {} }; } Q_DECLARE_INTERFACE(History::Plugin, "com.canonical.historyservice.Plugin") #endif // HISTORY_PLUGIN_H history-service-0.1+14.04.20140407/src/unionfilter_p.h0000644000015301777760000000252512320627220022725 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_UNIONFILTER_P_H #define HISTORY_UNIONFILTER_P_H #include "filter_p.h" namespace History { class UnionFilter; class UnionFilterPrivate : public FilterPrivate { public: UnionFilterPrivate(); ~UnionFilterPrivate(); Filters filters; virtual FilterType type() const { return FilterTypeUnion; } QString toString(const QString &propertyPrefix = QString::null) const; bool match(const QVariantMap properties) const; bool isValid() const; virtual QVariantMap properties() const; HISTORY_FILTER_DECLARE_CLONE(UnionFilter) }; } #endif // HISTORY_UNIONFILTER_P_H history-service-0.1+14.04.20140407/src/plugineventview.h0000644000015301777760000000271112320627220023300 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef PLUGINEVENTVIEW_H #define PLUGINEVENTVIEW_H #include #include #include #include namespace History { class PluginEventViewPrivate; class PluginEventView : public QObject, public QDBusContext { Q_OBJECT Q_DECLARE_PRIVATE(PluginEventView) public: explicit PluginEventView(QObject *parent = 0); virtual ~PluginEventView(); // DBus exposed methods Q_NOREPLY void Destroy(); virtual QList NextPage() = 0; virtual bool IsValid() const; // other methods QString objectPath() const; Q_SIGNALS: void Invalidated(); private: QScopedPointer d_ptr; }; } #endif // PLUGINEVENTVIEW_H history-service-0.1+14.04.20140407/src/plugineventview.cpp0000644000015301777760000000371212320627220023635 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "plugineventview.h" #include "plugineventview_p.h" #include "plugineventviewadaptor.h" #include "types.h" #include #include Q_DECLARE_METATYPE(QList< QVariantMap >) namespace History { PluginEventViewPrivate::PluginEventViewPrivate() : adaptor(0) { } PluginEventView::PluginEventView(QObject *parent) : QObject(parent), d_ptr(new PluginEventViewPrivate()) { Q_D(PluginEventView); qDBusRegisterMetaType >(); d->adaptor = new EventViewAdaptor(this); QString id = QString("eventview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz")); d->objectPath = QString("%1/%2").arg(History::DBusObjectPath, id); QDBusConnection::sessionBus().registerObject(d->objectPath, this); } PluginEventView::~PluginEventView() { Q_D(PluginEventView); QDBusConnection::sessionBus().unregisterObject(d->objectPath); } void PluginEventView::Destroy() { qDebug() << __PRETTY_FUNCTION__; Q_D(PluginEventView); deleteLater(); } bool PluginEventView::IsValid() const { return true; } QString PluginEventView::objectPath() const { Q_D(const PluginEventView); return d->objectPath; } } history-service-0.1+14.04.20140407/src/PluginEventView.xml0000644000015301777760000000265212320627220023515 0ustar pbusernogroup00000000000000 An interface to the history service EventView object. history-service-0.1+14.04.20140407/src/textevent.cpp0000644000015301777760000001423012320627220022425 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "textevent.h" #include "textevent_p.h" #include "texteventattachment.h" namespace History { // ------------- TextEventPrivate ------------------------------------------------ TextEventPrivate::TextEventPrivate() { } TextEventPrivate::TextEventPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theSender, const QDateTime &theTimestamp, bool theNewEvent, const QString &theMessage, MessageType theMessageType, MessageStatus theMessageStatus, const QDateTime &theReadTimestamp, const QString &theSubject, const TextEventAttachments &theAttachments, const QStringList &theParticipants) : EventPrivate(theAccountId, theThreadId, theEventId, theSender, theTimestamp, theNewEvent, theParticipants), message(theMessage), messageType(theMessageType), messageStatus(theMessageStatus), readTimestamp(theReadTimestamp), subject(theSubject), attachments(theAttachments) { } TextEventPrivate::~TextEventPrivate() { } EventType TextEventPrivate::type() const { return EventTypeText; } QVariantMap TextEventPrivate::properties() const { QVariantMap map = EventPrivate::properties(); map[FieldMessage] = message; map[FieldMessageType] = (int)messageType; map[FieldMessageStatus] = (int)messageStatus; map[FieldReadTimestamp] = readTimestamp.toString(Qt::ISODate); map[FieldSubject] = subject; QList attachmentsMap; Q_FOREACH(const TextEventAttachment &attachment, attachments) { attachmentsMap << attachment.properties(); } map[FieldAttachments] = QVariant::fromValue(attachmentsMap); return map; } // ------------- TextEvent ------------------------------------------------------- HISTORY_EVENT_DEFINE_COPY(TextEvent, EventTypeText) TextEvent::TextEvent() : Event(*new TextEventPrivate()) { } TextEvent::TextEvent(const QString &accountId, const QString &threadId, const QString &eventId, const QString &sender, const QDateTime ×tamp, bool newEvent, const QString &message, MessageType messageType, MessageStatus messageStatus, const QDateTime &readTimestamp, const QString &subject, const TextEventAttachments &attachments, const QStringList &participants) : Event(*new TextEventPrivate(accountId, threadId, eventId, sender, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject, attachments, participants)) { } TextEvent::~TextEvent() { } QString TextEvent::message() const { Q_D(const TextEvent); return d->message; } MessageType TextEvent::messageType() const { Q_D(const TextEvent); return d->messageType; } MessageStatus TextEvent::messageStatus() const { Q_D(const TextEvent); return d->messageStatus; } void TextEvent::setMessageStatus(const MessageStatus &value) { Q_D(TextEvent); d->messageStatus = value; } QDateTime TextEvent::readTimestamp() const { Q_D(const TextEvent); return d->readTimestamp; } void TextEvent::setReadTimestamp(const QDateTime &value) { Q_D(TextEvent); d->readTimestamp = value; } QString TextEvent::subject() const { Q_D(const TextEvent); return d->subject; } TextEventAttachments TextEvent::attachments() const { Q_D(const TextEvent); return d->attachments; } Event TextEvent::fromProperties(const QVariantMap &properties) { Event event; if (properties.isEmpty()) { return event; } QString accountId = properties[FieldAccountId].toString(); QString threadId = properties[FieldThreadId].toString(); QString eventId = properties[FieldEventId].toString(); QString senderId = properties[FieldSenderId].toString(); QDateTime timestamp = QDateTime::fromString(properties[FieldTimestamp].toString(), Qt::ISODate); bool newEvent = properties[FieldNewEvent].toBool(); QStringList participants = properties[FieldParticipants].toStringList(); QString message = properties[FieldMessage].toString(); QString subject = properties[FieldSubject].toString(); MessageType messageType = (MessageType) properties[FieldMessageType].toInt(); MessageStatus messageStatus = (MessageStatus) properties[FieldMessageStatus].toInt(); QDateTime readTimestamp = QDateTime::fromString(properties[FieldReadTimestamp].toString(), Qt::ISODate); // read the attachments QList attachmentProperties = properties[FieldAttachments].value >(); TextEventAttachments attachments; Q_FOREACH(const QVariantMap &map, attachmentProperties) { TextEventAttachment attachment = TextEventAttachment::fromProperties(map); if (!attachment.isNull()) { attachments << attachment; } } // and finally create the event event = TextEvent(accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject, attachments, participants); return event; } } history-service-0.1+14.04.20140407/src/intersectionfilter.h0000644000015301777760000000267512320627220023772 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_INTERSECTIONFILTER_H #define HISTORY_INTERSECTIONFILTER_H #include "filter.h" #include "types.h" namespace History { class IntersectionFilterPrivate; // AND filter class IntersectionFilter : public Filter { Q_DECLARE_PRIVATE(IntersectionFilter) public: IntersectionFilter(); ~IntersectionFilter(); // copy related members IntersectionFilter(const Filter &other); IntersectionFilter& operator=(const Filter &other); void setFilters(const Filters &filters); void prepend(const Filter &filter); void append(const Filter &filter); void clear(); Filters filters() const; static Filter fromProperties(const QVariantMap &properties); }; } #endif history-service-0.1+14.04.20140407/src/textevent.h0000644000015301777760000000427612320627220022103 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_TEXTEVENT_H #define HISTORY_TEXTEVENT_H #include "event.h" #include "texteventattachment.h" namespace History { class TextEventPrivate; class ItemFactory; class TextEvent : public Event { Q_DECLARE_PRIVATE(TextEvent) friend class ItemFactory; public: explicit TextEvent(); TextEvent(const QString &accountId, const QString &threadId, const QString &eventId, const QString &sender, const QDateTime ×tamp, bool newEvent, const QString &message, MessageType messageType, MessageStatus messageStatus = MessageStatusUnknown, const QDateTime &readTimestamp = QDateTime(), const QString &subject = QString(), const TextEventAttachments &attachments = TextEventAttachments(), const QStringList &participants = QStringList()); ~TextEvent(); // copy related members TextEvent(const Event &other); TextEvent& operator=(const Event &other); QString message() const; MessageType messageType() const; MessageStatus messageStatus() const; void setMessageStatus(const MessageStatus &value); QDateTime readTimestamp() const; void setReadTimestamp(const QDateTime &value); QString subject() const; TextEventAttachments attachments() const; static Event fromProperties(const QVariantMap &properties); }; } #endif // HISTORY_TEXTEVENT_H history-service-0.1+14.04.20140407/src/intersectionfilter_p.h0000644000015301777760000000262512320627220024304 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_INTERSECTIONFILTER_P_H #define HISTORY_INTERSECTIONFILTER_P_H #include "filter_p.h" namespace History { class IntersectionFilter; class IntersectionFilterPrivate : public FilterPrivate { public: IntersectionFilterPrivate(); ~IntersectionFilterPrivate(); virtual FilterType type() const { return FilterTypeIntersection; } QString toString(const QString &propertyPrefix = QString::null) const; bool match(const QVariantMap properties) const; bool isValid() const; Filters filters; virtual QVariantMap properties() const; HISTORY_FILTER_DECLARE_CLONE(IntersectionFilter) }; } #endif // HISTORY_INTERSECTIONFILTER_P_H history-service-0.1+14.04.20140407/src/managerdbus_p.h0000644000015301777760000000565212320627220022663 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef MANAGERDBUS_P_H #define MANAGERDBUS_P_H #include #include #include "types.h" #include "event.h" #include "thread.h" class HistoryServiceAdaptor; namespace History { class ManagerDBus : public QObject { Q_OBJECT public: explicit ManagerDBus(QObject *parent = 0); Thread threadForParticipants(const QString &accountId, EventType type, const QStringList &participants, History::MatchFlags matchFlags, bool create); bool writeEvents(const History::Events &events); bool removeThreads(const Threads &threads); bool removeEvents(const Events &events); Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId); Event getSingleEvent(EventType type, const QString &accountId, const QString &threadId, const QString &eventId); Q_SIGNALS: // signals that will be triggered after processing bus signals void threadsAdded(const History::Threads &threads); void threadsModified(const History::Threads &threads); void threadsRemoved(const History::Threads &threads); void eventsAdded(const History::Events &events); void eventsModified(const History::Events &events); void eventsRemoved(const History::Events &events); protected Q_SLOTS: void onThreadsAdded(const QList &threads); void onThreadsModified(const QList &threads); void onThreadsRemoved(const QList &threads); void onEventsAdded(const QList &events); void onEventsModified(const QList &events); void onEventsRemoved(const QList &events); protected: Threads threadsFromProperties(const QList &threadsProperties); QList threadsToProperties(const Threads &threads); Event eventFromProperties(const QVariantMap &properties); Events eventsFromProperties(const QList &eventsProperties); QList eventsToProperties(const Events &events); private: HistoryServiceAdaptor *mAdaptor; QDBusInterface mInterface; }; } #endif // MANAGERDBUS_P_H history-service-0.1+14.04.20140407/src/threadview.h0000644000015301777760000000341712320627220022213 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_THREADVIEW_H #define HISTORY_THREADVIEW_H #include "types.h" #include "filter.h" #include "sort.h" #include "thread.h" #include namespace History { class ThreadViewPrivate; class ThreadView : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(ThreadView) public: ThreadView(History::EventType type, const History::Sort &sort, const History::Filter &filter); ~ThreadView(); Threads nextPage(); bool isValid() const; Q_SIGNALS: void threadsAdded(const History::Threads &threads); void threadsModified(const History::Threads &threads); void threadsRemoved(const History::Threads &threads); void invalidated(); private: Q_PRIVATE_SLOT(d_func(), void _d_threadsAdded(const History::Threads &threads)) Q_PRIVATE_SLOT(d_func(), void _d_threadsModified(const History::Threads &threads)) Q_PRIVATE_SLOT(d_func(), void _d_threadsRemoved(const History::Threads &threads)) QScopedPointer d_ptr; }; } #endif // HISTORY_THREADVIEW_H history-service-0.1+14.04.20140407/src/tests/0000755000015301777760000000000012320627745021051 5ustar pbusernogroup00000000000000history-service-0.1+14.04.20140407/src/tests/ManagerTest.cpp0000644000015301777760000004415512320627220023764 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "eventview.h" #include "manager.h" #include "thread.h" #include "threadview.h" #include "textevent.h" #include "voiceevent.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::MatchFlags) Q_DECLARE_METATYPE(History::Threads) Q_DECLARE_METATYPE(History::Events) class ManagerTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testThreadForParticipants_data(); void testThreadForParticipants(); void testQueryEvents(); void testQueryThreads(); void testGetSingleThread(); void testWriteEvents(); void testRemoveEvents(); void testGetSingleEvent(); void testRemoveThreads(); void cleanupTestCase(); private: History::Manager *mManager; }; void ManagerTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); mManager = History::Manager::instance(); } void ManagerTest::testThreadForParticipants_data() { QTest::addColumn("accountId"); QTest::addColumn("type"); QTest::addColumn("participants"); QTest::addColumn("matchFlags"); QTest::addColumn("participantsToMatch"); QTest::newRow("text thread with one participant") << "oneAccountId" << History::EventTypeText << (QStringList() << "oneParticipant") << History::MatchFlags(History::MatchCaseSensitive) << (QStringList() << "oneParticipant"); QTest::newRow("voice thread using phone match") << "anotherAccountId" << History::EventTypeVoice << (QStringList() << "+1234567890") << History::MatchFlags(History::MatchPhoneNumber) << (QStringList() << "4567890"); } void ManagerTest::testThreadForParticipants() { QFETCH(QString, accountId); QFETCH(History::EventType, type); QFETCH(QStringList, participants); QFETCH(History::MatchFlags, matchFlags); QFETCH(QStringList, participantsToMatch); QSignalSpy spy(mManager, SIGNAL(threadsAdded(History::Threads))); History::Thread thread = mManager->threadForParticipants(accountId, type, participants, matchFlags, true); QVERIFY(!thread.isNull()); QTRY_COMPARE(spy.count(), 1); QCOMPARE(thread.accountId(), accountId); QCOMPARE(thread.type(), type); QCOMPARE(thread.participants(), participants); // now try to get the thread again to see if it is returned correctly History::Thread sameThread = mManager->threadForParticipants(accountId, type, participantsToMatch, matchFlags, false); QVERIFY(sameThread == thread); } void ManagerTest::testQueryEvents() { // just make sure the view returned is not null // the contents of the view will be tested in its own tests History::EventViewPtr eventView = mManager->queryEvents(History::EventTypeText); QVERIFY(!eventView.isNull()); QVERIFY(eventView->isValid()); } void ManagerTest::testQueryThreads() { // just make sure the view returned is not null // the contents of the view will be tested in its own tests History::ThreadViewPtr threadView = mManager->queryThreads(History::EventTypeVoice); QVERIFY(!threadView.isNull()); QVERIFY(threadView->isValid()); } void ManagerTest::testGetSingleThread() { History::Thread thread = mManager->threadForParticipants("theAccountId", History::EventTypeText, QStringList() << "theParticipant", History::MatchCaseSensitive, true); QVERIFY(!thread.isNull()); // try getting the same thread History::Thread sameThread = mManager->getSingleThread(thread.type(), thread.accountId(), thread.threadId()); QVERIFY(sameThread == thread); } void ManagerTest::testWriteEvents() { // create two threads, one for voice and one for text History::Thread textThread = mManager->threadForParticipants("textAccountId", History::EventTypeText, QStringList()<< "textParticipant", History::MatchCaseSensitive, true); History::Thread voiceThread = mManager->threadForParticipants("voiceAccountId", History::EventTypeVoice, QStringList()<< "voiceParticipant", History::MatchCaseSensitive, true); // insert some text and voice events History::Events events; for (int i = 0; i < 50; ++i) { History::TextEvent textEvent(textThread.accountId(), textThread.threadId(), QString("eventId%1").arg(i), textThread.participants().first(), QDateTime::currentDateTime(), true, QString("Hello world %1").arg(i), History::MessageTypeText, History::MessageStatusAccepted); events.append(textEvent); History::VoiceEvent voiceEvent(voiceThread.accountId(), voiceThread.threadId(), QString("eventId%1").arg(i), voiceThread.participants().first(), QDateTime::currentDateTime(), true, true); events.append(voiceEvent); } QSignalSpy newEventsSpy(mManager, SIGNAL(eventsAdded(History::Events))); QSignalSpy threadsModifiedSpy(mManager, SIGNAL(threadsModified(History::Threads))); QVERIFY(mManager->writeEvents(events)); QTRY_COMPARE(newEventsSpy.count(), 1); QTRY_COMPARE(threadsModifiedSpy.count(), 1); // check that the signal was emitted with the correct number of events History::Events returnedEvents = newEventsSpy.first().first().value(); // get two events to modify before sorting History::TextEvent modifiedTextEvent = events[0]; History::VoiceEvent modifiedVoiceEvent = events[1]; // just in case, sort the lists before comparing qSort(events); qSort(returnedEvents); QCOMPARE(returnedEvents, events); History::Threads returnedThreads = threadsModifiedSpy.first().first().value(); QCOMPARE(returnedThreads.count(), 2); // and now modify the events modifiedTextEvent.setNewEvent(false); modifiedTextEvent.setMessageStatus(History::MessageStatusDelivered); modifiedVoiceEvent.setNewEvent(false); QSignalSpy eventsModifiedSpy(mManager, SIGNAL(eventsModified(History::Events))); threadsModifiedSpy.clear(); events.clear(); events << modifiedTextEvent << modifiedVoiceEvent; QVERIFY(mManager->writeEvents(events)); QTRY_COMPARE(eventsModifiedSpy.count(), 1); QTRY_COMPARE(threadsModifiedSpy.count(), 1); returnedEvents = eventsModifiedSpy.first().first().value(); qDebug() << returnedEvents.first().accountId(); QCOMPARE(returnedEvents.count(), 2); returnedThreads = threadsModifiedSpy.first().first().value(); QCOMPARE(returnedThreads.count(), 2); } void ManagerTest::testRemoveEvents() { // create two threads, one for voice and one for text History::Thread textThread = mManager->threadForParticipants("textRemovableAccount", History::EventTypeText, QStringList()<< "textParticipant", History::MatchCaseSensitive, true); History::Thread voiceThread = mManager->threadForParticipants("voiceRemovableAccount", History::EventTypeVoice, QStringList()<< "voiceParticipant", History::MatchCaseSensitive, true); // insert some text and voice events History::Events events; for (int i = 0; i < 50; ++i) { History::TextEvent textEvent(textThread.accountId(), textThread.threadId(), QString("eventToBeRemoved%1").arg(i), textThread.participants().first(), QDateTime::currentDateTime(), true, QString("Hello world %1").arg(i), History::MessageTypeText); events.append(textEvent); History::VoiceEvent voiceEvent(voiceThread.accountId(), voiceThread.threadId(), QString("eventToBeRemoved%1").arg(i), voiceThread.participants().first(), QDateTime::currentDateTime(), true, true); events.append(voiceEvent); } QSignalSpy eventsRemovedSpy(mManager, SIGNAL(eventsRemoved(History::Events))); QSignalSpy threadsModifiedSpy(mManager, SIGNAL(threadsModified(History::Threads))); QVERIFY(mManager->writeEvents(events)); QTRY_COMPARE(threadsModifiedSpy.count(), 1); threadsModifiedSpy.clear(); History::Events secondRemoval; secondRemoval << events.takeFirst() << events.takeLast(); QVERIFY(mManager->removeEvents(events)); QTRY_COMPARE(eventsRemovedSpy.count(), 1); QTRY_COMPARE(threadsModifiedSpy.count(), 1); History::Events removedEvents = eventsRemovedSpy.first().first().value(); History::Threads modifiedThreads = threadsModifiedSpy.first().first().value(); qSort(events); qSort(removedEvents); QCOMPARE(removedEvents, events); QCOMPARE(modifiedThreads.count(), 2); // now remove the remaining events and make sure the threads get removed too QSignalSpy threadsRemovedSpy(mManager, SIGNAL(threadsRemoved(History::Threads))); eventsRemovedSpy.clear(); QVERIFY(mManager->removeEvents(secondRemoval)); QTRY_COMPARE(eventsRemovedSpy.count(), 1); QTRY_COMPARE(threadsRemovedSpy.count(), 1); removedEvents = eventsRemovedSpy.first().first().value(); History::Threads removedThreads = threadsRemovedSpy.first().first().value(); qSort(removedEvents); qSort(secondRemoval); QCOMPARE(removedEvents, secondRemoval); QCOMPARE(removedThreads.count(), 2); } void ManagerTest::testGetSingleEvent() { // create two threads, one for voice and one for text History::Thread textThread = mManager->threadForParticipants("textSingleAccount", History::EventTypeText, QStringList()<< "textSingleParticipant", History::MatchCaseSensitive, true); History::Thread voiceThread = mManager->threadForParticipants("voiceSingleAccount", History::EventTypeVoice, QStringList()<< "voiceSingleParticipant", History::MatchCaseSensitive, true); // now add two events History::TextEvent textEvent(textThread.accountId(), textThread.threadId(), "singleEventId", "self", QDateTime::currentDateTime(), true, "Hello big world!", History::MessageTypeText, History::MessageStatusPending); History::VoiceEvent voiceEvent(voiceThread.accountId(), voiceThread.threadId(), "singleEventId", "self", QDateTime::currentDateTime(), false, false, QTime(1,2,3)); QVERIFY(mManager->writeEvents(History::Events() << textEvent << voiceEvent)); // and now try to get them History::TextEvent retrievedTextEvent = mManager->getSingleEvent(History::EventTypeText, textEvent.accountId(), textEvent.threadId(), textEvent.eventId()); QVERIFY(retrievedTextEvent == textEvent); QCOMPARE(retrievedTextEvent.newEvent(), textEvent.newEvent()); QCOMPARE(retrievedTextEvent.message(), textEvent.message()); QCOMPARE(retrievedTextEvent.messageType(), textEvent.messageType()); QCOMPARE(retrievedTextEvent.messageStatus(), textEvent.messageStatus()); History::VoiceEvent retrievedVoiceEvent = mManager->getSingleEvent(History::EventTypeVoice, voiceEvent.accountId(), voiceEvent.threadId(), voiceEvent.eventId()); QVERIFY(retrievedVoiceEvent == voiceEvent); QCOMPARE(retrievedVoiceEvent.newEvent(), voiceEvent.newEvent()); QCOMPARE(retrievedVoiceEvent.missed(), voiceEvent.missed()); QCOMPARE(retrievedVoiceEvent.duration(), voiceEvent.duration()); } void ManagerTest::testRemoveThreads() { // create two threads, one for voice and one for text History::Thread textThread = mManager->threadForParticipants("textThreadRemovalAccount", History::EventTypeText, QStringList()<< "textParticipant", History::MatchCaseSensitive, true); QVERIFY(!textThread.isNull()); History::Thread voiceThread = mManager->threadForParticipants("voiceThreadRemovalAccount", History::EventTypeVoice, QStringList()<< "voiceParticipant", History::MatchCaseSensitive, true); QVERIFY(!voiceThread.isNull()); History::Threads threads; threads << textThread << voiceThread; // insert some text and voice events History::Events events; for (int i = 0; i < 50; ++i) { History::TextEvent textEvent(textThread.accountId(), textThread.threadId(), QString("eventToBeRemoved%1").arg(i), textThread.participants().first(), QDateTime::currentDateTime(), true, QString("Hello world %1").arg(i), History::MessageTypeText); events.append(textEvent); History::VoiceEvent voiceEvent(voiceThread.accountId(), voiceThread.threadId(), QString("eventToBeRemoved%1").arg(i), voiceThread.participants().first(), QDateTime::currentDateTime(), true, true); events.append(voiceEvent); } QVERIFY(mManager->writeEvents(events)); QSignalSpy eventsRemovedSpy(mManager, SIGNAL(eventsRemoved(History::Events))); QSignalSpy threadsRemovedSpy(mManager, SIGNAL(threadsRemoved(History::Threads))); QVERIFY(mManager->removeThreads(threads)); QTRY_COMPARE(eventsRemovedSpy.count(), 1); QTRY_COMPARE(threadsRemovedSpy.count(), 1); History::Events removedEvents = eventsRemovedSpy.first().first().value(); qSort(removedEvents); qSort(events); QCOMPARE(removedEvents, events); History::Threads removedThreads = threadsRemovedSpy.first().first().value(); qSort(removedThreads); qSort(threads); QCOMPARE(removedThreads, threads); } void ManagerTest::cleanupTestCase() { delete mManager; } QTEST_MAIN(ManagerTest) #include "ManagerTest.moc" history-service-0.1+14.04.20140407/src/tests/TextEventAttachmentTest.cpp0000644000015301777760000002236112320627220026344 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "thread.h" #include "textevent.h" #include "voiceevent.h" Q_DECLARE_METATYPE(History::AttachmentFlags) Q_DECLARE_METATYPE(History::AttachmentFlag) class TextEventAttachmentTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testCreateNewTextEventAttachment_data(); void testCreateNewTextEventAttachment(); void testFromProperties(); void testCopyConstructor(); void testAssignment(); void testEquals_data(); void testEquals(); void testIsNull(); }; void TextEventAttachmentTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); } void TextEventAttachmentTest::testCreateNewTextEventAttachment_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("attachmentId"); QTest::addColumn("contentType"); QTest::addColumn("filePath"); QTest::addColumn("status"); QTest::newRow("regular attachment") << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << "image/x-jpeg" << "/some/file/path.jpg" << History::AttachmentFlags(History::AttachmentDownloaded); QTest::newRow("no content type and pending attachment") << "anotheraccountid" << "anotherthreadid" << "anothereventid" << "anotherattachmentid" << "" << "/another/file/path.jpg" << History::AttachmentFlags(History::AttachmentPending); QTest::newRow("attachment with error") << "yetanotheraccountid" << "yetanotherthreadid" << "yetanothereventid" << "yetanotherattachmentid" << "" << "/some/file/path" << History::AttachmentFlags(History::AttachmentError); } void TextEventAttachmentTest::testCreateNewTextEventAttachment() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, attachmentId); QFETCH(QString, contentType); QFETCH(QString, filePath); QFETCH(History::AttachmentFlags, status); History::TextEventAttachment attachment(accountId, threadId, eventId, attachmentId, contentType, filePath, status); QCOMPARE(attachment.accountId(), accountId); QCOMPARE(attachment.threadId(), threadId); QCOMPARE(attachment.eventId(), eventId); QCOMPARE(attachment.attachmentId(), attachmentId); QCOMPARE(attachment.contentType(), contentType); QCOMPARE(attachment.filePath(), filePath); QCOMPARE(attachment.status(), status); QVariantMap properties = attachment.properties(); QCOMPARE(properties[History::FieldAccountId].toString(), accountId); QCOMPARE(properties[History::FieldThreadId].toString(), threadId); QCOMPARE(properties[History::FieldEventId].toString(), eventId); QCOMPARE(properties[History::FieldAttachmentId].toString(), attachmentId); QCOMPARE(properties[History::FieldContentType].toString(), contentType); QCOMPARE(properties[History::FieldFilePath].toString(), filePath); QCOMPARE(properties[History::FieldStatus].toInt(), (int) status); } void TextEventAttachmentTest::testFromProperties() { QVariantMap properties; properties[History::FieldAccountId] = "someAccountId"; properties[History::FieldThreadId] = "someThreadId"; properties[History::FieldEventId] = "someEventId"; properties[History::FieldAttachmentId] = "someAttachmentId"; properties[History::FieldContentType] = "someContentType"; properties[History::FieldFilePath] = "/some/file/path"; properties[History::FieldStatus] = (int) History::AttachmentDownloaded; History::TextEventAttachment attachment = History::TextEventAttachment::fromProperties(properties); QCOMPARE(attachment.accountId(), properties[History::FieldAccountId].toString()); QCOMPARE(attachment.threadId(), properties[History::FieldThreadId].toString()); QCOMPARE(attachment.eventId(), properties[History::FieldEventId].toString()); QCOMPARE(attachment.attachmentId(), properties[History::FieldAttachmentId].toString()); QCOMPARE(attachment.contentType(), properties[History::FieldContentType].toString()); QCOMPARE(attachment.filePath(), properties[History::FieldFilePath].toString()); QCOMPARE(attachment.status(), (History::AttachmentFlags) properties[History::FieldStatus].toInt()); // now load from an empty map History::TextEventAttachment emptyAttachment = History::TextEventAttachment::fromProperties(QVariantMap()); QVERIFY(emptyAttachment.isNull()); } void TextEventAttachmentTest::testCopyConstructor() { History::TextEventAttachment attachment("oneAccountId", "oneThreadId", "oneEventId", "oneAttachmentId", "oneContentType", "/one/file/path", History::AttachmentPending); History::TextEventAttachment copy(attachment); QCOMPARE(copy.accountId(), attachment.accountId()); QCOMPARE(copy.threadId(), attachment.threadId()); QCOMPARE(copy.eventId(), attachment.eventId()); QCOMPARE(copy.attachmentId(), attachment.attachmentId()); QCOMPARE(copy.contentType(), attachment.contentType()); QCOMPARE(copy.filePath(), attachment.filePath()); QCOMPARE(copy.status(), attachment.status()); } void TextEventAttachmentTest::testAssignment() { History::TextEventAttachment attachment("oneAccountId", "oneThreadId", "oneEventId", "oneAttachmentId", "oneContentType", "/one/file/path", History::AttachmentPending); History::TextEventAttachment copy; copy = attachment; QCOMPARE(copy.accountId(), attachment.accountId()); QCOMPARE(copy.threadId(), attachment.threadId()); QCOMPARE(copy.eventId(), attachment.eventId()); QCOMPARE(copy.attachmentId(), attachment.attachmentId()); QCOMPARE(copy.contentType(), attachment.contentType()); QCOMPARE(copy.filePath(), attachment.filePath()); QCOMPARE(copy.status(), attachment.status()); } void TextEventAttachmentTest::testEquals_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("attachmentId"); QTest::addColumn("secondAccountId"); QTest::addColumn("secondThreadId"); QTest::addColumn("secondEventId"); QTest::addColumn("secondAttachmentId"); QTest::addColumn("result"); QTest::newRow("equal") << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << true; QTest::newRow("different accountId") << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << "otheraccountid" << "somethreadid" << "someeventid" << "someattachmentid" << false; QTest::newRow("different threadId") << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << "someaccountid" << "otherthreadid" << "someeventid" << "someattachmentid" << false; QTest::newRow("different eventId") << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << "someaccountid" << "somethreadid" << "othereventid" << "someattachmentid" << false; QTest::newRow("different attachmentId") << "someaccountid" << "somethreadid" << "someeventid" << "someattachmentid" << "someaccountid" << "somethreadid" << "someeventid" << "otherattachmentid" << false; } void TextEventAttachmentTest::testEquals() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, attachmentId); QFETCH(QString, secondAccountId); QFETCH(QString, secondThreadId); QFETCH(QString, secondEventId); QFETCH(QString, secondAttachmentId); QFETCH(bool, result); History::TextEventAttachment attachment(accountId, threadId, eventId, attachmentId, "oneContentType", "/one/file/path", History::AttachmentPending); History::TextEventAttachment anotherAttachment(secondAccountId, secondThreadId, secondEventId, secondAttachmentId, "anotherContentType", "/different/file/path", History::AttachmentDownloaded); QCOMPARE(attachment == anotherAttachment, result); } void TextEventAttachmentTest::testIsNull() { History::TextEventAttachment attachment; QVERIFY(attachment.isNull()); } QTEST_MAIN(TextEventAttachmentTest) #include "TextEventAttachmentTest.moc" history-service-0.1+14.04.20140407/src/tests/UnionFilterTest.cpp0000644000015301777760000002265112320627220024645 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "unionfilter.h" Q_DECLARE_METATYPE(History::MatchFlags) Q_DECLARE_METATYPE(History::UnionFilter) class UnionFilterTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testSetFilters(); void testAppendFilter(); void testPrependFilter(); void testClear(); void testMatch_data(); void testMatch(); void testToStringWithNoFilters(); void testToStringWithOneFilter(); void testToStringWithManyFilters(); void testConvertToFilterAndBack(); void testIsValid_data(); void testIsValid(); void testProperties(); void testFromProperties(); }; void UnionFilterTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); } void UnionFilterTest::testSetFilters() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo); QCOMPARE(unionFilter.filters().count(), 2); QCOMPARE(unionFilter.filters()[0], filterOne); QCOMPARE(unionFilter.filters()[1], filterTwo); } void UnionFilterTest::testAppendFilter() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo); unionFilter.append(filterThree); QCOMPARE(unionFilter.filters().count(), 3); QCOMPARE(unionFilter.filters()[2], filterThree); } void UnionFilterTest::testPrependFilter() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo); unionFilter.prepend(filterThree); QCOMPARE(unionFilter.filters().count(), 3); QCOMPARE(unionFilter.filters()[0], filterThree); } void UnionFilterTest::testClear() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo); unionFilter.clear(); QVERIFY(unionFilter.filters().isEmpty()); } void UnionFilterTest::testMatch_data() { QTest::addColumn("filterProperties"); QTest::addColumn("itemProperties"); QTest::addColumn("result"); // FIXME: take into account the match flags QVariantMap filterProperties; QVariantMap itemProperties; filterProperties["stringProperty"] = QString("stringValue"); filterProperties["intProperty"] = 10; itemProperties = filterProperties; QTest::newRow("all matching values") << filterProperties << itemProperties << true; itemProperties["intProperty"] = 11; QTest::newRow("one of the values match") << filterProperties << itemProperties << true; itemProperties["stringProperty"] = QString("noMatch"); QTest::newRow("no match") << filterProperties << itemProperties << false; QTest::newRow("empty match") << QVariantMap() << itemProperties << true; } void UnionFilterTest::testMatch() { QFETCH(QVariantMap, filterProperties); QFETCH(QVariantMap, itemProperties); QFETCH(bool, result); QList filters; Q_FOREACH(const QString &key, filterProperties.keys()) { filters << History::Filter(key, filterProperties[key]); } History::UnionFilter unionFilter; unionFilter.setFilters(filters); QCOMPARE(unionFilter.match(itemProperties), result); } void UnionFilterTest::testToStringWithNoFilters() { History::UnionFilter filter; QVERIFY(filter.toString().isNull()); } void UnionFilterTest::testToStringWithOneFilter() { // test that with a single filter the result of toString() is equal to the output // of calling toString() on the filter directly History::Filter filter("aProperty", "aValue"); History::UnionFilter unionFilter; unionFilter.append(filter); QCOMPARE(unionFilter.toString(), filter.toString()); } void UnionFilterTest::testToStringWithManyFilters() { // check if all the individual filters are present in the toString() call History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo << filterThree); QString stringResult = unionFilter.toString(); QVERIFY(stringResult.contains(filterOne.toString())); QVERIFY(stringResult.contains(filterTwo.toString())); QVERIFY(stringResult.contains(filterThree.toString())); // check that the filter starts with two parenthesis (one for the block and one for the first filter // and that it also ends with two parentesis QVERIFY(stringResult.startsWith("((")); QVERIFY(stringResult.endsWith("))")); } void UnionFilterTest::testConvertToFilterAndBack() { History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo << filterThree); History::Filter castFilter = unionFilter; QCOMPARE(castFilter.toString(), unionFilter.toString()); QCOMPARE(castFilter.type(), History::FilterTypeUnion); History::UnionFilter andBack = castFilter; QCOMPARE(andBack, unionFilter); QCOMPARE(andBack.toString(), unionFilter.toString()); } void UnionFilterTest::testIsValid_data() { QTest::addColumn("filter"); QTest::addColumn("isValid"); History::UnionFilter filter; QTest::newRow("invalid filter") << filter << false; filter.append(History::Filter()); QTest::newRow("valid filter") << filter << true; } void UnionFilterTest::testIsValid() { QFETCH(History::UnionFilter, filter); QFETCH(bool, isValid); QCOMPARE(filter.isValid(), isValid); } void UnionFilterTest::testProperties() { History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::UnionFilter unionFilter; unionFilter.setFilters(QList() << filterOne << filterTwo << filterThree); QVariantMap properties = unionFilter.properties(); QVERIFY(!properties.isEmpty()); QVERIFY(properties.contains(History::FieldFilters)); QCOMPARE(properties[History::FieldFilterType].toInt(), (int) History::FilterTypeUnion); QVariantList filters = properties[History::FieldFilters].toList(); QCOMPARE(filters.count(), unionFilter.filters().count()); QVariantMap propsOne = filters[0].toMap(); QCOMPARE(propsOne, filterOne.properties()); QVariantMap propsTwo = filters[1].toMap(); QCOMPARE(propsTwo, filterTwo.properties()); QVariantMap propsThree = filters[2].toMap(); QCOMPARE(propsThree, filterThree.properties()); // check that a null filter returns an empty QVariantMap History::UnionFilter nullFilter; QVERIFY(nullFilter.properties().isEmpty()); } void UnionFilterTest::testFromProperties() { QVariantMap properties; // check that a null filter is returned History::Filter nullFilter = History::UnionFilter::fromProperties(properties); QVERIFY(nullFilter.isNull()); properties[History::FieldFilterType] = (int)History::FilterTypeUnion; QVariantList filters; for (int i = 0; i < 3; ++i) { History::Filter filter(QString("filter%1").arg(QString::number(i)), QString("value%1").arg(QString::number(i)), History::MatchCaseInsensitive); filters.append(filter.properties()); } properties[History::FieldFilters] = filters; History::Filter filter = History::UnionFilter::fromProperties(properties); QCOMPARE(filter.type(), History::FilterTypeUnion); History::UnionFilter unionFilter = filter; QCOMPARE(unionFilter.filters().count(), filters.count()); for (int i = 0; i < filters.count(); ++i) { QCOMPARE(unionFilter.filters()[i].properties(), filters[i].toMap()); } } QTEST_MAIN(UnionFilterTest) #include "UnionFilterTest.moc" history-service-0.1+14.04.20140407/src/tests/ThreadTest.cpp0000644000015301777760000003223512320627220023615 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "thread.h" #include "textevent.h" #include "voiceevent.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::Thread) class ThreadTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testCreateNewThread_data(); void testCreateNewThread(); void testFromProperties_data(); void testFromProperties(); void testFromNullProperties(); void testProperties_data(); void testProperties(); void testIsNull_data(); void testIsNull(); void testEqualsOperator_data(); void testEqualsOperator(); void testCopyConstructor(); void testAssignmentOperator(); }; void ThreadTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); } void ThreadTest::testCreateNewThread_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("type"); QTest::addColumn("participants"); QTest::addColumn("count"); QTest::addColumn("unreadCount"); QTest::newRow("voice thread with count, unread count and one participant") << "someaccountid" << "somethreadid" << History::EventTypeVoice << (QStringList() << "someparticipant") << 10 << 4; QTest::newRow("voice thread with zero messages and two participants") << "anotheraccountid" << "anotherthreadid" << History::EventTypeVoice << (QStringList() << "someparticipant" << "anotherparticipant") << 0 << 0; QTest::newRow("text thread with count, unread count and one participant") << "somevoiceaccountid" << "somevoicethreadid" << History::EventTypeText << (QStringList() << "somevoiceparticipant") << 10 << 4; QTest::newRow("voice thread with zero messages and two participants") << "anothervoiceaccountid" << "anothervoicethreadid" << History::EventTypeText << (QStringList() << "someparticipant" << "anotherparticipant") << 0 << 0; } void ThreadTest::testCreateNewThread() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(History::EventType, type); QFETCH(QStringList, participants); QFETCH(int, count); QFETCH(int, unreadCount); History::Event event; switch (type) { case History::EventTypeText: // the eventId doesn´t really matter here, just faking a random one to not use always the same event = History::TextEvent(accountId, threadId, QString("eventId%1").arg(QString::number(qrand() % 1024)), participants[0], QDateTime::currentDateTime(), false, "Random Message", History::MessageTypeText); break; case History::EventTypeVoice: event = History::VoiceEvent(accountId, threadId, QString("eventId%1").arg(QString::number(qrand() % 1024)), participants[0], QDateTime::currentDateTime(), false, false, QTime(1,2,3)); break; } History::Thread threadItem(accountId, threadId, type, participants, event, count, unreadCount); QCOMPARE(threadItem.accountId(), accountId); QCOMPARE(threadItem.threadId(), threadId); QCOMPARE(threadItem.type(), type); QCOMPARE(threadItem.participants(), participants); QCOMPARE(threadItem.lastEvent(), event); QCOMPARE(threadItem.count(), count); QCOMPARE(threadItem.unreadCount(), unreadCount); QVERIFY(threadItem.lastEvent() == event); } void ThreadTest::testFromProperties_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("type"); QTest::addColumn("participants"); QTest::addColumn("count"); QTest::addColumn("unreadCount"); QTest::newRow("voice thread with count, unread count and one participant") << "someaccountid" << "somethreadid" << History::EventTypeVoice << (QStringList() << "someparticipant") << 10 << 4; QTest::newRow("voice thread with zero messages and two participants") << "anotheraccountid" << "anotherthreadid" << History::EventTypeVoice << (QStringList() << "someparticipant" << "anotherparticipant") << 0 << 0; QTest::newRow("text thread with count, unread count and one participant") << "somevoiceaccountid" << "somevoicethreadid" << History::EventTypeText << (QStringList() << "somevoiceparticipant") << 10 << 4; QTest::newRow("voice thread with zero messages and two participants") << "anothervoiceaccountid" << "anothervoicethreadid" << History::EventTypeText << (QStringList() << "someparticipant" << "anotherparticipant") << 0 << 0; } void ThreadTest::testFromProperties() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(History::EventType, type); QFETCH(QStringList, participants); QFETCH(int, count); QFETCH(int, unreadCount); History::Event event; switch (type) { case History::EventTypeText: // the eventId doesn´t really matter here, just faking a random one to not use always the same event = History::TextEvent(accountId, threadId, QString("eventId%1").arg(QString::number(qrand() % 1024)), participants[0], QDateTime::currentDateTime(), false, "Random Message", History::MessageTypeText); break; case History::EventTypeVoice: event = History::VoiceEvent(accountId, threadId, QString("eventId%1").arg(QString::number(qrand() % 1024)), participants[0], QDateTime::currentDateTime(), false, false, QTime(1,2,3)); break; } QVariantMap properties = event.properties(); properties[History::FieldAccountId] = accountId; properties[History::FieldThreadId] = threadId; properties[History::FieldType] = (int) type; properties[History::FieldParticipants] = participants; properties[History::FieldCount] = count; properties[History::FieldUnreadCount] = unreadCount; History::Thread thread = History::Thread::fromProperties(properties); QCOMPARE(thread.accountId(), accountId); QCOMPARE(thread.threadId(), threadId); QCOMPARE(thread.type(), type); QCOMPARE(thread.participants(), participants); QCOMPARE(thread.count(), count); QCOMPARE(thread.unreadCount(), unreadCount); QVERIFY(thread.lastEvent() == event); } void ThreadTest::testFromNullProperties() { History::Thread thread = History::Thread::fromProperties(QVariantMap()); QVERIFY(thread.isNull()); } void ThreadTest::testProperties_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("type"); QTest::addColumn("participants"); QTest::addColumn("count"); QTest::addColumn("unreadCount"); QTest::newRow("voice thread with count, unread count and one participant") << "someaccountid" << "somethreadid" << History::EventTypeVoice << (QStringList() << "someparticipant") << 10 << 4; QTest::newRow("voice thread with zero messages and two participants") << "anotheraccountid" << "anotherthreadid" << History::EventTypeVoice << (QStringList() << "someparticipant" << "anotherparticipant") << 0 << 0; QTest::newRow("text thread with count, unread count and one participant") << "somevoiceaccountid" << "somevoicethreadid" << History::EventTypeText << (QStringList() << "somevoiceparticipant") << 10 << 4; QTest::newRow("voice thread with zero messages and two participants") << "anothervoiceaccountid" << "anothervoicethreadid" << History::EventTypeText << (QStringList() << "someparticipant" << "anotherparticipant") << 0 << 0; } void ThreadTest::testProperties() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(History::EventType, type); QFETCH(QStringList, participants); QFETCH(int, count); QFETCH(int, unreadCount); History::Event event; switch (type) { case History::EventTypeText: // the eventId doesn´t really matter here, just faking a random one to not use always the same event = History::TextEvent(accountId, threadId, QString("eventId%1").arg(QString::number(qrand() % 1024)), participants[0], QDateTime::currentDateTime(), false, "Random Message", History::MessageTypeText); break; case History::EventTypeVoice: event = History::VoiceEvent(accountId, threadId, QString("eventId%1").arg(QString::number(qrand() % 1024)), participants[0], QDateTime::currentDateTime(), false, false, QTime(1,2,3)); break; } History::Thread threadItem(accountId, threadId, type, participants, event, count, unreadCount); QVariantMap properties = threadItem.properties(); QCOMPARE(properties[History::FieldAccountId].toString(), accountId); QCOMPARE(properties[History::FieldThreadId].toString(), threadId); QCOMPARE(properties[History::FieldType].toInt(), (int)type); QCOMPARE(properties[History::FieldParticipants].toStringList(), participants); QCOMPARE(properties[History::FieldCount].toInt(), count); QCOMPARE(properties[History::FieldUnreadCount].toInt(), unreadCount); } void ThreadTest::testIsNull_data() { QTest::addColumn("thread"); QTest::addColumn("isNull"); QTest::newRow("empty thread") << History::Thread() << true; QTest::newRow("empty accountId") << History::Thread(QString(), "threadId" , History::EventTypeText, QStringList() << "Foo" << "Bar") << false; QTest::newRow("empty threadId") << History::Thread("AccountId", QString(), History::EventTypeVoice, QStringList() << "Foo" << "Bar") << false; QTest::newRow("empty participants") << History::Thread("AccountId", "ThreadId", History::EventTypeText, QStringList()) << false; QTest::newRow("construct empty thread") << History::Thread(QString(), QString(), History::EventTypeNull, QStringList()) << true; } void ThreadTest::testIsNull() { QFETCH(History::Thread, thread); QFETCH(bool, isNull); QCOMPARE(thread.isNull(), isNull); } void ThreadTest::testEqualsOperator_data() { QTest::addColumn("firstAccountId"); QTest::addColumn("firstThreadId"); QTest::addColumn("firstType"); QTest::addColumn("secondAccountId"); QTest::addColumn("secondThreadId"); QTest::addColumn("secondType"); QTest::addColumn("result"); QTest::newRow("equal threads") << "theAccountId" << "theThreadId" << History::EventTypeText << "theAccountId" << "theThreadId" << History::EventTypeText << true; QTest::newRow("different types") << "oneAccountId" << "oneThreadId" << History::EventTypeVoice << "oneAccountId" << "oneThreadId" << History::EventTypeText << false; QTest::newRow("different account IDs") << "firstAccountId" << "theThreadId" << History::EventTypeVoice << "secondAccountId" << "theThreadId" << History::EventTypeVoice << false; QTest::newRow("different thread IDs") << "oneAccountId" << "firstThreadId" << History::EventTypeText << "oneAccountId" << "secondThreadId" << History::EventTypeText << false; } void ThreadTest::testEqualsOperator() { QFETCH(QString, firstAccountId); QFETCH(QString, firstThreadId); QFETCH(History::EventType, firstType); QFETCH(QString, secondAccountId); QFETCH(QString, secondThreadId); QFETCH(History::EventType, secondType); QFETCH(bool, result); History::Thread firstThread(firstAccountId, firstThreadId, firstType, QStringList()); History::Thread secondThread(secondAccountId, secondThreadId, secondType, QStringList()); QVERIFY((firstThread == secondThread) == result); } void ThreadTest::testCopyConstructor() { History::Thread thread("OneAccountId", "OneThreadId", History::EventTypeText, QStringList() << "Foo" << "Bar"); History::Thread copy(thread); QVERIFY(thread == copy); } void ThreadTest::testAssignmentOperator() { History::Thread thread("OneAccountId", "OneThreadId", History::EventTypeText, QStringList() << "Foo" << "Bar"); History::Thread other; other = thread; QVERIFY(other == thread); } QTEST_MAIN(ThreadTest) #include "ThreadTest.moc" history-service-0.1+14.04.20140407/src/tests/ThreadViewTest.cpp0000644000015301777760000001224212320627220024444 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "manager.h" #include "thread.h" #include "threadview.h" #include "textevent.h" #include "unionfilter.h" #include "voiceevent.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::MatchFlags) Q_DECLARE_METATYPE(History::Threads) Q_DECLARE_METATYPE(History::Events) #define THREAD_COUNT 50 class ThreadViewTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testNextPage(); void testFilter(); void testSort(); private: void populate(); }; void ThreadViewTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); populate(); } void ThreadViewTest::testNextPage() { // create a view to return all text threads and check that the right number of items get returned History::ThreadViewPtr view = History::Manager::instance()->queryThreads(History::EventTypeText); QVERIFY(view->isValid()); History::Threads threads = view->nextPage(); History::Threads allThreads; while (threads.count() > 0) { allThreads << threads; threads = view->nextPage(); } QCOMPARE(allThreads.count(), THREAD_COUNT); Q_FOREACH(const History::Thread &thread, threads) { QCOMPARE(thread.type(), History::EventTypeText); } } void ThreadViewTest::testFilter() { History::UnionFilter filter; filter.append(History::Filter(History::FieldAccountId, "account10")); filter.append(History::Filter(History::FieldAccountId, "account35")); History::ThreadViewPtr view = History::Manager::instance()->queryThreads(History::EventTypeVoice, History::Sort(History::FieldAccountId), filter); QVERIFY(view->isValid()); History::Threads threads = view->nextPage(); QCOMPARE(threads.count(), 2); QCOMPARE(threads.first().accountId(), QString("account10")); QCOMPARE(threads.first().type(), History::EventTypeVoice); QCOMPARE(threads.last().accountId(), QString("account35")); QCOMPARE(threads.last().type(), History::EventTypeVoice); // make sure no more items are returned QVERIFY(view->nextPage().isEmpty()); } void ThreadViewTest::testSort() { History::Sort ascendingSort(History::FieldAccountId, Qt::AscendingOrder); History::ThreadViewPtr view = History::Manager::instance()->queryThreads(History::EventTypeText, ascendingSort); QVERIFY(view->isValid()); History::Threads allThreads; History::Threads threads = view->nextPage(); while (!threads.isEmpty()) { allThreads << threads; threads = view->nextPage(); } QCOMPARE(allThreads.first().accountId(), QString("account00")); QCOMPARE(allThreads.last().accountId(), QString("account%1").arg(THREAD_COUNT-1)); History::Sort descendingSort(History::FieldAccountId, Qt::DescendingOrder); allThreads.clear(); view = History::Manager::instance()->queryThreads(History::EventTypeVoice, descendingSort); QVERIFY(view->isValid()); threads = view->nextPage(); while (!threads.isEmpty()) { allThreads << threads; threads = view->nextPage(); } QCOMPARE(allThreads.first().accountId(), QString("account%1").arg(THREAD_COUNT-1)); QCOMPARE(allThreads.last().accountId(), QString("account00")); } void ThreadViewTest::populate() { // create voice threads for (int i = 0; i < THREAD_COUNT; ++i) { History::Manager::instance()->threadForParticipants(QString("account%1").arg(i, 2, 10, QChar('0')), History::EventTypeVoice, QStringList() << QString("participant%1").arg(i, 2, 10, QChar('0')), History::MatchCaseSensitive, true); } // and the text threads for (int i = 0; i < THREAD_COUNT; ++i) { History::Manager::instance()->threadForParticipants(QString("account%1").arg(i, 2, 10, QChar('0')), History::EventTypeText, QStringList() << QString("participant%1").arg(i, 2, 10, QChar('0')), History::MatchCaseSensitive, true); } } QTEST_MAIN(ThreadViewTest) #include "ThreadViewTest.moc" history-service-0.1+14.04.20140407/src/tests/PhoneUtilsTest.cpp0000644000015301777760000000707312320627220024502 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "phoneutils_p.h" class PhoneUtilsTest : public QObject { Q_OBJECT private Q_SLOTS: void testIsPhoneNumber_data(); void testIsPhoneNumber(); void testComparePhoneNumbers_data(); void testComparePhoneNumbers(); }; void PhoneUtilsTest::testIsPhoneNumber_data() { QTest::addColumn("number"); QTest::addColumn("expectedResult"); QTest::newRow("simple number") << "12345678" << true; QTest::newRow("number with dash") << "1234-5678" << true; QTest::newRow("number with area code") << "(123)12345678" << true; QTest::newRow("number with extension") << "12345678#123" << true; QTest::newRow("number with comma") << "33333333,1,1" << true; QTest::newRow("number with semicolon") << "33333333;1" << true; QTest::newRow("short/emergency number") << "190" << true; QTest::newRow("non phone numbers") << "abcdefg" << false; } void PhoneUtilsTest::testIsPhoneNumber() { QFETCH(QString, number); QFETCH(bool, expectedResult); bool result = PhoneUtils::isPhoneNumber(number); QCOMPARE(result, expectedResult); } void PhoneUtilsTest::testComparePhoneNumbers_data() { QTest::addColumn("number1"); QTest::addColumn("number2"); QTest::addColumn("expectedResult"); QTest::newRow("string equal") << "12345678" << "12345678" << true; QTest::newRow("number with dash") << "1234-5678" << "12345678" << true; QTest::newRow("number with area code") << "12312345678" << "12345678" << true; QTest::newRow("number with extension") << "12345678#123" << "12345678" << false; QTest::newRow("both numbers with extension") << "(123)12345678#1" << "12345678#1" << true; QTest::newRow("numbers with different extension") << "1234567#1" << "1234567#2" << false; QTest::newRow("number with comma") << "33333333,1,1" << "33333333" << true; QTest::newRow("both numbers with comma") << "22222222,1" << "22222222,2,1" << true; QTest::newRow("number with semicolon") << "33333333;1" << "33333333" << true; QTest::newRow("both numbers with semicolon") << "22222222;1" << "22222222;2" << true; QTest::newRow("short/emergency numbers") << "190" << "190" << true; QTest::newRow("different numbers") << "12345678" << "1234567" << false; QTest::newRow("both non phone numbers") << "abcdefg" << "abcdefg" << true; QTest::newRow("different non phone numbers") << "abcdefg" << "bcdefg" << false; QTest::newRow("phone number and custom string") << "abc12345678" << "12345678" << false; // FIXME: check what other cases we need to test here" } void PhoneUtilsTest::testComparePhoneNumbers() { QFETCH(QString, number1); QFETCH(QString, number2); QFETCH(bool, expectedResult); bool result = PhoneUtils::comparePhoneNumbers(number1, number2); QCOMPARE(result, expectedResult); } QTEST_MAIN(PhoneUtilsTest) #include "PhoneUtilsTest.moc" history-service-0.1+14.04.20140407/src/tests/TextEventTest.cpp0000644000015301777760000003321412320627220024332 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "textevent.h" class TextEventTest : public QObject { Q_OBJECT private Q_SLOTS: void testCreateNewEvent_data(); void testCreateNewEvent(); void testCastToEventAndBack(); void testFromProperties_data(); void testFromProperties(); void testFromNullProperties(); void testProperties_data(); void testProperties(); void testSetProperties(); }; void TextEventTest::testCreateNewEvent_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("senderId"); QTest::addColumn("timestamp"); QTest::addColumn("newEvent"); QTest::addColumn("message"); QTest::addColumn("messageType"); QTest::addColumn("messageStatus"); QTest::addColumn("readTimestamp"); QTest::addColumn("subject"); QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeText << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject"; QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2" << "testSenderId2" << QDateTime::currentDateTime().addDays(-10) << false << "One Test Message" << (int)History::MessageTypeText << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2"; QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeText << (int)History::MessageStatusAccepted << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3"; QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeMultiParty << 0 << QDateTime::currentDateTime().addDays(-5) << QString(); } void TextEventTest::testCreateNewEvent() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, senderId); QFETCH(QDateTime, timestamp); QFETCH(bool, newEvent); QFETCH(QString, message); QFETCH(int, messageType); QFETCH(int, messageStatus); QFETCH(QDateTime, readTimestamp); QFETCH(QString, subject); History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent, message, (History::MessageType)messageType, (History::MessageStatus)messageStatus, readTimestamp, subject); // check that the values are properly set QCOMPARE(event.accountId(), accountId); QCOMPARE(event.threadId(), threadId); QCOMPARE(event.eventId(), eventId); QCOMPARE(event.senderId(), senderId); QCOMPARE(event.timestamp(), timestamp); QCOMPARE(event.newEvent(), newEvent); QCOMPARE(event.message(), message); QCOMPARE(event.messageType(), (History::MessageType)messageType); QCOMPARE(event.messageStatus(), (History::MessageStatus)messageStatus); QCOMPARE(event.readTimestamp(), readTimestamp); QCOMPARE(event.subject(), subject); } void TextEventTest::testCastToEventAndBack() { History::TextEvent textEvent("oneAccountId", "oneThreadId", "oneEventId", "oneSender", QDateTime::currentDateTime(), true, "Hello", History::MessageTypeText); // test the copy constructor History::Event historyEvent(textEvent); QVERIFY(historyEvent == textEvent); History::TextEvent castBack(historyEvent); QVERIFY(castBack == textEvent); // and now the assignment operator History::Event anotherEvent; anotherEvent = textEvent; QVERIFY(anotherEvent == textEvent); History::TextEvent backAgain; backAgain = anotherEvent; QVERIFY(backAgain == textEvent); } void TextEventTest::testFromProperties_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("senderId"); QTest::addColumn("timestamp"); QTest::addColumn("newEvent"); QTest::addColumn("message"); QTest::addColumn("messageType"); QTest::addColumn("messageStatus"); QTest::addColumn("readTimestamp"); QTest::addColumn("subject"); QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeText << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject"; QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2" << "testSenderId2" << QDateTime::currentDateTime().addDays(-10) << false << "One Test Message" << (int)History::MessageTypeText << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2"; QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeText << (int)History::MessageStatusDelivered << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3"; QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeMultiParty << 0 << QDateTime::currentDateTime().addDays(-5) << QString(); } void TextEventTest::testFromProperties() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, senderId); QFETCH(QDateTime, timestamp); QFETCH(bool, newEvent); QFETCH(QString, message); QFETCH(int, messageType); QFETCH(int, messageStatus); QFETCH(QDateTime, readTimestamp); QFETCH(QString, subject); QVariantMap properties; properties[History::FieldAccountId] = accountId; properties[History::FieldThreadId] = threadId; properties[History::FieldEventId] = eventId; properties[History::FieldSenderId] = senderId; properties[History::FieldTimestamp] = timestamp.toString(Qt::ISODate); properties[History::FieldNewEvent] = newEvent; properties[History::FieldMessage] = message; properties[History::FieldMessageType] = messageType; properties[History::FieldMessageStatus] = messageStatus; properties[History::FieldReadTimestamp] = readTimestamp.toString(Qt::ISODate); properties[History::FieldSubject] = subject; History::TextEvent textEvent = History::TextEvent::fromProperties(properties); QCOMPARE(textEvent.accountId(), accountId); QCOMPARE(textEvent.threadId(), threadId); QCOMPARE(textEvent.eventId(), eventId); QCOMPARE(textEvent.senderId(), senderId); QCOMPARE(textEvent.timestamp().toString(Qt::ISODate), timestamp.toString(Qt::ISODate)); QCOMPARE(textEvent.newEvent(), newEvent); QCOMPARE(textEvent.message(), message); QCOMPARE(textEvent.messageType(), (History::MessageType) messageType); QCOMPARE(textEvent.messageStatus(), (History::MessageStatus) messageStatus); QCOMPARE(textEvent.readTimestamp().toString(Qt::ISODate), readTimestamp.toString(Qt::ISODate)); QCOMPARE(textEvent.subject(), subject); } void TextEventTest::testFromNullProperties() { // just to make sure, test that calling ::fromProperties() on an empty map returns a null event History::Event nullEvent = History::TextEvent::fromProperties(QVariantMap()); QVERIFY(nullEvent.isNull()); QCOMPARE(nullEvent.type(), History::EventTypeNull); } void TextEventTest::testProperties_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("senderId"); QTest::addColumn("timestamp"); QTest::addColumn("newEvent"); QTest::addColumn("message"); QTest::addColumn("messageType"); QTest::addColumn("messageStatus"); QTest::addColumn("readTimestamp"); QTest::addColumn("subject"); QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeText << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject"; QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2" << "testSenderId2" << QDateTime::currentDateTime().addDays(-10) << false << "One Test Message" << (int)History::MessageTypeText << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2"; QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeText << (int)History::MessageStatusPending << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3"; QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << "One Test Message" << (int)History::MessageTypeMultiParty << 0 << QDateTime::currentDateTime().addDays(-5) << QString(); } void TextEventTest::testProperties() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, senderId); QFETCH(QDateTime, timestamp); QFETCH(bool, newEvent); QFETCH(QString, message); QFETCH(int, messageType); QFETCH(int, messageStatus); QFETCH(QDateTime, readTimestamp); QFETCH(QString, subject); History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent, message, (History::MessageType)messageType, (History::MessageStatus)messageStatus, readTimestamp, subject); QVariantMap properties = event.properties(); QCOMPARE(properties[History::FieldAccountId].toString(), accountId); QCOMPARE(properties[History::FieldThreadId].toString(), threadId); QCOMPARE(properties[History::FieldEventId].toString(), eventId); QCOMPARE(properties[History::FieldSenderId].toString(), senderId); QCOMPARE(properties[History::FieldTimestamp].toString(), timestamp.toString(Qt::ISODate)); QCOMPARE(properties[History::FieldNewEvent].toBool(), newEvent); QCOMPARE(properties[History::FieldMessage].toString(), message); QCOMPARE(properties[History::FieldMessageType].toInt(), messageType); QCOMPARE(properties[History::FieldMessageStatus].toInt(), messageStatus); QCOMPARE(properties[History::FieldReadTimestamp].toString(), readTimestamp.toString(Qt::ISODate)); QCOMPARE(properties[History::FieldSubject].toString(), subject); } void TextEventTest::testSetProperties() { History::TextEvent textEvent("oneAccountId", "oneThreadId", "oneEventId", "oneSender", QDateTime::currentDateTime(), true, "Hello", History::MessageTypeText); QDateTime readTimestamp = QDateTime::currentDateTime(); History::MessageStatus status = History::MessageStatusDelivered; bool newEvent = false; textEvent.setReadTimestamp(readTimestamp); textEvent.setMessageStatus(status); textEvent.setNewEvent(newEvent); QCOMPARE(textEvent.readTimestamp(), readTimestamp); QCOMPARE(textEvent.messageStatus(), status); QCOMPARE(textEvent.newEvent(), newEvent); } QTEST_MAIN(TextEventTest) #include "TextEventTest.moc" history-service-0.1+14.04.20140407/src/tests/FilterTest.cpp0000644000015301777760000002572012320627220023634 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "filter.h" #include "intersectionfilter.h" #include "unionfilter.h" Q_DECLARE_METATYPE(History::MatchFlags) Q_DECLARE_METATYPE(History::Filter) class FilterTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testCreateNewFilter_data(); void testCreateNewFilter(); void testSetProperties_data(); void testSetProperties(); void testToString_data(); void testToString(); void testToStringPrefix(); void testNullToString(); void testMatch_data(); void testMatch(); void testEqualsOperator(); void testAssignmentOperator(); void testIsValid_data(); void testIsValid(); void testType(); void testProperties(); void testFromProperties(); }; void FilterTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); } void FilterTest::testCreateNewFilter_data() { QTest::addColumn("filterProperty"); QTest::addColumn("filterValue"); QTest::addColumn("matchFlags"); QTest::newRow("string property and value") << "oneProperty" << QVariant("oneValue") << History::MatchFlags(); QStringList list; list << "oneValue" << "anotherValue" << "yetAnotherValue"; QTest::newRow("a stringlist property") << "stringListProperty" << QVariant(list) << History::MatchFlags(History::MatchCaseSensitive); QTest::newRow("combining two flags and an int property") << "intProperty" << QVariant(11) << History::MatchFlags(History::MatchPhoneNumber | History::MatchContains); } void FilterTest::testCreateNewFilter() { QFETCH(QString, filterProperty); QFETCH(QVariant, filterValue); QFETCH(History::MatchFlags, matchFlags); History::Filter filter(filterProperty, filterValue, matchFlags); QCOMPARE(filter.filterProperty(), filterProperty); QCOMPARE(filter.filterValue(), filterValue); QCOMPARE(filter.matchFlags(), matchFlags); } void FilterTest::testSetProperties_data() { QTest::addColumn("filterProperty"); QTest::addColumn("filterValue"); QTest::addColumn("matchFlags"); QTest::newRow("string property and value") << "oneProperty" << QVariant("oneValue") << History::MatchFlags(); QStringList list; list << "oneValue" << "anotherValue" << "yetAnotherValue"; QTest::newRow("a stringlist property") << "stringListProperty" << QVariant(list) << History::MatchFlags(History::MatchCaseSensitive); QTest::newRow("combining two flags and an int property") << "intProperty" << QVariant(11) << History::MatchFlags(History::MatchPhoneNumber | History::MatchContains); } void FilterTest::testSetProperties() { QFETCH(QString, filterProperty); QFETCH(QVariant, filterValue); QFETCH(History::MatchFlags, matchFlags); History::Filter filter; filter.setFilterProperty(filterProperty); QCOMPARE(filter.filterProperty(), filterProperty); filter.setFilterValue(filterValue); QCOMPARE(filter.filterValue(), filterValue); filter.setMatchFlags(matchFlags); QCOMPARE(filter.matchFlags(), matchFlags); } void FilterTest::testToString_data() { QTest::addColumn("filterProperty"); QTest::addColumn("filterValue"); QTest::addColumn("result"); QTest::newRow("string value") << "stringProperty" << QVariant("stringValue") << "stringProperty=\"stringValue\""; QTest::newRow("bool property with false value") << "boolProperty" << QVariant(false) << "boolProperty=0"; QTest::newRow("bool property with true value") << "boolProperty" << QVariant(true) << "boolProperty=1"; QTest::newRow("int property") << "intProperty" << QVariant(15) << "intProperty=15"; QTest::newRow("double property") << "doubleProperty" << QVariant(1.5) << "doubleProperty=1.5"; } void FilterTest::testToString() { QFETCH(QString, filterProperty); QFETCH(QVariant, filterValue); QFETCH(QString, result); History::Filter filter(filterProperty, filterValue); QCOMPARE(filter.toString(), result); } void FilterTest::testToStringPrefix() { QString prefix("somePrefix"); QString filterProperty("someProperty"); QString filterValue("someValue"); History::Filter filter(filterProperty, filterValue); QVERIFY(filter.toString(prefix).startsWith(QString("%1.").arg(prefix))); QVERIFY(filter.toString().startsWith(filterProperty)); } void FilterTest::testNullToString() { History::Filter filter; QVERIFY(filter.toString().isNull()); } void FilterTest::testMatch_data() { QTest::addColumn("properties"); QTest::addColumn("filterProperty"); QTest::addColumn("filterValue"); QTest::addColumn("matchFlags"); QTest::addColumn("result"); QVariantMap map; map["stringProperty"] = QString("stringValue"); QTest::newRow("simple match of a string property") << map << "stringProperty" << QVariant("stringValue") << History::MatchFlags(History::MatchCaseSensitive) << true; map.clear(); map["stringProperty"] = QString("anotherValue"); QTest::newRow("string property that should not match") << map << "stringProperty" << QVariant("stringValue") << History::MatchFlags(History::MatchCaseSensitive) << false; map.clear(); map["intProperty"] = 42; QTest::newRow("integer property") << map << "intProperty" << QVariant(42) << History::MatchFlags() << true; map.clear(); map["intProperty"] = 41; QTest::newRow("integer property that should not match") << map << "intProperty" << QVariant(42) << History::MatchFlags() << false; map.clear(); map["intProperty"] = 42; QTest::newRow("empty property") << map << "" << QVariant(42) << History::MatchFlags() << true; QTest::newRow("empty value") << map << "intProperty" << QVariant() << History::MatchFlags() << true; // FIXME: add more test cases for the match flags once they are implemented } void FilterTest::testMatch() { QFETCH(QVariantMap, properties); QFETCH(QString, filterProperty); QFETCH(QVariant, filterValue); QFETCH(History::MatchFlags, matchFlags); QFETCH(bool, result); History::Filter filter(filterProperty, filterValue, matchFlags); QCOMPARE(filter.match(properties), result); } void FilterTest::testEqualsOperator() { History::Filter filterOne("oneProperty", "oneValue"); History::Filter equal("oneProperty", "oneValue"); History::Filter differentProperty("anotherProperty", "oneValue"); History::Filter differentValue("oneProperty", "anotherValue"); QVERIFY(filterOne == equal); QVERIFY(!(filterOne == differentProperty)); QVERIFY(!(filterOne == differentValue)); QVERIFY(filterOne != differentProperty); QVERIFY(filterOne != differentValue); } void FilterTest::testAssignmentOperator() { History::Filter filter(History::FieldAccountId, "OneAccountId", History::MatchFlags(History::MatchContains | History::MatchCaseSensitive)); History::Filter other; other = filter; QVERIFY(other == filter); } void FilterTest::testIsValid_data() { QTest::addColumn("filter"); QTest::addColumn("isValid"); QTest::newRow("null filter") << History::Filter() << false; QTest::newRow("null property") << History::Filter(QString::null, "Foobar") << false; QTest::newRow("null value") << History::Filter("oneProperty") << false; QTest::newRow("valid filter") << History::Filter("oneProperty", "oneValue") << true; } void FilterTest::testIsValid() { QFETCH(History::Filter, filter); QFETCH(bool, isValid); QCOMPARE(filter.isValid(), isValid); QCOMPARE(filter.isNull(), !isValid); } void FilterTest::testType() { History::Filter filter; QCOMPARE(filter.type(), History::FilterTypeStandard); } void FilterTest::testProperties() { // test an empty filter History::Filter emptyFilter; QVERIFY(emptyFilter.properties().isEmpty()); // and now a regular filter History::Filter filter("foobarProperty", "foobarValue", History::MatchCaseInsensitive); QVariantMap properties = filter.properties(); QCOMPARE(properties[History::FieldFilterType].toInt(), (int)filter.type()); QCOMPARE(properties[History::FieldFilterProperty].toString(), filter.filterProperty()); QCOMPARE(properties[History::FieldFilterValue], filter.filterValue()); QCOMPARE(properties[History::FieldMatchFlags].toInt(), (int)filter.matchFlags()); } void FilterTest::testFromProperties() { QVariantMap properties; // test an empty filter History::Filter filter = History::Filter::fromProperties(properties); QVERIFY(filter.isNull()); // and now a regular filter properties[History::FieldFilterType] = (int) History::FilterTypeStandard; properties[History::FieldFilterProperty] = "oneProperty"; properties[History::FieldFilterValue] = "oneValue"; properties[History::FieldMatchFlags] = (int) History::MatchContains; filter = History::Filter::fromProperties(properties); QCOMPARE(filter.type(), (History::FilterType)properties[History::FieldFilterType].toInt()); QCOMPARE(filter.filterProperty(), properties[History::FieldFilterProperty].toString()); QCOMPARE(filter.filterValue(), properties[History::FieldFilterValue]); QCOMPARE(filter.matchFlags(), History::MatchFlags(properties[History::FieldMatchFlags].toInt())); // test that calling fromProperties() on intersection filters works as expected History::IntersectionFilter intersectionFilter; intersectionFilter.append(History::Filter("oneProperty", "oneValue")); properties = intersectionFilter.properties(); filter = History::Filter::fromProperties(properties); QCOMPARE(filter.type(), History::FilterTypeIntersection); QCOMPARE(filter.properties(), properties); // and also on union filters History::UnionFilter unionFilter; unionFilter.append(History::Filter("oneProperty", "oneValue")); properties = unionFilter.properties(); filter = History::Filter::fromProperties(properties); QCOMPARE(filter.type(), History::FilterTypeUnion); QCOMPARE(filter.properties(), properties); } QTEST_MAIN(FilterTest) #include "FilterTest.moc" history-service-0.1+14.04.20140407/src/tests/IntersectionFilterTest.cpp0000644000015301777760000002342712320627220026225 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "intersectionfilter.h" Q_DECLARE_METATYPE(History::MatchFlags) Q_DECLARE_METATYPE(History::IntersectionFilter) class IntersectionFilterTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testSetFilters(); void testAppendFilter(); void testPrependFilter(); void testClear(); void testMatch_data(); void testMatch(); void testToStringWithNoFilters(); void testToStringWithOneFilter(); void testToStringWithManyFilters(); void testConvertToFilterAndBack(); void testIsValid_data(); void testIsValid(); void testProperties(); void testFromProperties(); }; void IntersectionFilterTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); } void IntersectionFilterTest::testSetFilters() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo); QCOMPARE(intersectionFilter.filters().count(), 2); QCOMPARE(intersectionFilter.filters()[0], filterOne); QCOMPARE(intersectionFilter.filters()[1], filterTwo); } void IntersectionFilterTest::testAppendFilter() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo); intersectionFilter.append(filterThree); QCOMPARE(intersectionFilter.filters().count(), 3); QCOMPARE(intersectionFilter.filters()[2], filterThree); } void IntersectionFilterTest::testPrependFilter() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo); intersectionFilter.prepend(filterThree); QCOMPARE(intersectionFilter.filters().count(), 3); QCOMPARE(intersectionFilter.filters()[0], filterThree); } void IntersectionFilterTest::testClear() { // create two filters and check that they are properly set History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo); intersectionFilter.clear(); QVERIFY(intersectionFilter.filters().isEmpty()); } void IntersectionFilterTest::testMatch_data() { QTest::addColumn("filterProperties"); QTest::addColumn("itemProperties"); QTest::addColumn("result"); // FIXME: take into account the match flags QVariantMap filterProperties; QVariantMap itemProperties; filterProperties["stringProperty"] = QString("stringValue"); filterProperties["intProperty"] = 10; itemProperties = filterProperties; QTest::newRow("all matching values") << filterProperties << itemProperties << true; itemProperties["intProperty"] = 11; QTest::newRow("one of the values is different") << filterProperties << itemProperties << false; itemProperties["stringProperty"] = QString("noMatch"); QTest::newRow("no match at all") << filterProperties << itemProperties << false; QTest::newRow("empty match") << QVariantMap() << itemProperties << true; } void IntersectionFilterTest::testMatch() { QFETCH(QVariantMap, filterProperties); QFETCH(QVariantMap, itemProperties); QFETCH(bool, result); QList filters; Q_FOREACH(const QString &key, filterProperties.keys()) { filters << History::Filter(key, filterProperties[key]); } History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(filters); QCOMPARE(intersectionFilter.match(itemProperties), result); } void IntersectionFilterTest::testToStringWithNoFilters() { History::IntersectionFilter filter; QVERIFY(filter.toString().isNull()); } void IntersectionFilterTest::testToStringWithOneFilter() { // test that with a single filter the result of toString() is equal to the output // of calling toString() on the filter directly History::Filter filter("aProperty", "aValue"); History::IntersectionFilter intersectionFilter; intersectionFilter.append(filter); QCOMPARE(intersectionFilter.toString(), filter.toString()); } void IntersectionFilterTest::testToStringWithManyFilters() { // check if each of the individual filters are present in the toString output History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo << filterThree); QString stringResult = intersectionFilter.toString(); QVERIFY(stringResult.contains(filterOne.toString())); QVERIFY(stringResult.contains(filterTwo.toString())); QVERIFY(stringResult.contains(filterThree.toString())); } void IntersectionFilterTest::testConvertToFilterAndBack() { History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo << filterThree); History::Filter castFilter = intersectionFilter; QCOMPARE(castFilter.toString(), intersectionFilter.toString()); QCOMPARE(castFilter.type(), History::FilterTypeIntersection); History::IntersectionFilter andBack = castFilter; QCOMPARE(andBack, intersectionFilter); QCOMPARE(andBack.toString(), intersectionFilter.toString()); } void IntersectionFilterTest::testIsValid_data() { QTest::addColumn("filter"); QTest::addColumn("isValid"); History::IntersectionFilter filter; QTest::newRow("invalid filter") << filter << false; filter.append(History::Filter()); QTest::newRow("valid filter") << filter << true; } void IntersectionFilterTest::testIsValid() { QFETCH(History::IntersectionFilter, filter); QFETCH(bool, isValid); QCOMPARE(filter.isValid(), isValid); } void IntersectionFilterTest::testProperties() { History::Filter filterOne("propertyOne", "valueOne"); History::Filter filterTwo("propertyTwo", "valueTwo"); History::Filter filterThree("propertyThree", "valueThree"); History::IntersectionFilter intersectionFilter; intersectionFilter.setFilters(QList() << filterOne << filterTwo << filterThree); QVariantMap properties = intersectionFilter.properties(); QVERIFY(!properties.isEmpty()); QVERIFY(properties.contains(History::FieldFilters)); QCOMPARE(properties[History::FieldFilterType].toInt(), (int) History::FilterTypeIntersection); QVariantList filters = properties[History::FieldFilters].toList(); QCOMPARE(filters.count(), intersectionFilter.filters().count()); QVariantMap propsOne = filters[0].toMap(); QCOMPARE(propsOne, filterOne.properties()); QVariantMap propsTwo = filters[1].toMap(); QCOMPARE(propsTwo, filterTwo.properties()); QVariantMap propsThree = filters[2].toMap(); QCOMPARE(propsThree, filterThree.properties()); // check that a null filter returns an empty QVariantMap History::IntersectionFilter nullFilter; QVERIFY(nullFilter.properties().isEmpty()); } void IntersectionFilterTest::testFromProperties() { QVariantMap properties; // check that a null filter is returned History::Filter nullFilter = History::IntersectionFilter::fromProperties(properties); QVERIFY(nullFilter.isNull()); properties[History::FieldFilterType] = (int)History::FilterTypeIntersection; QVariantList filters; for (int i = 0; i < 3; ++i) { History::Filter filter(QString("filter%1").arg(QString::number(i)), QString("value%1").arg(QString::number(i)), History::MatchCaseInsensitive); filters.append(filter.properties()); } properties[History::FieldFilters] = filters; History::Filter filter = History::IntersectionFilter::fromProperties(properties); QCOMPARE(filter.type(), History::FilterTypeIntersection); History::IntersectionFilter intersectionFilter = filter; QCOMPARE(intersectionFilter.filters().count(), filters.count()); for (int i = 0; i < filters.count(); ++i) { QCOMPARE(intersectionFilter.filters()[i].properties(), filters[i].toMap()); } } QTEST_MAIN(IntersectionFilterTest) #include "IntersectionFilterTest.moc" history-service-0.1+14.04.20140407/src/tests/EventViewTest.cpp0000644000015301777760000001530012320627220024314 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "eventview.h" #include "intersectionfilter.h" #include "manager.h" #include "thread.h" #include "textevent.h" #include "unionfilter.h" #include "voiceevent.h" Q_DECLARE_METATYPE(History::EventType) Q_DECLARE_METATYPE(History::MatchFlags) #define EVENT_COUNT 50 class EventViewTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testNextPage(); void testFilter(); void testSort(); private: void populate(); }; void EventViewTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); populate(); } void EventViewTest::testNextPage() { // create a view to return all text threads and check that the right number of items get returned History::EventViewPtr view = History::Manager::instance()->queryEvents(History::EventTypeText); QVERIFY(view->isValid()); History::Events events = view->nextPage(); History::Events allEvents; while (events.count() > 0) { allEvents << events; events = view->nextPage(); } QCOMPARE(allEvents.count(), EVENT_COUNT * 2); Q_FOREACH(const History::Event &event, events) { QCOMPARE(event.type(), History::EventTypeText); } } void EventViewTest::testFilter() { History::IntersectionFilter filter; filter.append(History::Filter(History::FieldAccountId, "account0")); filter.append(History::Filter(History::FieldThreadId, "participant0")); filter.append(History::Filter(History::FieldEventId, "event21")); History::EventViewPtr view = History::Manager::instance()->queryEvents(History::EventTypeVoice, History::Sort(History::FieldAccountId), filter); QVERIFY(view->isValid()); History::Events events = view->nextPage(); QCOMPARE(events.count(), 1); History::Event event = events.first(); QCOMPARE(event.accountId(), QString("account0")); QCOMPARE(event.type(), History::EventTypeVoice); QCOMPARE(event.threadId(), QString("participant0")); QCOMPARE(event.eventId(), QString("event21")); // make sure no more items are returned QVERIFY(view->nextPage().isEmpty()); } void EventViewTest::testSort() { History::Sort ascendingSort(History::FieldEventId, Qt::AscendingOrder); History::EventViewPtr view = History::Manager::instance()->queryEvents(History::EventTypeText, ascendingSort); QVERIFY(view->isValid()); History::Events allEvents; History::Events events = view->nextPage(); while (!events.isEmpty()) { allEvents << events; events = view->nextPage(); } QCOMPARE(allEvents.first().eventId(), QString("event00")); QCOMPARE(allEvents.last().eventId(), QString("event%1").arg(EVENT_COUNT-1)); History::Sort descendingSort(History::FieldEventId, Qt::DescendingOrder); allEvents.clear(); view = History::Manager::instance()->queryEvents(History::EventTypeVoice, descendingSort); QVERIFY(view->isValid()); events = view->nextPage(); while (!events.isEmpty()) { allEvents << events; events = view->nextPage(); } QCOMPARE(allEvents.first().eventId(), QString("event%1").arg(EVENT_COUNT-1)); QCOMPARE(allEvents.last().eventId(), QString("event00")); } void EventViewTest::populate() { // create two threads of each type for (int i = 0; i < 2; ++i) { History::Thread voiceThread = History::Manager::instance()->threadForParticipants(QString("account%1").arg(i), History::EventTypeVoice, QStringList() << QString("participant%1").arg(i), History::MatchCaseSensitive, true); QVERIFY(!voiceThread.isNull()); History::Thread textThread = History::Manager::instance()->threadForParticipants(QString("account%1").arg(i), History::EventTypeText, QStringList() << QString("participant%1").arg(i), History::MatchCaseSensitive, true); QVERIFY(!textThread.isNull()); // now create some events for the threads History::Events events; for (int j = 0; j < EVENT_COUNT; ++j) { History::VoiceEvent voiceEvent(voiceThread.accountId(), voiceThread.threadId(), QString("event%1").arg(j, 2, 10, QChar('0')), j % 2 ? "self" : QString("participant%1").arg(i), QDateTime::currentDateTime(), j % 2, j % 2, j % 2 ? QTime(i, j, 0) : QTime()); History::TextEvent textEvent(textThread.accountId(), textThread.threadId(), QString("event%1").arg(j, 2, 10, QChar('0')), j % 2 ? "self" : QString("participant%1").arg(i), QDateTime::currentDateTime(), j % 2, QString("Hello %1").arg(j), History::MessageTypeText, History::MessageStatusDelivered); events << voiceEvent << textEvent; } QVERIFY(History::Manager::instance()->writeEvents(events)); } } QTEST_MAIN(EventViewTest) #include "EventViewTest.moc" history-service-0.1+14.04.20140407/src/tests/CMakeLists.txt0000644000015301777760000000316312320627220023600 0ustar pbusernogroup00000000000000include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) macro(generate_test TESTNAME USE_DBUS) add_executable(${TESTNAME} ${TESTNAME}.cpp) qt5_use_modules(${TESTNAME} Core DBus Test) target_link_libraries(${TESTNAME} historyservice ) if (${USE_DBUS}) add_test(${TESTNAME} ${DBUS_RUNNER} --keep-env --task ${CMAKE_BINARY_DIR}/daemon/history-daemon --ignore-return --task ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -p -xunitxml -p -o -p ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml --wait-for=com.canonical.HistoryService) else (${USE_DBUS}) add_test(${TESTNAME} ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} -xunitxml -o ${CMAKE_BINARY_DIR}/test_${TESTNAME}.xml) endif(${USE_DBUS}) set(TEST_ENVIRONMENT "HOME=/tmp/history_test_home\;HISTORY_SQLITE_DBPATH=:memory:\;HISTORY_PLUGIN_PATH=${CMAKE_BINARY_DIR}/plugins/sqlite") set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT ${TEST_ENVIRONMENT} TIMEOUT 60) endmacro(generate_test) generate_test(FilterTest False) generate_test(IntersectionFilterTest False) generate_test(PhoneUtilsTest False) generate_test(SortTest False) generate_test(ThreadTest False) generate_test(TextEventTest False) generate_test(TextEventAttachmentTest False) generate_test(UnionFilterTest False) generate_test(VoiceEventTest False) if (DBUS_RUNNER) generate_test(ManagerTest True) generate_test(ThreadViewTest True) generate_test(EventViewTest True) endif (DBUS_RUNNER) history-service-0.1+14.04.20140407/src/tests/VoiceEventTest.cpp0000644000015301777760000002121412320627220024450 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "voiceevent.h" class VoiceEventTest : public QObject { Q_OBJECT private Q_SLOTS: void testCreateNewEvent_data(); void testCreateNewEvent(); void testCastToEventAndBack(); void testFromProperties_data(); void testFromProperties(); void testFromNullProperties(); void testProperties_data(); void testProperties(); }; void VoiceEventTest::testCreateNewEvent_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("senderId"); QTest::addColumn("timestamp"); QTest::addColumn("newEvent"); QTest::addColumn("missed"); QTest::addColumn("duration"); QTest::newRow("unread missed call") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << true << QTime(0, 0, 0); QTest::newRow("missed call") << "testAccountId2" << "testThreadId2" << "testEventId2" << "testSenderId2" << QDateTime::currentDateTime().addDays(-5) << false << true << QTime(0, 0, 0); QTest::newRow("not missed call") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << false << false << QTime(1, 2, 3); } void VoiceEventTest::testCreateNewEvent() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, senderId); QFETCH(QDateTime, timestamp); QFETCH(bool, newEvent); QFETCH(bool, missed); QFETCH(QTime, duration); History::VoiceEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent, missed, duration); // check that the values are properly set QCOMPARE(event.accountId(), accountId); QCOMPARE(event.threadId(), threadId); QCOMPARE(event.eventId(), eventId); QCOMPARE(event.senderId(), senderId); QCOMPARE(event.timestamp(), timestamp); QCOMPARE(event.newEvent(), newEvent); QCOMPARE(event.missed(), missed); QCOMPARE(event.duration(), duration); } void VoiceEventTest::testCastToEventAndBack() { History::VoiceEvent voiceEvent("oneAccountId", "oneThreadId", "oneEventId", "oneSender", QDateTime::currentDateTime(), true, true, QTime(1,2,3)); // test the copy constructor History::Event historyEvent(voiceEvent); QVERIFY(historyEvent == voiceEvent); History::VoiceEvent castBack(historyEvent); QVERIFY(castBack == voiceEvent); // and now the assignment operator History::Event anotherEvent; anotherEvent = voiceEvent; QVERIFY(anotherEvent == voiceEvent); History::VoiceEvent backAgain; backAgain = anotherEvent; QVERIFY(backAgain == voiceEvent); } void VoiceEventTest::testFromProperties_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("senderId"); QTest::addColumn("timestamp"); QTest::addColumn("newEvent"); QTest::addColumn("missed"); QTest::addColumn("duration"); QTest::newRow("unread missed call") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << true << QTime(0, 0, 0); QTest::newRow("missed call") << "testAccountId2" << "testThreadId2" << "testEventId2" << "testSenderId2" << QDateTime::currentDateTime().addDays(-5) << false << true << QTime(0, 0, 0); QTest::newRow("not missed call") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << false << false << QTime(1, 2, 3); } void VoiceEventTest::testFromProperties() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, senderId); QFETCH(QDateTime, timestamp); QFETCH(bool, newEvent); QFETCH(bool, missed); QFETCH(QTime, duration); QVariantMap properties; properties[History::FieldAccountId] = accountId; properties[History::FieldThreadId] = threadId; properties[History::FieldEventId] = eventId; properties[History::FieldSenderId] = senderId; properties[History::FieldTimestamp] = timestamp.toString(Qt::ISODate); properties[History::FieldNewEvent] = newEvent; properties[History::FieldMissed] = missed; properties[History::FieldDuration] = QTime(0,0,0,0).secsTo(duration); History::VoiceEvent voiceEvent = History::VoiceEvent::fromProperties(properties); QCOMPARE(voiceEvent.accountId(), accountId); QCOMPARE(voiceEvent.threadId(), threadId); QCOMPARE(voiceEvent.eventId(), eventId); QCOMPARE(voiceEvent.senderId(), senderId); QCOMPARE(voiceEvent.timestamp().toString(Qt::ISODate), timestamp.toString(Qt::ISODate)); QCOMPARE(voiceEvent.newEvent(), newEvent); QCOMPARE(voiceEvent.missed(), missed); QCOMPARE(voiceEvent.duration(), duration); } void VoiceEventTest::testFromNullProperties() { // just to make sure, test that calling ::fromProperties() on an empty map returns a null event History::Event nullEvent = History::VoiceEvent::fromProperties(QVariantMap()); QVERIFY(nullEvent.isNull()); QCOMPARE(nullEvent.type(), History::EventTypeNull); } void VoiceEventTest::testProperties_data() { QTest::addColumn("accountId"); QTest::addColumn("threadId"); QTest::addColumn("eventId"); QTest::addColumn("senderId"); QTest::addColumn("timestamp"); QTest::addColumn("newEvent"); QTest::addColumn("missed"); QTest::addColumn("duration"); QTest::newRow("unread missed call") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << true << true << QTime(0, 0, 0); QTest::newRow("missed call") << "testAccountId2" << "testThreadId2" << "testEventId2" << "testSenderId2" << QDateTime::currentDateTime().addDays(-5) << false << true << QTime(0, 0, 0); QTest::newRow("not missed call") << "testAccountId" << "testThreadId" << "testEventId" << "testSenderId" << QDateTime::currentDateTime().addDays(-10) << false << false << QTime(1, 2, 3); } void VoiceEventTest::testProperties() { QFETCH(QString, accountId); QFETCH(QString, threadId); QFETCH(QString, eventId); QFETCH(QString, senderId); QFETCH(QDateTime, timestamp); QFETCH(bool, newEvent); QFETCH(bool, missed); QFETCH(QTime, duration); History::VoiceEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent, missed, duration); // check that the values are properly set QVariantMap properties = event.properties(); QCOMPARE(properties[History::FieldAccountId].toString(), accountId); QCOMPARE(properties[History::FieldThreadId].toString(), threadId); QCOMPARE(properties[History::FieldEventId].toString(), eventId); QCOMPARE(properties[History::FieldSenderId].toString(), senderId); QCOMPARE(properties[History::FieldTimestamp].toString(), timestamp.toString(Qt::ISODate)); QCOMPARE(properties[History::FieldNewEvent].toBool(), newEvent); QCOMPARE(properties[History::FieldMissed].toBool(), missed); QCOMPARE(QTime(0,0).addSecs(properties[History::FieldDuration].toInt()), duration); } QTEST_MAIN(VoiceEventTest) #include "VoiceEventTest.moc" history-service-0.1+14.04.20140407/src/tests/SortTest.cpp0000644000015301777760000001336712320627220023342 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include #include #include "sort.h" Q_DECLARE_METATYPE(Qt::SortOrder) Q_DECLARE_METATYPE(Qt::CaseSensitivity) class SortTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testCreateNewSort_data(); void testCreateNewSort(); void testCopyConstructor(); void testSetSortProperties_data(); void testSetSortProperties(); void testFromProperties_data(); void testFromProperties(); void testFromNullProperties(); void testProperties_data(); void testProperties(); }; void SortTest::initTestCase() { qRegisterMetaType(); qRegisterMetaType(); } void SortTest::testCreateNewSort_data() { QTest::addColumn("sortField"); QTest::addColumn("sortOrder"); QTest::addColumn("caseSensitivity"); QTest::newRow("threadId field ascending case sensitive") << "threadId" << Qt::AscendingOrder << Qt::CaseSensitive; QTest::newRow("eventId field descending case insensitive") << "threadId" << Qt::DescendingOrder << Qt::CaseInsensitive; } void SortTest::testCreateNewSort() { QFETCH(QString, sortField); QFETCH(Qt::SortOrder, sortOrder); QFETCH(Qt::CaseSensitivity, caseSensitivity); History::Sort sort(sortField, sortOrder, caseSensitivity); QCOMPARE(sort.sortField(), sortField); QCOMPARE(sort.sortOrder(), sortOrder); QCOMPARE(sort.caseSensitivity(), caseSensitivity); } void SortTest::testCopyConstructor() { History::Sort sort(History::FieldCount, Qt::DescendingOrder, Qt::CaseSensitive); History::Sort otherSort(sort); QCOMPARE(otherSort.sortField(), sort.sortField()); QCOMPARE(otherSort.sortOrder(), sort.sortOrder()); QCOMPARE(otherSort.caseSensitivity(), sort.caseSensitivity()); } void SortTest::testSetSortProperties_data() { QTest::addColumn("sortField"); QTest::addColumn("sortOrder"); QTest::addColumn("caseSensitivity"); QTest::newRow("threadId field ascending case sensitive") << "threadId" << Qt::AscendingOrder << Qt::CaseSensitive; QTest::newRow("eventId field descending case insensitive") << "threadId" << Qt::DescendingOrder << Qt::CaseInsensitive; } void SortTest::testSetSortProperties() { QFETCH(QString, sortField); QFETCH(Qt::SortOrder, sortOrder); QFETCH(Qt::CaseSensitivity, caseSensitivity); History::Sort sort; sort.setSortField(sortField); QCOMPARE(sort.sortField(), sortField); sort.setSortOrder(sortOrder); QCOMPARE(sort.sortOrder(), sortOrder); sort.setCaseSensitivity(caseSensitivity); QCOMPARE(sort.caseSensitivity(), caseSensitivity); } void SortTest::testFromProperties_data() { QTest::addColumn("sortField"); QTest::addColumn("sortOrder"); QTest::addColumn("caseSensitivity"); QTest::newRow("threadId field ascending case sensitive") << "threadId" << Qt::AscendingOrder << Qt::CaseSensitive; QTest::newRow("eventId field descending case insensitive") << "threadId" << Qt::DescendingOrder << Qt::CaseInsensitive; } void SortTest::testFromProperties() { QFETCH(QString, sortField); QFETCH(Qt::SortOrder, sortOrder); QFETCH(Qt::CaseSensitivity, caseSensitivity); QVariantMap properties; properties[History::FieldSortField] = sortField; properties[History::FieldSortOrder] = (int) sortOrder; properties[History::FieldCaseSensitivity] = (int) caseSensitivity; History::Sort sort = History::Sort::fromProperties(properties); sort.setSortField(sortField); QCOMPARE(sort.sortField(), sortField); sort.setSortOrder(sortOrder); QCOMPARE(sort.sortOrder(), sortOrder); sort.setCaseSensitivity(caseSensitivity); QCOMPARE(sort.caseSensitivity(), caseSensitivity); } void SortTest::testFromNullProperties() { History::Sort nullSort; History::Sort sort = History::Sort::fromProperties(QVariantMap()); QCOMPARE(sort.sortField(), nullSort.sortField()); QCOMPARE(sort.sortOrder(), nullSort.sortOrder()); QCOMPARE(sort.caseSensitivity(), nullSort.caseSensitivity()); } void SortTest::testProperties_data() { QTest::addColumn("sortField"); QTest::addColumn("sortOrder"); QTest::addColumn("caseSensitivity"); QTest::newRow("threadId field ascending case sensitive") << "threadId" << Qt::AscendingOrder << Qt::CaseSensitive; QTest::newRow("eventId field descending case insensitive") << "threadId" << Qt::DescendingOrder << Qt::CaseInsensitive; } void SortTest::testProperties() { QFETCH(QString, sortField); QFETCH(Qt::SortOrder, sortOrder); QFETCH(Qt::CaseSensitivity, caseSensitivity); History::Sort sort(sortField, sortOrder, caseSensitivity); QVariantMap properties = sort.properties(); QCOMPARE(properties[History::FieldSortField].toString(), sortField); QCOMPARE(properties[History::FieldSortOrder].toInt(), (int) sortOrder); QCOMPARE(properties[History::FieldCaseSensitivity].toInt(), (int) caseSensitivity); } QTEST_MAIN(SortTest) #include "SortTest.moc" history-service-0.1+14.04.20140407/src/voiceevent_p.h0000644000015301777760000000304512320627220022534 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_VOICEEVENT_P_H #define HISTORY_VOICEEVENT_P_H #include "event_p.h" namespace History { class VoiceEventPrivate : public EventPrivate { public: VoiceEventPrivate(); VoiceEventPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theSender, const QDateTime &theTimestamp, bool theNewEvent, bool theMissed, const QTime &theDuration, const QStringList &theParticipants); ~VoiceEventPrivate(); bool missed; QTime duration; EventType type() const; QVariantMap properties() const; HISTORY_EVENT_DECLARE_CLONE(VoiceEvent) }; } #endif // HISTORY_VOICEEVENT_P_H history-service-0.1+14.04.20140407/src/manager.cpp0000644000015301777760000000767012320627220022023 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "manager.h" #include "manager_p.h" #include "managerdbus_p.h" #include "eventview.h" #include "intersectionfilter.h" #include "textevent.h" #include "thread.h" #include "threadview.h" #include "voiceevent.h" #include namespace History { // ------------- ManagerPrivate ------------------------------------------------ ManagerPrivate::ManagerPrivate() : dbus(new ManagerDBus()) { } ManagerPrivate::~ManagerPrivate() { } // ------------- Manager ------------------------------------------------------- Manager::Manager() : d_ptr(new ManagerPrivate()) { Q_D(Manager); // Propagate the signals from the event watcher connect(d->dbus.data(), SIGNAL(threadsAdded(History::Threads)), SIGNAL(threadsAdded(History::Threads))); connect(d->dbus.data(), SIGNAL(threadsModified(History::Threads)), SIGNAL(threadsModified(History::Threads))); connect(d->dbus.data(), SIGNAL(threadsRemoved(History::Threads)), SIGNAL(threadsRemoved(History::Threads))); connect(d->dbus.data(), SIGNAL(eventsAdded(History::Events)), SIGNAL(eventsAdded(History::Events))); connect(d->dbus.data(), SIGNAL(eventsModified(History::Events)), SIGNAL(eventsModified(History::Events))); connect(d->dbus.data(), SIGNAL(eventsRemoved(History::Events)), SIGNAL(eventsRemoved(History::Events))); } Manager::~Manager() { } Manager *Manager::instance() { static Manager *self = new Manager(); return self; } ThreadViewPtr Manager::queryThreads(EventType type, const Sort &sort, const Filter &filter) { return ThreadViewPtr(new ThreadView(type, sort, filter)); } EventViewPtr Manager::queryEvents(EventType type, const Sort &sort, const Filter &filter) { return EventViewPtr(new EventView(type, sort, filter)); } Event Manager::getSingleEvent(EventType type, const QString &accountId, const QString &threadId, const QString &eventId) { Q_D(Manager); Event event = d->dbus->getSingleEvent(type, accountId, threadId, eventId); return event; } Thread Manager::threadForParticipants(const QString &accountId, EventType type, const QStringList &participants, MatchFlags matchFlags, bool create) { Q_D(Manager); return d->dbus->threadForParticipants(accountId, type, participants, matchFlags, create); } Thread Manager::getSingleThread(EventType type, const QString &accountId, const QString &threadId) { Q_D(Manager); Thread thread = d->dbus->getSingleThread(type, accountId, threadId); return thread; } bool Manager::writeEvents(const Events &events) { Q_D(Manager); return d->dbus->writeEvents(events); } bool Manager::removeThreads(const Threads &threads) { Q_D(Manager); return d->dbus->removeThreads(threads); } bool Manager::removeEvents(const Events &events) { Q_D(Manager); return d->dbus->removeEvents(events); } } history-service-0.1+14.04.20140407/src/texteventattachment.cpp0000644000015301777760000001356512320627220024510 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "texteventattachment.h" #include "texteventattachment_p.h" namespace History { // ------------- TextEventAttachmentPrivate ------------------------------------------------ TextEventAttachmentPrivate::TextEventAttachmentPrivate() { } TextEventAttachmentPrivate::TextEventAttachmentPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theAttachmentId, const QString &theContentType, const QString &theFilePath, const AttachmentFlags &theStatus) : accountId(theAccountId), threadId(theThreadId), eventId(theEventId), attachmentId(theAttachmentId), contentType(theContentType), filePath(theFilePath), status(theStatus) { } TextEventAttachmentPrivate::~TextEventAttachmentPrivate() { } // ------------- TextEventAttachment ------------------------------------------------------ /*! * \class TextEventAttachment * * \brief The TextEventAttachment class provides a way to store a single attachment * belonging to a text event. */ TextEventAttachment::TextEventAttachment() : d_ptr(new TextEventAttachmentPrivate()) { } TextEventAttachment::TextEventAttachment(const QString &accountId, const QString &threadId, const QString &eventId, const QString &attachmentId, const QString &contentType, const QString &filePath, const AttachmentFlags &status) : d_ptr(new TextEventAttachmentPrivate(accountId, threadId, eventId, attachmentId, contentType, filePath, status)) { } TextEventAttachment::TextEventAttachment(const TextEventAttachment &other) : d_ptr(new TextEventAttachmentPrivate(*other.d_ptr)) { } TextEventAttachment::~TextEventAttachment() { } TextEventAttachment& TextEventAttachment::operator=(const TextEventAttachment &other) { d_ptr = QSharedPointer(new TextEventAttachmentPrivate(*other.d_ptr)); } /*! * \brief Returns the account ID this attachment belongs to. */ QString TextEventAttachment::accountId() const { Q_D(const TextEventAttachment); return d->accountId; } /*! * \brief Returns the thread ID this attachment belongs to. */ QString TextEventAttachment::threadId() const { Q_D(const TextEventAttachment); return d->threadId; } /*! * \brief Returns the event ID this attachment belongs to. */ QString TextEventAttachment::eventId() const { Q_D(const TextEventAttachment); return d->eventId; } /*! * \brief Returns the attachment ID */ QString TextEventAttachment::attachmentId() const { Q_D(const TextEventAttachment); return d->attachmentId; } /*! * \brief Returns the content type of this attachment */ QString TextEventAttachment::contentType() const { Q_D(const TextEventAttachment); return d->contentType; } /*! * \brief Returns the file path of this attachment */ QString TextEventAttachment::filePath() const { Q_D(const TextEventAttachment); return d->filePath; } /*! * \brief Returns the status of this attachment */ AttachmentFlags TextEventAttachment::status() const { Q_D(const TextEventAttachment); return d->status; } QVariantMap TextEventAttachment::properties() const { Q_D(const TextEventAttachment); QVariantMap map; map[FieldAccountId] = d->accountId; map[FieldThreadId] = d->threadId; map[FieldEventId] = d->eventId; map[FieldAttachmentId] = d->attachmentId; map[FieldContentType] = d->contentType; map[FieldFilePath] = d->filePath; map[FieldStatus] = (int)d->status; return map; } TextEventAttachment TextEventAttachment::fromProperties(const QVariantMap &properties) { TextEventAttachment attachment; if (properties.isEmpty()) { return attachment; } attachment = TextEventAttachment(properties[FieldAccountId].toString(), properties[FieldThreadId].toString(), properties[FieldEventId].toString(), properties[FieldAttachmentId].toString(), properties[FieldContentType].toString(), properties[FieldFilePath].toString(), (History::AttachmentFlags)properties[FieldStatus].toInt()); return attachment; } bool TextEventAttachment::isNull() const { Q_D(const TextEventAttachment); return d->accountId.isNull() && d->threadId.isNull() && d->eventId.isNull() && d->attachmentId.isNull(); } bool TextEventAttachment::operator==(const TextEventAttachment &other) { Q_D(TextEventAttachment); if (d->accountId != other.d_ptr->accountId) { return false; } if (d->threadId != other.d_ptr->threadId) { return false; } if (d->eventId != other.d_ptr->eventId) { return false; } if (d->attachmentId != other.d_ptr->attachmentId) { return false; } return true; } } history-service-0.1+14.04.20140407/src/intersectionfilter.cpp0000644000015301777760000001022212320627220024310 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "intersectionfilter.h" #include "intersectionfilter_p.h" #include #include #include namespace History { IntersectionFilterPrivate::IntersectionFilterPrivate() { } IntersectionFilterPrivate::~IntersectionFilterPrivate() { } bool IntersectionFilterPrivate::match(const QVariantMap properties) const { // return true only if all filters match Q_FOREACH(const Filter &filter, filters) { if (!filter.match(properties)) { return false; } } return true; } bool IntersectionFilterPrivate::isValid() const { // FIXME: maybe we should check if at least one of the inner filters are valid? return !filters.isEmpty(); } QVariantMap IntersectionFilterPrivate::properties() const { QVariantMap map; if (!isValid()) { return map; } QVariantList filterList; Q_FOREACH(const Filter &filter, filters) { filterList << filter.properties(); } map[FieldFilters] = filterList; map[FieldFilterType] = (int) History::FilterTypeIntersection; return map; } QString IntersectionFilterPrivate::toString(const QString &propertyPrefix) const { if (filters.isEmpty()) { return QString::null; } else if (filters.count() == 1) { return filters.first().toString(); } QStringList output; // wrap each filter string around parenthesis Q_FOREACH(const Filter &filter, filters) { QString value = filter.toString(propertyPrefix); if (!value.isEmpty()) { output << QString("(%1)").arg(value); } } return output.join(" AND "); } HISTORY_FILTER_DEFINE_COPY(IntersectionFilter, FilterTypeIntersection) IntersectionFilter::IntersectionFilter() : Filter(*new IntersectionFilterPrivate()) { } IntersectionFilter::~IntersectionFilter() { } void IntersectionFilter::setFilters(const Filters &filters) { Q_D(IntersectionFilter); d->filters = filters; } void IntersectionFilter::prepend(const Filter &filter) { Q_D(IntersectionFilter); d->filters.prepend(filter); } void IntersectionFilter::append(const Filter &filter) { Q_D(IntersectionFilter); d->filters.append(filter); } void IntersectionFilter::clear() { Q_D(IntersectionFilter); d->filters.clear(); } Filters IntersectionFilter::filters() const { Q_D(const IntersectionFilter); return d->filters; } Filter IntersectionFilter::fromProperties(const QVariantMap &properties) { IntersectionFilter filter; if (properties.isEmpty()) { return filter; } QVariant filters = properties[FieldFilters]; QVariantList filterList; // when the filter travels through DBus, it arrives marshalled into QDBusArguments. // cover that case too. if (filters.canConvert()) { QDBusArgument argument = filters.value(); QVariantList list; argument >> list; // and cast also the inner filters Q_FOREACH(const QVariant &var, list) { QDBusArgument arg = var.value(); QVariantMap map; arg >> map; filterList.append(map); } } else { filterList = filters.toList(); } Q_FOREACH(const QVariant &props, filterList) { Filter innerFilter = History::Filter::fromProperties(props.toMap()); if (innerFilter.isValid()) { filter.append(innerFilter); } } return filter; } } history-service-0.1+14.04.20140407/src/event.h0000644000015301777760000000325012320627220021165 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_EVENT_H #define HISTORY_EVENT_H #include #include #include #include #include #include "types.h" namespace History { class EventPrivate; class Event { Q_DECLARE_PRIVATE(Event) public: explicit Event(); Event(const Event &other); virtual ~Event(); Event& operator=(const Event &other); QString accountId() const; QString threadId() const; QString eventId() const; QString senderId() const; QDateTime timestamp() const; bool newEvent() const; void setNewEvent(bool value); EventType type() const; QStringList participants() const; QVariantMap properties() const; bool isNull() const; bool operator==(const Event &other) const; bool operator<(const Event &other) const; protected: Event(EventPrivate &p); QSharedPointer d_ptr; }; typedef QList Events; } #endif history-service-0.1+14.04.20140407/src/voiceevent.cpp0000644000015301777760000000735412320627220022557 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "voiceevent.h" #include "voiceevent_p.h" namespace History { // ------------- VoiceEventPrivate ------------------------------------------------ VoiceEventPrivate::VoiceEventPrivate() { } VoiceEventPrivate::VoiceEventPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theSender, const QDateTime &theTimestamp, bool theNewEvent, bool theMissed, const QTime &theDuration, const QStringList &theParticipants) : EventPrivate(theAccountId, theThreadId, theEventId, theSender, theTimestamp, theNewEvent, theParticipants), missed(theMissed), duration(theDuration) { } VoiceEventPrivate::~VoiceEventPrivate() { } EventType VoiceEventPrivate::type() const { return EventTypeVoice; } QVariantMap VoiceEventPrivate::properties() const { QVariantMap map = EventPrivate::properties(); map[FieldMissed] = missed; map[FieldDuration] = QTime(0,0,0,0).secsTo(duration); return map; } // ------------- VoiceEvent ------------------------------------------------------- HISTORY_EVENT_DEFINE_COPY(VoiceEvent, EventTypeVoice) VoiceEvent::VoiceEvent() : Event(*new VoiceEventPrivate()) { } VoiceEvent::VoiceEvent(const QString &accountId, const QString &threadId, const QString &eventId, const QString &sender, const QDateTime ×tamp, bool newEvent, bool missed, const QTime &duration, const QStringList &participants) : Event(*new VoiceEventPrivate(accountId, threadId, eventId, sender, timestamp, newEvent, missed, duration, participants)) { } VoiceEvent::~VoiceEvent() { } bool VoiceEvent::missed() const { Q_D(const VoiceEvent); return d->missed; } QTime VoiceEvent::duration() const { Q_D(const VoiceEvent); return d->duration; } Event VoiceEvent::fromProperties(const QVariantMap &properties) { Event event; if (properties.isEmpty()) { return event; } QString accountId = properties[FieldAccountId].toString(); QString threadId = properties[FieldThreadId].toString(); QString eventId = properties[FieldEventId].toString(); QString senderId = properties[FieldSenderId].toString(); QDateTime timestamp = QDateTime::fromString(properties[FieldTimestamp].toString(), Qt::ISODate); bool newEvent = properties[FieldNewEvent].toBool(); QStringList participants = properties[FieldParticipants].toStringList(); bool missed = properties[FieldMissed].toBool(); QTime duration = QTime(0,0,0).addSecs(properties[FieldDuration].toInt()); event = VoiceEvent(accountId, threadId, eventId, senderId, timestamp, newEvent, missed, duration, participants); return event; } } history-service-0.1+14.04.20140407/src/plugineventview_p.h0000644000015301777760000000202512320627220023615 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef PLUGINEVENTVIEW_P_H #define PLUGINEVENTVIEW_P_H #include class EventViewAdaptor; namespace History { class PluginEventViewPrivate { public: PluginEventViewPrivate(); EventViewAdaptor *adaptor; QString objectPath; }; } #endif // PLUGINEVENTVIEW_P_H history-service-0.1+14.04.20140407/src/threadview_p.h0000644000015301777760000000313512320627220022527 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef THREADVIEW_P_H #define THREADVIEW_P_H #include "types.h" #include namespace History { class ThreadView; class ThreadViewPrivate { Q_DECLARE_PUBLIC(ThreadView) public: ThreadViewPrivate(History::EventType theType, const History::Sort &theSort, const History::Filter &theFilter); EventType type; Sort sort; Filter filter; QString objectPath; bool valid; QDBusInterface *dbus; Threads filteredThreads(const Threads &threads); // private slots void _d_threadsAdded(const History::Threads &threads); void _d_threadsModified(const History::Threads &threads); void _d_threadsRemoved(const History::Threads &threads); ThreadView *q_ptr; }; } #endif // THREADVIEW_P_H history-service-0.1+14.04.20140407/src/TextEventAttachment0000644000015301777760000000004112320627220023550 0ustar pbusernogroup00000000000000#include "texteventattachment.h" history-service-0.1+14.04.20140407/src/event_p.h0000644000015301777760000000466712320627220021521 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_EVENT_P_H #define HISTORY_EVENT_P_H #include #include #include #include "types.h" #define HISTORY_EVENT_DECLARE_CLONE(Class) \ virtual EventPrivate *clone() { return new Class##Private(*this); } #define HISTORY_EVENT_DEFINE_COPY(Class, Type) \ Class::Class(const Event &other) { \ if (other.type() == Type) { d_ptr = QSharedPointer(reinterpret_cast(EventPrivate::getD(other)->clone())); } \ else { d_ptr = QSharedPointer(new Class##Private()); } \ } \ Class& Class::operator=(const Event &other) { \ if (other.type() == Type) { d_ptr = QSharedPointer(reinterpret_cast(EventPrivate::getD(other)->clone())); } \ return *this; \ } namespace History { class EventPrivate { public: EventPrivate(); EventPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theSenderId, const QDateTime &theTimestamp, bool theNewEvent, const QStringList &theParticipants); virtual ~EventPrivate(); virtual EventType type() const { return EventTypeNull; } virtual QVariantMap properties() const; QString accountId; QString threadId; QString eventId; QString senderId; QString receiver; QDateTime timestamp; bool newEvent; QStringList participants; static const QSharedPointer& getD(const Event& other) { return other.d_ptr; } HISTORY_EVENT_DECLARE_CLONE(Event) }; } #endif // HISTORY_EVENT_P_H history-service-0.1+14.04.20140407/src/textevent_p.h0000644000015301777760000000365712320627220022424 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_TEXTEVENT_P_H #define HISTORY_TEXTEVENT_P_H #include "event_p.h" #include "types.h" namespace History { class TextEvent; class TextEventPrivate : public EventPrivate { public: TextEventPrivate(); TextEventPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theSender, const QDateTime &theTimestamp, bool theNewEvent, const QString &theMessage, MessageType theMessageType, MessageStatus theMessageStatus, const QDateTime &theReadTimestamp, const QString &theSubject, const TextEventAttachments &theAttachments, const QStringList &theParticipants); ~TextEventPrivate(); QString message; MessageType messageType; MessageStatus messageStatus; QDateTime readTimestamp; QString subject; TextEventAttachments attachments; EventType type() const; QVariantMap properties() const; HISTORY_EVENT_DECLARE_CLONE(TextEvent) }; } #endif // HISTORY_TEXTEVENT_P_H history-service-0.1+14.04.20140407/src/filter_p.h0000644000015301777760000000464312320627220021657 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_FILTER_P_H #define HISTORY_FILTER_P_H #include #include #include #include "types.h" #define HISTORY_FILTER_DECLARE_CLONE(Class) \ virtual FilterPrivate *clone() { return new Class##Private(*this); } #define HISTORY_FILTER_DEFINE_COPY(Class, Type) \ Class::Class(const Filter &other) { \ if (other.type() == Type) { d_ptr = QSharedPointer(reinterpret_cast(FilterPrivate::getD(other)->clone())); } \ else { d_ptr = QSharedPointer(new Class##Private()); } \ } \ Class& Class::operator=(const Filter &other) { \ if (other.type() == Type) { d_ptr = QSharedPointer(reinterpret_cast(FilterPrivate::getD(other)->clone())); } \ return *this; \ } namespace History { class FilterPrivate { public: FilterPrivate(); FilterPrivate(const QString &theFilterProperty, const QVariant &theFilterValue, MatchFlags theMatchFlags); virtual ~FilterPrivate(); QString filterProperty; QVariant filterValue; MatchFlags matchFlags; static const QSharedPointer& getD(const Filter& other) { return other.d_ptr; } virtual QString toString(const QString &propertyPrefix = QString::null) const; virtual bool match(const QVariantMap properties) const; virtual FilterType type() const { return History::FilterTypeStandard; } virtual bool isValid() const { return (!filterProperty.isNull()) && (!filterValue.isNull()); } virtual QVariantMap properties() const; HISTORY_FILTER_DECLARE_CLONE(Filter) }; } #endif // HISTORY_FILTER_P_H history-service-0.1+14.04.20140407/src/PluginThreadView.xml0000644000015301777760000000265512320627220023646 0ustar pbusernogroup00000000000000 An interface to the history service ThreadView object. history-service-0.1+14.04.20140407/src/thread_p.h0000644000015301777760000000274012320627220021635 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_THREAD_P_H #define HISTORY_THREAD_P_H #include #include "types.h" namespace History { class Thread; class ThreadPrivate { public: explicit ThreadPrivate(); ThreadPrivate(const QString &theAccountId, const QString &theThreadId, EventType theType, const QStringList &theParticipants, const Event &theLastEvent, int theCount, int theUnreadCount); virtual ~ThreadPrivate(); QString accountId; QString threadId; QStringList participants; EventType type; Event lastEvent; int count; int unreadCount; }; } #endif // HISTORY_THREAD_P_H history-service-0.1+14.04.20140407/src/managerdbus.cpp0000644000015301777760000001562412320627220022677 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "managerdbus_p.h" #include "event.h" #include "manager.h" #include "thread.h" #include "textevent.h" #include "voiceevent.h" #include #include Q_DECLARE_METATYPE(QList< QVariantMap >) namespace History { ManagerDBus::ManagerDBus(QObject *parent) : QObject(parent), mAdaptor(0), mInterface(DBusService, DBusObjectPath, DBusInterface) { qDBusRegisterMetaType >(); qRegisterMetaType >(); // listen for signals coming from the bus QDBusConnection connection = QDBusConnection::sessionBus(); connection.connect(DBusService, DBusObjectPath, DBusInterface, "ThreadsAdded", this, SLOT(onThreadsAdded(QList))); connection.connect(DBusService, DBusObjectPath, DBusInterface, "ThreadsModified", this, SLOT(onThreadsModified(QList))); connection.connect(DBusService, DBusObjectPath, DBusInterface, "ThreadsRemoved", this, SLOT(onThreadsRemoved(QList))); connection.connect(DBusService, DBusObjectPath, DBusInterface, "EventsAdded", this, SLOT(onEventsAdded(QList))); connection.connect(DBusService, DBusObjectPath, DBusInterface, "EventsModified", this, SLOT(onEventsModified(QList))); connection.connect(DBusService, DBusObjectPath, DBusInterface, "EventsRemoved", this, SLOT(onEventsRemoved(QList))); } Thread ManagerDBus::threadForParticipants(const QString &accountId, EventType type, const QStringList &participants, MatchFlags matchFlags, bool create) { Thread thread; // FIXME: move to async call if possible QDBusReply reply = mInterface.call("ThreadForParticipants", accountId, (int) type, participants, (int)matchFlags, create); if (reply.isValid()) { QVariantMap properties = reply.value(); thread = Thread::fromProperties(properties); } return thread; } bool ManagerDBus::writeEvents(const Events &events) { QList eventMap = eventsToProperties(events); if (eventMap.isEmpty()) { return false; } QDBusReply reply = mInterface.call("WriteEvents", QVariant::fromValue(eventMap)); if (!reply.isValid()) { return false; } return reply.value(); } bool ManagerDBus::removeThreads(const Threads &threads) { QList threadMap = threadsToProperties(threads); if (threadMap.isEmpty()) { return false; } QDBusReply reply = mInterface.call("RemoveThreads", QVariant::fromValue(threadMap)); if (!reply.isValid()) { return false; } return reply.value(); } bool ManagerDBus::removeEvents(const Events &events) { QList eventMap = eventsToProperties(events); if (eventMap.isEmpty()) { return false; } QDBusReply reply = mInterface.call("RemoveEvents", QVariant::fromValue(eventMap)); if (!reply.isValid()) { return false; } return reply.value(); } Thread ManagerDBus::getSingleThread(EventType type, const QString &accountId, const QString &threadId) { Thread thread; QDBusReply reply = mInterface.call("GetSingleThread", (int)type, accountId, threadId); if (!reply.isValid()) { return thread; } thread = Thread::fromProperties(reply.value()); return thread; } Event ManagerDBus::getSingleEvent(EventType type, const QString &accountId, const QString &threadId, const QString &eventId) { Event event; QDBusReply reply = mInterface.call("GetSingleEvent", (int)type, accountId, threadId, eventId); if (!reply.isValid()) { return event; } event = eventFromProperties(reply.value()); return event; } void ManagerDBus::onThreadsAdded(const QList &threads) { Q_EMIT threadsAdded(threadsFromProperties(threads)); } void ManagerDBus::onThreadsModified(const QList &threads) { Q_EMIT threadsModified(threadsFromProperties(threads)); } void ManagerDBus::onThreadsRemoved(const QList &threads) { Q_EMIT threadsRemoved(threadsFromProperties(threads)); } void ManagerDBus::onEventsAdded(const QList &events) { Q_EMIT eventsAdded(eventsFromProperties(events)); } void ManagerDBus::onEventsModified(const QList &events) { Q_EMIT eventsModified(eventsFromProperties(events)); } void ManagerDBus::onEventsRemoved(const QList &events) { Q_EMIT eventsRemoved(eventsFromProperties(events)); } Threads ManagerDBus::threadsFromProperties(const QList &threadsProperties) { Threads threads; Q_FOREACH(const QVariantMap &map, threadsProperties) { Thread thread = Thread::fromProperties(map); if (!thread.isNull()) { threads << thread; } } return threads; } QList ManagerDBus::threadsToProperties(const Threads &threads) { QList threadsPropertyMap; Q_FOREACH(const Thread &thread, threads) { threadsPropertyMap << thread.properties(); } return threadsPropertyMap; } Event ManagerDBus::eventFromProperties(const QVariantMap &properties) { EventType type = (EventType)properties[FieldType].toInt(); switch (type) { case EventTypeText: return TextEvent::fromProperties(properties); case EventTypeVoice: return VoiceEvent::fromProperties(properties); } } Events ManagerDBus::eventsFromProperties(const QList &eventsProperties) { Events events; Q_FOREACH(const QVariantMap &map, eventsProperties) { events << eventFromProperties(map); } return events; } QList ManagerDBus::eventsToProperties(const Events &events) { QList eventsPropertyMap; Q_FOREACH(const Event &event, events) { eventsPropertyMap << event.properties(); } return eventsPropertyMap; } } history-service-0.1+14.04.20140407/src/phonenumberutils_p.h0000644000015301777760000001727312320627220024000 0ustar pbusernogroup00000000000000/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Original source code available at: http://androidxref.com/4.0.4/xref/frameworks/base/telephony/java/android/telephony/PhoneNumberUtils.java */ #ifndef PHONENUMBERUTILS_H #define PHONENUMBERUTILS_H #include #include namespace PhoneNumberUtils { /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE */ bool isNonSeparator(char c) { return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N' || c == ';' || c == ','; } /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */ bool isDialable(char c) { return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N'; } /** True if c is ISO-LATIN characters 0-9 */ bool isISODigit (char c) { return c >= '0' && c <= '9'; } /** or -1 if both are negative */ int minPositive (int a, int b) { if (a >= 0 && b >= 0) { return (a < b) ? a : b; } else if (a >= 0) { /* && b < 0 */ return a; } else if (b >= 0) { /* && a < 0 */ return b; } else { /* a < 0 && b < 0 */ return -1; } } /** index of the last character of the network portion * (eg anything after is a post-dial string) */ int indexOfLastNetworkChar(const QString &a) { int pIndex, wIndex; int origLength; int trimIndex; origLength = a.length(); pIndex = a.indexOf(','); wIndex = a.indexOf(';'); trimIndex = minPositive(pIndex, wIndex); if (trimIndex < 0) { return origLength - 1; } else { return trimIndex - 1; } } /** all of a up to len must be an international prefix or * separators/non-dialing digits */ bool matchIntlPrefix(const QString &a, int len) { /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */ /* 0 1 2 3 45 */ int state = 0; for (int i = 0 ; i < len ; i++) { char c = a.at(i).toLatin1(); switch (state) { case 0: if (c == '+') state = 1; else if (c == '0') state = 2; else if (isNonSeparator(c)) return false; break; case 2: if (c == '0') state = 3; else if (c == '1') state = 4; else if (isNonSeparator(c)) return false; break; case 4: if (c == '1') state = 5; else if (isNonSeparator(c)) return false; break; default: if (isNonSeparator(c)) return false; break; } } return state == 1 || state == 3 || state == 5; } /** all of 'a' up to len must match non-US trunk prefix ('0') */ bool matchTrunkPrefix(const QString &a, int len) { bool found; found = false; for (int i = 0 ; i < len ; i++) { char c = a.at(i).toLatin1(); if (c == '0' && !found) { found = true; } else if (isNonSeparator(c)) { return false; } } return found; } /** all of 'a' up to len must be a (+|00|011)country code) * We're fast and loose with the country code. Any \d{1,3} matches */ bool matchIntlPrefixAndCC(const QString &a, int len) { /* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */ /* 0 1 2 3 45 6 7 8 */ int state = 0; for (int i = 0 ; i < len ; i++ ) { char c = a.at(i).toLatin1(); switch (state) { case 0: if (c == '+') state = 1; else if (c == '0') state = 2; else if (isNonSeparator(c)) return false; break; case 2: if (c == '0') state = 3; else if (c == '1') state = 4; else if (isNonSeparator(c)) return false; break; case 4: if (c == '1') state = 5; else if (isNonSeparator(c)) return false; break; case 1: case 3: case 5: if (isISODigit(c)) state = 6; else if (isNonSeparator(c)) return false; break; case 6: case 7: if (isISODigit(c)) state++; else if (isNonSeparator(c)) return false; break; default: if (isNonSeparator(c)) return false; } } return state == 6 || state == 7 || state == 8; } /** * Compare phone numbers a and b, return true if they're identical * enough for caller ID purposes. * * - Compares from right to left * - requires MIN_MATCH (7) characters to match * - handles common trunk prefixes and international prefixes * (basically, everything except the Russian trunk prefix) * * Note that this method does not return false even when the two phone numbers * are not exactly same; rather; we can call this method "similar()", not "equals()". * * @hide */ bool compareLoosely(const QString &a, const QString &b) { int ia, ib; int matched; int numNonDialableCharsInA = 0; int numNonDialableCharsInB = 0; if (a.length() == 0 || b.length() == 0) { return false; } if (a == b) { return true; } ia = indexOfLastNetworkChar (a); ib = indexOfLastNetworkChar (b); matched = 0; while (ia >= 0 && ib >=0) { char ca, cb; bool skipCmp = false; ca = a.at(ia).toLatin1(); if (!isDialable(ca)) { ia--; skipCmp = true; numNonDialableCharsInA++; } cb = b.at(ib).toLatin1(); if (!isDialable(cb)) { ib--; skipCmp = true; numNonDialableCharsInB++; } if (!skipCmp) { if (cb != ca && ca != 'N' && cb != 'N') { break; } ia--; ib--; matched++; } } if (matched < 7) { int effectiveALen = a.length() - numNonDialableCharsInA; int effectiveBLen = b.length() - numNonDialableCharsInB; // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH, // treat them as equal (i.e. 404-04 and 40404) if (effectiveALen == effectiveBLen && effectiveALen == matched) { return true; } return false; } // At least one string has matched completely; if (matched >= 7 && (ia < 0 || ib < 0)) { return true; } /* * Now, what remains must be one of the following for a * match: * * - a '+' on one and a '00' or a '011' on the other * - a '0' on one and a (+,00) on the other * (for this, a '0' and a '00' prefix would have succeeded above) */ if (matchIntlPrefix(a, ia + 1) && matchIntlPrefix (b, ib +1) ) { return true; } if (matchTrunkPrefix(a, ia + 1) && matchIntlPrefixAndCC(b, ib +1) ) { return true; } if (matchTrunkPrefix(b, ib + 1) && matchIntlPrefixAndCC(a, ia +1) ) { return true; } return false; } } #endif history-service-0.1+14.04.20140407/src/pluginthreadview.cpp0000644000015301777760000000373512320627220023770 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "pluginthreadview.h" #include "pluginthreadview_p.h" #include "pluginthreadviewadaptor.h" #include "types.h" #include #include Q_DECLARE_METATYPE(QList< QVariantMap >) namespace History { PluginThreadViewPrivate::PluginThreadViewPrivate() : adaptor(0) { } PluginThreadView::PluginThreadView(QObject *parent) : QObject(parent), d_ptr(new PluginThreadViewPrivate()) { Q_D(PluginThreadView); qDBusRegisterMetaType >(); d->adaptor = new ThreadViewAdaptor(this); QString id = QString("threadview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz")); d->objectPath = QString("%1/%2").arg(History::DBusObjectPath, id); QDBusConnection::sessionBus().registerObject(d->objectPath, this); } PluginThreadView::~PluginThreadView() { Q_D(PluginThreadView); QDBusConnection::sessionBus().unregisterObject(d->objectPath); } void PluginThreadView::Destroy() { qDebug() << __PRETTY_FUNCTION__; Q_D(PluginThreadView); deleteLater(); } bool PluginThreadView::IsValid() const { return true; } QString PluginThreadView::objectPath() const { Q_D(const PluginThreadView); return d->objectPath; } } history-service-0.1+14.04.20140407/src/VoiceEvent0000644000015301777760000000003012320627220021656 0ustar pbusernogroup00000000000000#include "voiceevent.h" history-service-0.1+14.04.20140407/src/ThreadView0000644000015301777760000000003012320627220021651 0ustar pbusernogroup00000000000000#include "threadview.h" history-service-0.1+14.04.20140407/src/sort.cpp0000644000015301777760000000535412320627220021375 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "sort.h" #include "sort_p.h" namespace History { // ------------- SortPrivate ------------------------------------------------ SortPrivate::SortPrivate(const QString &theSortField, Qt::SortOrder theSortOrder, Qt::CaseSensitivity theCaseSensitivity) : sortField(theSortField), sortOrder(theSortOrder), caseSensitivity(theCaseSensitivity) { } SortPrivate::~SortPrivate() { } // ------------- Sort ------------------------------------------------------- Sort::Sort(const QString &sortField, Qt::SortOrder sortOrder, Qt::CaseSensitivity caseSensitivity) : d_ptr(new SortPrivate(sortField, sortOrder, caseSensitivity)) { } Sort::Sort(const Sort &other) : d_ptr(new SortPrivate(*other.d_ptr)) { } Sort::~Sort() { } QString Sort::sortField() const { Q_D(const Sort); return d->sortField; } void Sort::setSortField(const QString &value) { Q_D(Sort); d->sortField = value; } Qt::SortOrder Sort::sortOrder() const { Q_D(const Sort); return d->sortOrder; } void Sort::setSortOrder(Qt::SortOrder value) { Q_D(Sort); d->sortOrder = value; } Qt::CaseSensitivity Sort::caseSensitivity() const { Q_D(const Sort); return d->caseSensitivity; } void Sort::setCaseSensitivity(Qt::CaseSensitivity value) { Q_D(Sort); d->caseSensitivity = value; } QVariantMap Sort::properties() const { Q_D(const Sort); QVariantMap map; map[FieldSortField] = d->sortField; map[FieldSortOrder] = (int)d->sortOrder; map[FieldCaseSensitivity] = (int)d->caseSensitivity; return map; } Sort Sort::fromProperties(const QVariantMap &properties) { Sort sort; if (properties.isEmpty()) { return sort; } sort = Sort(properties[FieldSortField].toString(), (Qt::SortOrder) properties[FieldSortOrder].toInt(), (Qt::CaseSensitivity) properties[FieldCaseSensitivity].toInt()); return sort; } } history-service-0.1+14.04.20140407/src/Sort0000644000015301777760000000002212320627220020537 0ustar pbusernogroup00000000000000#include "sort.h" history-service-0.1+14.04.20140407/src/thread.cpp0000644000015301777760000001154112320627220021650 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "thread.h" #include "thread_p.h" #include "textevent.h" #include "voiceevent.h" namespace History { // ------------- ThreadPrivate ------------------------------------------------ ThreadPrivate::ThreadPrivate() { } ThreadPrivate::ThreadPrivate(const QString &theAccountId, const QString &theThreadId, EventType theType, const QStringList &theParticipants, const Event &theLastEvent, int theCount, int theUnreadCount) : accountId(theAccountId), threadId(theThreadId), type(theType), participants(theParticipants), lastEvent(theLastEvent), count(theCount), unreadCount(theUnreadCount) { } ThreadPrivate::~ThreadPrivate() { } // ------------- Thread ------------------------------------------------------ Thread::Thread() : d_ptr(new ThreadPrivate()) { } Thread::Thread(const QString &accountId, const QString &threadId, EventType type, const QStringList &participants, const Event &lastEvent, int count, int unreadCount) : d_ptr(new ThreadPrivate(accountId, threadId, type, participants, lastEvent, count, unreadCount)) { } Thread::Thread(const Thread &other) : d_ptr(new ThreadPrivate(*other.d_ptr)) { } Thread::~Thread() { } Thread &Thread::operator=(const Thread &other) { if (&other == this) { return *this; } d_ptr = QSharedPointer(new ThreadPrivate(*other.d_ptr)); return *this; } QString Thread::accountId() const { Q_D(const Thread); return d->accountId; } QString Thread::threadId() const { Q_D(const Thread); return d->threadId; } EventType Thread::type() const { Q_D(const Thread); return d->type; } QStringList Thread::participants() const { Q_D(const Thread); return d->participants; } Event Thread::lastEvent() const { Q_D(const Thread); return d->lastEvent; } int Thread::count() const { Q_D(const Thread); return d->count; } int Thread::unreadCount() const { Q_D(const Thread); return d->unreadCount; } bool Thread::isNull() const { Q_D(const Thread); return d->accountId.isNull() && d->threadId.isNull() && d->participants.isEmpty(); } bool Thread::operator ==(const Thread &other) const { Q_D(const Thread); if (d->type != other.d_ptr->type) { return false; } if (d->accountId != other.d_ptr->accountId) { return false; } if (d->threadId != other.d_ptr->threadId) { return false; } return true; } bool Thread::operator<(const Thread &other) const { QString selfData = QString::number(type()) + accountId() + threadId(); QString otherData = QString::number(other.type()) + other.accountId() + other.threadId(); return selfData < otherData; } QVariantMap Thread::properties() const { Q_D(const Thread); QVariantMap map; map[FieldAccountId] = d->accountId; map[FieldThreadId] = d->threadId; map[FieldType] = d->type; map[FieldParticipants] = d->participants; map[FieldCount] = d->count; map[FieldUnreadCount] = d->unreadCount; return map; } Thread Thread::fromProperties(const QVariantMap &properties) { Thread thread; if (properties.isEmpty()) { return thread; } // FIXME: save the rest of the data QString accountId = properties[FieldAccountId].toString(); QString threadId = properties[FieldThreadId].toString(); EventType type = (EventType) properties[FieldType].toInt(); QStringList participants = properties[FieldParticipants].toStringList(); int count = properties[FieldCount].toInt(); int unreadCount = properties[FieldUnreadCount].toInt(); Event event; switch (type) { case EventTypeText: event = TextEvent::fromProperties(properties); break; case EventTypeVoice: event = VoiceEvent::fromProperties(properties); break; } return Thread(accountId, threadId, type, participants, event, count, unreadCount); } } history-service-0.1+14.04.20140407/src/Thread0000644000015301777760000000002412320627220021021 0ustar pbusernogroup00000000000000#include "thread.h" history-service-0.1+14.04.20140407/src/threadview.cpp0000644000015301777760000001115112320627220022540 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "threadview.h" #include "threadview_p.h" #include "filter.h" #include "manager.h" #include "sort.h" #include "thread.h" #include #include #include namespace History { // ------------- ThreadViewPrivate ------------------------------------------------ ThreadViewPrivate::ThreadViewPrivate(History::EventType theType, const History::Sort &theSort, const History::Filter &theFilter) : type(theType), sort(theSort), filter(theFilter), valid(true), dbus(0) { } Threads ThreadViewPrivate::filteredThreads(const Threads &threads) { bool filterNull = filter.isNull(); Threads filtered; Q_FOREACH(const Thread &thread, threads) { if (thread.type() != type) { continue; } if (filterNull || filter.match(thread.properties())) { filtered << thread; } } return filtered; } void ThreadViewPrivate::_d_threadsAdded(const History::Threads &threads) { Q_Q(ThreadView); Threads filtered = filteredThreads(threads); if (!filtered.isEmpty()) { Q_EMIT q->threadsAdded(filtered); } } void ThreadViewPrivate::_d_threadsModified(const Threads &threads) { Q_Q(ThreadView); Threads filtered = filteredThreads(threads); if (!filtered.isEmpty()) { Q_EMIT q->threadsModified(filtered); } } void ThreadViewPrivate::_d_threadsRemoved(const Threads &threads) { Q_Q(ThreadView); Threads filtered = filteredThreads(threads); if (!filtered.isEmpty()) { Q_EMIT q->threadsRemoved(filtered); } } // ------------- ThreadView ------------------------------------------------------- ThreadView::ThreadView(History::EventType type, const History::Sort &sort, const Filter &filter) : d_ptr(new ThreadViewPrivate(type, sort, filter)) { d_ptr->q_ptr = this; QDBusInterface interface(History::DBusService, History::DBusObjectPath, History::DBusInterface); QDBusReply reply = interface.call("QueryThreads", (int) type, sort.properties(), filter.properties()); if (!reply.isValid()) { Q_EMIT invalidated(); d_ptr->valid = false; return; } d_ptr->objectPath = reply.value(); d_ptr->dbus = new QDBusInterface(History::DBusService, d_ptr->objectPath, History::ThreadViewInterface, QDBusConnection::sessionBus(), this); connect(Manager::instance(), SIGNAL(threadsAdded(History::Threads)), SLOT(_d_threadsAdded(History::Threads))); connect(Manager::instance(), SIGNAL(threadsModified(History::Threads)), SLOT(_d_threadsModified(History::Threads))); connect(Manager::instance(), SIGNAL(threadsRemoved(History::Threads)), SLOT(_d_threadsRemoved(History::Threads))); } ThreadView::~ThreadView() { Q_D(ThreadView); if (d->valid) { d->dbus->call("Destroy"); } } Threads ThreadView::nextPage() { Threads threads; Q_D(ThreadView); if (!d->valid) { return threads; } QDBusReply > reply = d->dbus->call("NextPage"); if (!reply.isValid()) { qDebug() << "Error:" << reply.error(); d->valid = false; Q_EMIT invalidated(); return threads; } QList threadsProperties = reply.value(); Q_FOREACH(const QVariantMap &properties, threadsProperties) { Thread thread = Thread::fromProperties(properties); if (!thread.isNull()) { threads << thread; } } return threads; } bool ThreadView::isValid() const { Q_D(const ThreadView); return d->valid; } } #include "moc_threadview.cpp" history-service-0.1+14.04.20140407/src/UnionFilter0000644000015301777760000000003112320627220022046 0ustar pbusernogroup00000000000000#include "unionfilter.h" history-service-0.1+14.04.20140407/src/TextEvent0000644000015301777760000000002712320627220021543 0ustar pbusernogroup00000000000000#include "textevent.h" history-service-0.1+14.04.20140407/src/sort.h0000644000015301777760000000321012320627220021027 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_SORT_H #define HISTORY_SORT_H #include #include #include #include #include "types.h" namespace History { class SortPrivate; class Sort { Q_DECLARE_PRIVATE(Sort) public: Sort(const QString &sortField = FieldTimestamp, Qt::SortOrder sortOrder = Qt::AscendingOrder, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive); Sort(const Sort &other); ~Sort(); QString sortField() const; void setSortField(const QString &value); Qt::SortOrder sortOrder() const; void setSortOrder(Qt::SortOrder value); Qt::CaseSensitivity caseSensitivity() const; void setCaseSensitivity(Qt::CaseSensitivity value); QVariantMap properties() const; static Sort fromProperties(const QVariantMap &properties); protected: QSharedPointer d_ptr; }; } #endif // HISTORY_SORT_H history-service-0.1+14.04.20140407/src/IntersectionFilter0000644000015301777760000000004012320627220023424 0ustar pbusernogroup00000000000000#include "intersectionfilter.h" history-service-0.1+14.04.20140407/src/unionfilter.cpp0000644000015301777760000001023612320627220022737 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "unionfilter.h" #include "unionfilter_p.h" #include #include #include namespace History { UnionFilterPrivate::UnionFilterPrivate() { } UnionFilterPrivate::~UnionFilterPrivate() { } bool UnionFilterPrivate::match(const QVariantMap properties) const { // if the filter list is empty, assume it matches if (filters.isEmpty()) { return true; } // return true if any of the filters match Q_FOREACH(const History::Filter &filter, filters) { if (filter.match(properties)) { return true; } } // if we reach this point it means none of the filters matched the properties return false; } bool UnionFilterPrivate::isValid() const { // FIXME: maybe we should check if at least one of the inner filters are valid? return !filters.isEmpty(); } QVariantMap UnionFilterPrivate::properties() const { QVariantMap map; if (!isValid()) { return map; } QVariantList filterList; Q_FOREACH(const Filter &filter, filters) { filterList << filter.properties(); } map[FieldFilters] = filterList; map[FieldFilterType] = (int) History::FilterTypeUnion; return map; } QString UnionFilterPrivate::toString(const QString &propertyPrefix) const { if (filters.isEmpty()) { return QString::null; } else if (filters.count() == 1) { return filters.first().toString(); } QStringList output; // wrap each filter string around parenthesis Q_FOREACH(const Filter &filter, filters) { QString value = filter.toString(propertyPrefix); if (!value.isEmpty()) { output << QString("(%1)").arg(value); } } return QString("(%1)").arg(output.join(" OR ")); } HISTORY_FILTER_DEFINE_COPY(UnionFilter, FilterTypeUnion) UnionFilter::UnionFilter() : Filter(*new UnionFilterPrivate()) { } UnionFilter::~UnionFilter() { } void UnionFilter::setFilters(const Filters &filters) { Q_D(UnionFilter); d->filters = filters; } void UnionFilter::prepend(const Filter &filter) { Q_D(UnionFilter); d->filters.prepend(filter); } void UnionFilter::append(const Filter &filter) { Q_D(UnionFilter); d->filters.append(filter); } void UnionFilter::clear() { Q_D(UnionFilter); d->filters.clear(); } Filters UnionFilter::filters() const { Q_D(const UnionFilter); return d->filters; } Filter UnionFilter::fromProperties(const QVariantMap &properties) { UnionFilter filter; if (properties.isEmpty()) { return filter; } QVariant filters = properties[FieldFilters]; QVariantList filterList; // when the filter travels through DBus, it arrives marshalled into QDBusArguments. // cover that case too. if (filters.canConvert()) { QDBusArgument argument = filters.value(); QVariantList list; argument >> list; // and cast also the inner filters Q_FOREACH(const QVariant &var, list) { QDBusArgument arg = var.value(); QVariantMap map; arg >> map; filterList.append(map); } } else { filterList = filters.toList(); } Q_FOREACH(const QVariant &props, filterList) { Filter innerFilter = History::Filter::fromProperties(props.toMap()); if (innerFilter.isValid()) { filter.append(innerFilter); } } return filter; } } history-service-0.1+14.04.20140407/src/voiceevent.h0000644000015301777760000000320512320627220022213 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_VOICEEVENT_H #define HISTORY_VOICEEVENT_H #include "event.h" namespace History { class VoiceEventPrivate; class ItemFactory; class VoiceEvent : public Event { Q_DECLARE_PRIVATE(VoiceEvent) friend class ItemFactory; public: explicit VoiceEvent(); VoiceEvent(const QString &accountId, const QString &threadId, const QString &eventId, const QString &sender, const QDateTime ×tamp, bool newEvent, bool missed, const QTime &duration = QTime(), const QStringList &participants = QStringList()); ~VoiceEvent(); // copy related members VoiceEvent(const Event &other); VoiceEvent& operator=(const Event &other); bool missed() const; QTime duration() const; static Event fromProperties(const QVariantMap &properties); }; } #endif history-service-0.1+14.04.20140407/src/unionfilter.h0000644000015301777760000000257112320627220022407 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_UNIONFILTER_H #define HISTORY_UNIONFILTER_H #include "filter.h" #include "types.h" namespace History { class UnionFilterPrivate; // OR filter class UnionFilter : public Filter { Q_DECLARE_PRIVATE(UnionFilter) public: UnionFilter(); ~UnionFilter(); // copy related members UnionFilter(const Filter &other); UnionFilter& operator=(const Filter &other); void setFilters(const Filters &filters); void prepend(const Filter &filter); void append(const Filter &filter); void clear(); Filters filters() const; static Filter fromProperties(const QVariantMap &properties); }; } #endif history-service-0.1+14.04.20140407/src/pluginthreadview.h0000644000015301777760000000272212320627220023430 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef PLUGINTHREADVIEW_H #define PLUGINTHREADVIEW_H #include #include #include #include namespace History { class PluginThreadViewPrivate; class PluginThreadView : public QObject, public QDBusContext { Q_OBJECT Q_DECLARE_PRIVATE(PluginThreadView) public: explicit PluginThreadView(QObject *parent = 0); virtual ~PluginThreadView(); // DBus exposed methods Q_NOREPLY void Destroy(); virtual QList NextPage() = 0; virtual bool IsValid() const; // other methods QString objectPath() const; Q_SIGNALS: void Invalidated(); private: QScopedPointer d_ptr; }; } #endif // PLUGINTHREADVIEW_H history-service-0.1+14.04.20140407/src/eventview.h0000644000015301777760000000337412320627220022067 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_EVENTVIEW_H #define HISTORY_EVENTVIEW_H #include "types.h" #include "event.h" #include "filter.h" #include "sort.h" #include namespace History { class EventViewPrivate; class EventView : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(EventView) public: EventView(History::EventType type, const History::Sort &sort, const History::Filter &filter); virtual ~EventView(); QList nextPage(); bool isValid() const; Q_SIGNALS: void eventsAdded(const History::Events &events); void eventsModified(const History::Events &events); void eventsRemoved(const History::Events &events); void invalidated(); private: Q_PRIVATE_SLOT(d_func(), void _d_eventsAdded(const History::Events &events)) Q_PRIVATE_SLOT(d_func(), void _d_eventsModified(const History::Events &events)) Q_PRIVATE_SLOT(d_func(), void _d_eventsRemoved(const History::Events &events)) QScopedPointer d_ptr; }; } #endif // HISTORY_EVENTVIEW_H history-service-0.1+14.04.20140407/src/thread.h0000644000015301777760000000373512320627220021323 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_THREAD_H #define HISTORY_THREAD_H #include #include #include #include #include "types.h" #include "event.h" namespace History { class ThreadPrivate; class ItemFactory; class Thread { Q_DECLARE_PRIVATE(Thread) friend class ItemFactory; public: explicit Thread(); Thread(const QString &accountId, const QString &threadId, EventType type, const QStringList &participants, const Event &lastEvent = Event(), int count = 0, int unreadCount = 0); Thread(const Thread &other); virtual ~Thread(); Thread& operator=(const Thread &other); QString accountId() const; QString threadId() const; EventType type() const; QStringList participants() const; Event lastEvent() const; int count() const; int unreadCount() const; bool isNull() const; bool operator==(const Thread &other) const; bool operator<(const Thread &other) const; virtual QVariantMap properties() const; static Thread fromProperties(const QVariantMap &properties); protected: QSharedPointer d_ptr; }; typedef QList Threads; } #endif // HISTORY_THREAD_H history-service-0.1+14.04.20140407/src/CMakeLists.txt0000644000015301777760000000446712320627220022446 0ustar pbusernogroup00000000000000set(library_SRCS event.cpp eventview.cpp filter.cpp intersectionfilter.cpp manager.cpp managerdbus.cpp phoneutils.cpp pluginthreadview.cpp plugineventview.cpp sort.cpp textevent.cpp texteventattachment.cpp thread.cpp threadview.cpp unionfilter.cpp voiceevent.cpp ) set(library_HDRS Event event.h EventView eventview.h Filter filter.h IntersectionFilter intersectionfilter.h Manager manager.h Plugin plugin.h PluginThreadView pluginthreadview.h PluginEventView plugineventview.h Sort sort.h TextEvent textevent.h TextEventAttachment texteventattachment.h Thread thread.h ThreadView threadview.h Types types.h UnionFilter unionfilter.h VoiceEvent voiceevent.h ) set(library_PRIV_HDRS event_p.h eventview_p.h filter_p.h intersectionfilter_p.h manager_p.h managerdbus_p.h phonenumberutils_p.h phoneutils_p.h pluginthreadview_p.h plugineventview_p.h sort_p.h textevent_p.h texteventattachment_p.h thread_p.h threadview_p.h unionfilter_p.h voiceevent_p.h ) qt5_add_dbus_adaptor(library_SRCS PluginThreadView.xml pluginthreadview.h History::PluginThreadView) qt5_add_dbus_adaptor(library_SRCS PluginEventView.xml plugineventview.h History::PluginEventView) include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}) add_library(historyservice SHARED ${library_SRCS} ${library_HDRS} ${library_PRIV_HDRS}) # Set the library version and the SOVERSION set_target_properties(historyservice PROPERTIES SOVERSION ${HISTORY_VERSION_MAJOR} VERSION ${HISTORY_VERSION_MAJOR}.${HISTORY_VERSION_MINOR}.${HISTORY_VERSION_PATCH}) qt5_use_modules(historyservice Core DBus) install(TARGETS historyservice DESTINATION ${CMAKE_INSTALL_LIBDIR}) set(INCLUDE_INSTALL_DIR include/history-service) install(FILES ${library_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/History) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/history-service.pc.in ${CMAKE_CURRENT_BINARY_DIR}/history-service.pc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/history-service.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT pkgconfig) add_subdirectory(tests) history-service-0.1+14.04.20140407/src/texteventattachment.h0000644000015301777760000000434212320627220024146 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_TEXT_EVENT_ATTACHMENT_H #define HISTORY_TEXT_EVENT_ATTACHMENT_H #include #include #include "types.h" namespace History { class TextEventAttachmentPrivate; class ItemFactory; class TextEventAttachment { Q_DECLARE_PRIVATE(TextEventAttachment) friend class ItemFactory; public: explicit TextEventAttachment(); TextEventAttachment(const QString &accountId, const QString &threadId, const QString &eventId, const QString &attachmentId, const QString &contentType, const QString &filePath, const History::AttachmentFlags &status = History::AttachmentDownloaded); TextEventAttachment(const TextEventAttachment &other); virtual ~TextEventAttachment(); TextEventAttachment& operator=(const TextEventAttachment &other); QString accountId() const; QString threadId() const; QString eventId() const; QString attachmentId() const; QString contentType() const; QString filePath() const; History::AttachmentFlags status() const; virtual QVariantMap properties() const; static TextEventAttachment fromProperties(const QVariantMap &properties); bool isNull() const; bool operator==(const TextEventAttachment &other); protected: QSharedPointer d_ptr; }; typedef QList TextEventAttachments; } #endif // HISTORY_TEXT_EVENT_ATTACHMENT_H history-service-0.1+14.04.20140407/src/filter.cpp0000644000015301777760000001260212320627220021665 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "filter.h" #include "filter_p.h" #include "intersectionfilter.h" #include "unionfilter.h" #include #include namespace History { // ------------- FilterPrivate ------------------------------------------------ FilterPrivate::FilterPrivate() { } FilterPrivate::FilterPrivate(const QString &theFilterProperty, const QVariant &theFilterValue, MatchFlags theMatchFlags) : filterProperty(theFilterProperty), filterValue(theFilterValue), matchFlags(theMatchFlags) { } FilterPrivate::~FilterPrivate() { } QString FilterPrivate::toString(const QString &propertyPrefix) const { if (filterProperty.isEmpty() || filterValue.isNull()) { return QString::null; } QString value; switch (filterValue.type()) { case QVariant::String: // FIXME: need to escape strings // wrap strings value = QString("\"%1\"").arg(filterValue.toString()); break; case QVariant::Bool: value = filterValue.toBool() ? "1" : "0"; break; case QVariant::Int: value = QString::number(filterValue.toInt()); break; case QVariant::Double: value = QString::number(filterValue.toDouble()); break; default: value = filterValue.toString(); } QString propertyName = propertyPrefix.isNull() ? filterProperty : QString("%1.%2").arg(propertyPrefix, filterProperty); // FIXME2: need to check for the match flags return QString("%1=%2").arg(propertyName, value); } bool FilterPrivate::match(const QVariantMap properties) const { // assume empty filters match anything if (filterProperty.isEmpty() || !filterValue.isValid() || !properties.contains(filterProperty)) { return true; } // FIXME: use the MatchFlags return properties[filterProperty] == filterValue; } QVariantMap FilterPrivate::properties() const { QVariantMap map; if (!isValid()) { return map; } map[FieldFilterType] = (int)FilterTypeStandard; map[FieldFilterProperty] = filterProperty; map[FieldFilterValue] = filterValue; map[FieldMatchFlags] = (int)matchFlags; return map; } // ------------- Filter ------------------------------------------------------- Filter::Filter(FilterPrivate &p) : d_ptr(&p) { } Filter::Filter(const QString &filterProperty, const QVariant &filterValue, MatchFlags matchFlags) : d_ptr(new FilterPrivate(filterProperty, filterValue, matchFlags)) { } Filter::Filter(const Filter &other) : d_ptr(other.d_ptr->clone()) { } Filter::~Filter() { } Filter &Filter::operator=(const Filter &other) { if (&other == this) { return *this; } d_ptr = QSharedPointer(other.d_ptr->clone()); } QString Filter::filterProperty() const { Q_D(const Filter); return d->filterProperty; } void Filter::setFilterProperty(const QString &value) { Q_D(Filter); d->filterProperty = value; } QVariant Filter::filterValue() const { Q_D(const Filter); return d->filterValue; } void Filter::setFilterValue(const QVariant &value) { Q_D(Filter); d->filterValue = value; } MatchFlags Filter::matchFlags() const { Q_D(const Filter); return d->matchFlags; } void Filter::setMatchFlags(const MatchFlags &flags) { Q_D(Filter); d->matchFlags = flags; } QString Filter::toString(const QString &propertyPrefix) const { Q_D(const Filter); return d->toString(propertyPrefix); } bool Filter::match(const QVariantMap properties) const { Q_D(const Filter); return d->match(properties); } FilterType Filter::type() const { Q_D(const Filter); return d->type(); } bool Filter::operator==(const Filter &other) const { // FIXME: implement in a more performant way return toString() == other.toString(); } bool Filter::isValid() const { Q_D(const Filter); return d->isValid(); } QVariantMap Filter::properties() const { Q_D(const Filter); return d->properties(); } Filter Filter::fromProperties(const QVariantMap &properties) { Filter filter; if (properties.isEmpty()) { return filter; } switch ((FilterType)properties[FieldFilterType].toInt()) { case FilterTypeStandard: filter = Filter(properties[FieldFilterProperty].toString(), properties[FieldFilterValue], (MatchFlags)properties[FieldMatchFlags].toInt()); break; case FilterTypeIntersection: filter = IntersectionFilter::fromProperties(properties); break; case FilterTypeUnion: filter = UnionFilter::fromProperties(properties); break; } return filter; } } history-service-0.1+14.04.20140407/src/history-service.pc.in0000644000015301777760000000070012320627220023760 0ustar pbusernogroup00000000000000prefix=${CMAKE_INSTALL_PREFIX} exec_prefix=${CMAKE_INSTALL_PREFIX} libdir=${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR} includedir=${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR} Name: history-service Description: History service to store messages and calls Version: ${PACKAGE_VERSION} Requires.private: Qt5Core, Qt5DBus Libs: -L${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} -lhistoryservice Cflags: -I${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR} history-service-0.1+14.04.20140407/src/manager.h0000644000015301777760000000501412320627220021456 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_MANAGER_H #define HISTORY_MANAGER_H #include #include #include "types.h" #include "event.h" #include "filter.h" #include "sort.h" #include "thread.h" namespace History { class ManagerPrivate; class Manager : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(Manager) public: ~Manager(); static Manager *instance(); ThreadViewPtr queryThreads(EventType type, const Sort &sort = Sort(), const Filter &filter = Filter()); EventViewPtr queryEvents(EventType type, const Sort &sort = Sort(), const Filter &filter = Filter()); Event getSingleEvent(EventType type, const QString &accountId, const QString &threadId, const QString &eventId); Thread threadForParticipants(const QString &accountId, EventType type, const QStringList &participants, History::MatchFlags matchFlags = History::MatchCaseSensitive, bool create = false); Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId); bool writeEvents(const History::Events &events); bool removeThreads(const Threads &threads); bool removeEvents(const Events &events); Q_SIGNALS: void threadsAdded(const History::Threads &threads); void threadsModified(const History::Threads &threads); void threadsRemoved(const History::Threads &threads); void eventsAdded(const History::Events &events); void eventsModified(const History::Events &events); void eventsRemoved(const History::Events &events); private: Manager(); QScopedPointer d_ptr; }; } #endif history-service-0.1+14.04.20140407/src/Types0000644000015301777760000000002312320627220020715 0ustar pbusernogroup00000000000000#include "types.h" history-service-0.1+14.04.20140407/src/eventview.cpp0000644000015301777760000001125412320627220022416 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "eventview.h" #include "eventview_p.h" #include "event.h" #include "filter.h" #include "manager.h" #include "sort.h" #include "textevent.h" #include "voiceevent.h" #include #include namespace History { // ------------- EventViewPrivate ------------------------------------------------ EventViewPrivate::EventViewPrivate(History::EventType theType, const History::Sort &theSort, const History::Filter &theFilter) : type(theType), sort(theSort), filter(theFilter), valid(true), dbus(0) { } Events EventViewPrivate::filteredEvents(const Events &events) { bool filterNull = filter.isNull(); Events filtered; Q_FOREACH(const Event &event, events) { if (event.type() != type) { continue; } if (filterNull || filter.match(event.properties())) { filtered << events; } } return filtered; } void EventViewPrivate::_d_eventsAdded(const Events &events) { Q_Q(EventView); Events filtered = filteredEvents(events); if (!filtered.isEmpty()) { Q_EMIT q->eventsAdded(filtered); } } void EventViewPrivate::_d_eventsModified(const Events &events) { Q_Q(EventView); Events filtered = filteredEvents(events); if (!filtered.isEmpty()) { Q_EMIT q->eventsModified(filtered); } } void EventViewPrivate::_d_eventsRemoved(const Events &events) { Q_Q(EventView); Events filtered = filteredEvents(events); if (!filtered.isEmpty()) { Q_EMIT q->eventsRemoved(filtered); } } // ------------- EventView ------------------------------------------------------- EventView::EventView(EventType type, const History::Sort &sort, const History::Filter &filter) : d_ptr(new EventViewPrivate(type, sort, filter)) { d_ptr->q_ptr = this; QDBusInterface interface(History::DBusService, History::DBusObjectPath, History::DBusInterface); QDBusReply reply = interface.call("QueryEvents", (int) type, sort.properties(), filter.properties()); if (!reply.isValid()) { Q_EMIT invalidated(); d_ptr->valid = false; return; } d_ptr->objectPath = reply.value(); d_ptr->dbus = new QDBusInterface(History::DBusService, d_ptr->objectPath, History::EventViewInterface, QDBusConnection::sessionBus(), this); connect(Manager::instance(), SIGNAL(eventsAdded(History::Events)), SLOT(_d_eventsAdded(History::Events))); connect(Manager::instance(), SIGNAL(eventsModified(History::Events)), SLOT(_d_eventsModified(History::Events))); connect(Manager::instance(), SIGNAL(eventsRemoved(History::Events)), SLOT(_d_eventsRemoved(History::Events))); } EventView::~EventView() { Q_D(EventView); if (d->valid) { d->dbus->call("Destroy"); } } QList EventView::nextPage() { Q_D(EventView); QList events; if (!d->valid) { return events; } QDBusReply > reply = d->dbus->call("NextPage"); if (!reply.isValid()) { d->valid = false; Q_EMIT invalidated(); return events; } QList eventsProperties = reply.value(); Q_FOREACH(const QVariantMap &properties, eventsProperties) { Event event; switch (d->type) { case EventTypeText: event = TextEvent::fromProperties(properties); break; case EventTypeVoice: event = VoiceEvent::fromProperties(properties); break; } if (!event.isNull()) { events << event; } } return events; } bool EventView::isValid() const { Q_D(const EventView); return d->valid; } } #include "moc_eventview.cpp" history-service-0.1+14.04.20140407/src/EventView0000644000015301777760000000002712320627220021531 0ustar pbusernogroup00000000000000#include "eventview.h" history-service-0.1+14.04.20140407/src/manager_p.h0000644000015301777760000000207312320627220021777 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_MANAGER_P_H #define HISTORY_MANAGER_P_H #include #include #include "types.h" namespace History { class Manager; class ManagerDBus; class ManagerPrivate { public: ManagerPrivate(); ~ManagerPrivate(); QScopedPointer dbus; }; } #endif // HISTORY_MANAGER_P_H history-service-0.1+14.04.20140407/src/filter.h0000644000015301777760000000411612320627220021333 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_FILTER_H #define HISTORY_FILTER_H #include #include #include #include "types.h" namespace History { class FilterPrivate; // simple filter class Filter { Q_DECLARE_PRIVATE(Filter) public: Filter(const QString &filterProperty = QString::null, const QVariant &filterValue = QVariant(), MatchFlags matchFlags = MatchCaseSensitive); Filter(const Filter &other); virtual ~Filter(); Filter& operator=(const Filter& other); QString filterProperty() const; void setFilterProperty(const QString &value); QVariant filterValue() const; void setFilterValue(const QVariant &value); MatchFlags matchFlags() const; void setMatchFlags(const MatchFlags &flags); QString toString(const QString &propertyPrefix = QString::null) const; bool match(const QVariantMap properties) const; FilterType type() const; bool operator==(const Filter &other) const; bool operator!=(const Filter &other) const { return !operator==(other); } bool isValid() const; bool isNull() const { return !isValid(); } QVariantMap properties() const; static Filter fromProperties(const QVariantMap &properties); protected: Filter(FilterPrivate &p); QSharedPointer d_ptr; }; typedef QList Filters; } #endif history-service-0.1+14.04.20140407/src/sort_p.h0000644000015301777760000000230512320627220021352 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_SORT_P_H #define HISTORY_SORT_P_H #include #include "types.h" namespace History { class Sort; class SortPrivate { public: SortPrivate(const QString &theSortField, Qt::SortOrder theSortOrder, Qt::CaseSensitivity theCaseSensitivity); virtual ~SortPrivate(); QString sortField; Qt::SortOrder sortOrder; Qt::CaseSensitivity caseSensitivity; }; } #endif // HISTORY_SORT_P_H history-service-0.1+14.04.20140407/src/PluginThreadView0000644000015301777760000000016212320627220023036 0ustar pbusernogroup00000000000000#ifndef PLUGINTHREADVIEW_H #define PLUGINTHREADVIEW_H #include "pluginthreadview.h" #endif // PLUGINTHREADVIEW_H history-service-0.1+14.04.20140407/src/types.h0000644000015301777760000000775312320627220021224 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef TYPES_H #define TYPES_H #include #define DefineSharedPointer(type) class type; typedef QSharedPointer type##Ptr; typedef QWeakPointer type##WeakPtr; typedef QList type##s; namespace History { DefineSharedPointer(EventView) DefineSharedPointer(Plugin) DefineSharedPointer(ThreadView) // enums enum EventType { EventTypeText, EventTypeVoice, EventTypeNull }; enum FilterType { FilterTypeStandard, FilterTypeIntersection, FilterTypeUnion }; enum MatchFlag { MatchCaseSensitive, MatchCaseInsensitive, MatchContains, MatchPhoneNumber }; Q_DECLARE_FLAGS(MatchFlags, MatchFlag) enum MessageStatus { MessageStatusUnknown, MessageStatusDelivered, MessageStatusTemporarilyFailed, MessageStatusPermanentlyFailed, MessageStatusAccepted, MessageStatusRead, MessageStatusDeleted, MessageStatusPending // pending attachment download }; enum MessageType { MessageTypeText, MessageTypeMultiParty }; enum AttachmentFlag { AttachmentDownloaded, AttachmentPending, AttachmentError }; Q_DECLARE_FLAGS(AttachmentFlags, AttachmentFlag) // Event writing results enum EventWriteResult { EventWriteCreated, EventWriteModified, EventWriteError }; // dbus service, object path and interface static const char* DBusService = "com.canonical.HistoryService"; static const char* DBusObjectPath = "/com/canonical/HistoryService"; static const char* DBusInterface = "com.canonical.HistoryService"; static const char* ThreadViewInterface = "com.canonical.HistoryService.ThreadView"; static const char* EventViewInterface = "com.canonical.HistoryService.EventView"; // fields static const char* FieldAccountId = "accountId"; static const char* FieldThreadId = "threadId"; static const char* FieldEventId = "eventId"; static const char* FieldType = "type"; static const char* FieldParticipants = "participants"; static const char* FieldCount = "count"; static const char* FieldUnreadCount = "unreadCount"; static const char* FieldSenderId = "senderId"; static const char* FieldTimestamp = "timestamp"; static const char* FieldNewEvent = "newEvent"; // text event fields static const char* FieldMessage = "message"; static const char* FieldMessageType = "messageType"; static const char* FieldMessageStatus = "messageStatus"; static const char* FieldReadTimestamp = "readTimestamp"; static const char* FieldSubject = "subject"; static const char* FieldAttachments = "attachments"; // text attachment fields static const char* FieldAttachmentId = "attachmentId"; static const char* FieldContentType = "contentType"; static const char* FieldFilePath = "filePath"; static const char* FieldStatus = "status"; // voice event fields static const char* FieldMissed = "missed"; static const char* FieldDuration = "duration"; // sort stuff static const char* FieldSortField = "sortField"; static const char* FieldSortOrder = "sortOrder"; static const char* FieldCaseSensitivity = "caseSensitivity"; // filter stuff static const char* FieldFilterType = "filterType"; static const char* FieldFilterProperty = "filterProperty"; static const char* FieldFilterValue = "filterValue"; static const char* FieldMatchFlags = "matchFlags"; static const char* FieldFilters = "filters"; } #endif // TYPES_H history-service-0.1+14.04.20140407/src/Filter0000644000015301777760000000002412320627220021037 0ustar pbusernogroup00000000000000#include "filter.h" history-service-0.1+14.04.20140407/src/texteventattachment_p.h0000644000015301777760000000331312320627220024462 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * Tiago Salem Herrmann * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef HISTORY_TEXT_EVENT_ATTACHMENT_P_H #define HISTORY_TEXT_EVENT_ATTACHMENT_P_H #include #include "types.h" namespace History { class TextEventAttachment; class TextEventAttachmentPrivate { public: explicit TextEventAttachmentPrivate(); TextEventAttachmentPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theAttachmentId, const QString &theContentType, const QString &theFilePath, const History::AttachmentFlags &theStatus); virtual ~TextEventAttachmentPrivate(); QString accountId; QString threadId; QString eventId; QString attachmentId; QString contentType; QString filePath; History::AttachmentFlags status; }; } #endif // HISTORY_TEXT_EVENT_ATTACHMENT_P_H history-service-0.1+14.04.20140407/src/Plugin0000644000015301777760000000002412320627220021050 0ustar pbusernogroup00000000000000#include "plugin.h" history-service-0.1+14.04.20140407/src/pluginthreadview_p.h0000644000015301777760000000203412320627220023743 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef PLUGINTHREADVIEW_P_H #define PLUGINTHREADVIEW_P_H #include class ThreadViewAdaptor; namespace History { class PluginThreadViewPrivate { public: PluginThreadViewPrivate(); ThreadViewAdaptor *adaptor; QString objectPath; }; } #endif // PLUGINTHREADVIEW_P_H history-service-0.1+14.04.20140407/src/Event0000644000015301777760000000002312320627220020672 0ustar pbusernogroup00000000000000#include "event.h" history-service-0.1+14.04.20140407/src/phoneutils.cpp0000644000015301777760000000320512320627220022571 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "phoneutils_p.h" #include "phonenumberutils_p.h" PhoneUtils::PhoneUtils(QObject *parent) : QObject(parent) { } bool PhoneUtils::comparePhoneNumbers(const QString &number1, const QString &number2) { if (isPhoneNumber(number1) && isPhoneNumber(number2)) { return PhoneNumberUtils::compareLoosely(number1, number2); } // if at least one of the id's is not a phone number, then perform a simple string comparison return number1 == number2; } bool PhoneUtils::isPhoneNumber(const QString &identifier) { // remove all non diable digits QString finalNumber = QString(identifier).replace(QRegExp("[p+*#(),;-]"),""); finalNumber = finalNumber.replace(QRegExp("(\\s+)"), ""); // if empty, the number is invalid if (finalNumber.isEmpty()) return false; finalNumber = finalNumber.replace(QRegExp("(\\d+)"), ""); return finalNumber.isEmpty(); } history-service-0.1+14.04.20140407/src/event.cpp0000644000015301777760000001267112320627220021527 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #include "event.h" #include "event_p.h" namespace History { // ------------- EventPrivate ------------------------------------------------ EventPrivate::EventPrivate() { } EventPrivate::EventPrivate(const QString &theAccountId, const QString &theThreadId, const QString &theEventId, const QString &theSenderId, const QDateTime &theTimestamp, bool theNewEvent, const QStringList &theParticipants) : accountId(theAccountId), threadId(theThreadId), eventId(theEventId), senderId(theSenderId), timestamp(theTimestamp), newEvent(theNewEvent), participants(theParticipants) { } EventPrivate::~EventPrivate() { } QVariantMap EventPrivate::properties() const { QVariantMap map; map[FieldAccountId] = accountId; map[FieldThreadId] = threadId; map[FieldEventId] = eventId; map[FieldSenderId] = senderId; map[FieldTimestamp] = timestamp.toString(Qt::ISODate); map[FieldNewEvent] = newEvent; map[FieldType] = type(); map[FieldParticipants] = participants; return map; } // ------------- Event ------------------------------------------------------- /*! * \class Event * * \brief The Event class provides the base class for all events stored * and loaded from the history backends. * * This class should not be used directly and instead * the derived classes should be used. * * \sa TextEvent, VoiceEvent */ /*! * \brief Constructs an empty Event */ Event::Event() : d_ptr(new EventPrivate()) { } /*! * \brief Constructs an Event by copying the data from another one. * \param other The item to be copied; */ Event::Event(const Event &other) : d_ptr(other.d_ptr->clone()) { } /*! \internal * \brief Constructor to be used by derived classes to pass a EventPrivate instance * \param p The instance of the private class; */ Event::Event(EventPrivate &p) : d_ptr(&p) { } Event::~Event() { } /*! * \brief Assign operator for the Event class * \param other The event to be copied; */ Event& Event::operator=(const Event &other) { if (&other == this) { return *this; } d_ptr = QSharedPointer(other.d_ptr->clone()); } /*! * \brief Returns the account ID this event belongs to. */ QString Event::accountId() const { Q_D(const Event); return d->accountId; } /*! * \brief Returns the ID of the communication thread this event belongs to. * \sa HistoryThread */ QString Event::threadId() const { Q_D(const Event); return d->threadId; } /*! * \brief Returns the ID that uniquely identifies this event. */ QString Event::eventId() const { Q_D(const Event); return d->eventId; } /*! * \brief Returns the ID of the sender of this event. */ QString Event::senderId() const { Q_D(const Event); return d->senderId; } /*! * \brief Returns the timestamp of when the event happened. */ QDateTime Event::timestamp() const { Q_D(const Event); return d->timestamp; } /*! * \brief Returns whether the event is new (not yet seen by the user). * \sa Event::setNewEvent() */ bool Event::newEvent() const { Q_D(const Event); return d->newEvent; } /*! * \brief Set whether this event is new (not yet seen by the user). * \param value True if the event is new. False otherwise. * \sa Event::newEvent() */ void Event::setNewEvent(bool value) { Q_D(Event); d->newEvent = value; } /*! * \brief Returns the type of this event. */ EventType Event::type() const { Q_D(const Event); return d->type(); } QStringList Event::participants() const { Q_D(const Event); return d->participants; } /*! * \brief Returns the event properties */ QVariantMap Event::properties() const { Q_D(const Event); return d->properties(); } /*! * \brief Return whether this event is a null event. */ bool Event::isNull() const { Q_D(const Event); return d->accountId.isNull() && d->threadId.isNull() && d->eventId.isNull(); } /*! * \brief Compare this event with another one. * \param other The other event; */ bool Event::operator==(const Event &other) const { Q_D(const Event); if (type() != other.type()) { return false; } if (d->accountId != other.d_ptr->accountId) { return false; } if (d->threadId != other.d_ptr->threadId) { return false; } if (d->eventId != other.d_ptr->eventId) { return false; } return true; } bool Event::operator<(const Event &other) const { QString selfData = accountId() + threadId() + eventId(); QString otherData = other.accountId() + other.threadId() + other.eventId(); return selfData < otherData; } } history-service-0.1+14.04.20140407/src/phoneutils_p.h0000644000015301777760000000213112320627220022552 0ustar pbusernogroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * Authors: * Gustavo Pichorim Boiko * * This file is part of history-service. * * history-service 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; version 3. * * history-service 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 . */ #ifndef PHONEUTILS_H #define PHONEUTILS_H #include class PhoneUtils : public QObject { Q_OBJECT public: explicit PhoneUtils(QObject *parent = 0); Q_INVOKABLE static bool comparePhoneNumbers(const QString &number1, const QString &number2); Q_INVOKABLE static bool isPhoneNumber(const QString &identifier); }; #endif // PHONEUTILS_H history-service-0.1+14.04.20140407/src/Manager0000644000015301777760000000002512320627220021165 0ustar pbusernogroup00000000000000#include "manager.h"