diff --git a/CMakeLists.txt b/CMakeLists.txt index b37f3ce0..6508cc0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ include(ECMGenerateHeaders) include(GenerateExportHeader) include(FeatureSummary) -find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick Test) +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Gui Widgets Qml Quick Test Sql) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n KIO People) find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons Notifications) diff --git a/dialer/src/CMakeLists.txt b/dialer/src/CMakeLists.txt index 6f5cd881..ef5aaa58 100644 --- a/dialer/src/CMakeLists.txt +++ b/dialer/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(plasmaphonedialer_SRCS main.cpp dialerutils.cpp call-handler.cpp + callhistorymodel.cpp call-manager.cpp resources.qrc ) @@ -15,6 +16,7 @@ target_link_libraries(plasmaphonedialer Qt5::Gui Qt5::Quick Qt5::Widgets + Qt5::Sql KF5::Declarative KF5::I18n KF5::QuickAddons diff --git a/dialer/src/callhistorymodel.cpp b/dialer/src/callhistorymodel.cpp new file mode 100644 index 00000000..97442eb0 --- /dev/null +++ b/dialer/src/callhistorymodel.cpp @@ -0,0 +1,149 @@ +/* + Copyright (C) 2019 Nicolas Fella + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + 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 . +*/ +#include "callhistorymodel.h" + +#include +#include +#include +#include +#include + +CallHistoryModel::CallHistoryModel(QObject *parent) + : QAbstractListModel(parent) + , m_db(QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("calldb"))) +{ + m_db.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "plasmaphonedialerdb.sqlite"); + bool open = m_db.open(); + + if (!open) { + qWarning() << "Could not open call database" << m_db.lastError(); + } + + QSqlQuery createTable(m_db); + createTable.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, time DATETIME, duration INTEGER, callType INTEGER)")); + + QSqlQuery fetchCalls(m_db); + fetchCalls.exec(QStringLiteral("SELECT id, number, time, duration, callType FROM History")); + + beginResetModel(); + while (fetchCalls.next()) { + CallData call; + call.id = fetchCalls.value(0).toString(); + call.number = fetchCalls.value(1).toString(); + call.time = QDateTime::fromMSecsSinceEpoch(fetchCalls.value(2).toInt()); + call.duration = fetchCalls.value(3).toInt(); + call.callType = fetchCalls.value(4).toInt(); + + m_calls.append(call); + } + endResetModel(); +} + +void CallHistoryModel::addCall(QString number, int duration, int type) +{ + beginInsertRows(QModelIndex(), m_calls.size(), m_calls.size()); + QSqlQuery putCall(m_db); + putCall.prepare(QStringLiteral("INSERT INTO History (number, time, duration, callType) VALUES (:number, :time, :duration, :callType)")); + putCall.bindValue(":number", number); + putCall.bindValue(":time", QDateTime::currentDateTime().toMSecsSinceEpoch()); + putCall.bindValue(":duration", duration); + putCall.bindValue(":callType", type); + putCall.exec(); + + CallData data; + data.id = putCall.lastInsertId().toString(); + data.number = number; + data.duration = duration; + data.time = QDateTime::currentDateTime(); + data.callType = type; + + m_calls.append(data); + + endInsertRows(); +} + +void CallHistoryModel::clear() +{ + beginResetModel(); + + QSqlQuery clearCalls(m_db); + clearCalls.exec(QStringLiteral("DELETE FROM History")); + m_calls.clear(); + + endResetModel(); +} + +QVariant CallHistoryModel::data(const QModelIndex& index, int role) const +{ + int row = index.row(); + + switch (role) { + case Roles::PhoneNumberRole: + return m_calls[row].number; + case Roles::CallTypeRole: + return m_calls[row].callType; + case Roles::DurationRole: + return m_calls[row].duration; + case Roles::TimeRole: + return m_calls[row].time; + case Roles::IdRole: + return m_calls[row].id; + } + return {}; +} + +int CallHistoryModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_calls.size(); +} + +QHash CallHistoryModel::roleNames() const +{ + QHash roleNames; + roleNames[PhoneNumberRole] = "number"; + roleNames[CallTypeRole] = "time"; + roleNames[DurationRole] = "duration"; + roleNames[TimeRole] = "callType"; + roleNames[IdRole] = "dbid"; + + return roleNames; +} + +bool CallHistoryModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(count) + + beginRemoveRows(parent, row, row); + QSqlQuery remove(m_db); + remove.prepare(QStringLiteral("DELETE FROM History WHERE id=:id")); + remove.bindValue(":id", m_calls[row].id); + remove.exec(); + + endRemoveRows(); + return true; +} + +void CallHistorySortFilterModel::remove(int index) +{ + QSortFilterProxyModel::removeRow(index); +} + +void CallHistorySortFilterModel::sort(int column, Qt::SortOrder order) +{ + QSortFilterProxyModel::sort(column, order); +} diff --git a/dialer/src/callhistorymodel.h b/dialer/src/callhistorymodel.h new file mode 100644 index 00000000..711accea --- /dev/null +++ b/dialer/src/callhistorymodel.h @@ -0,0 +1,69 @@ +/* + Copyright (C) 2019 Nicolas Fella + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + 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 . +*/ +#pragma once + +#include +#include +#include +#include +#include + +struct CallData { + QString id; + QString number; + QDateTime time; + int duration; + int callType; +}; + +class CallHistoryModel : public QAbstractListModel +{ + Q_OBJECT +public: + + CallHistoryModel(QObject *parent = nullptr); + + enum Roles { + PhoneNumberRole = Qt::UserRole + 1, + DurationRole, + TimeRole, + CallTypeRole, + IdRole + }; + Q_ENUM(Roles) + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + Q_INVOKABLE void addCall(QString number, int duration, int type); + Q_INVOKABLE void clear(); + + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + +private: + QSqlDatabase m_db; + QVector m_calls; +}; + +class CallHistorySortFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; +}; diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp index 905f1e40..4f34193e 100644 --- a/dialer/src/main.cpp +++ b/dialer/src/main.cpp @@ -21,6 +21,7 @@ #include "dialerutils.h" #include "call-handler.h" +#include "callhistorymodel.h" #include #include @@ -109,6 +110,9 @@ int main(int argc, char **argv) parser.process(app); + qmlRegisterType("org.kde.plasma.dialer", 1, 0, "CallHistoryModel"); + qmlRegisterType("org.kde.plasma.dialer", 1, 0, "CallHistorySortFilterModel"); + Tp::registerTypes(); Tp::AccountFactoryPtr accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(), diff --git a/dialer/src/qml/Dialer/History.qml b/dialer/src/qml/Dialer/History.qml index 4760465a..5d932f27 100644 --- a/dialer/src/qml/Dialer/History.qml +++ b/dialer/src/qml/Dialer/History.qml @@ -23,6 +23,8 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.plasma.dialer 1.0 + Item { function secondsToTimeString(seconds) { @@ -38,12 +40,12 @@ Item { PlasmaComponents.Label { anchors.centerIn: parent text: i18n("No recent calls") - visible: historyModel.count == 0 + visible: view.count == 0 } ColumnLayout { anchors.fill: parent - visible: historyModel.count > 0 + visible: view.count > 0 PlasmaComponents.ToolBar { Layout.fillWidth: true tools: RowLayout { @@ -55,7 +57,7 @@ Item { text: i18n("All") onCheckedChanged: { if (checked) { - filterModel.filterString = ""; + filterModel.setFilterFixedString("") } } } @@ -64,7 +66,7 @@ Item { text: i18n("Missed") onCheckedChanged: { if (checked) { - filterModel.filterString = "0"; + filterModel.setFilterFixedString("0") } } } @@ -74,7 +76,7 @@ Item { } PlasmaComponents.Button { text: i18n("Clear") - onClicked: clearHistory(); + onClicked: historyModel.clear() } } } @@ -83,12 +85,12 @@ Item { Layout.fillHeight: true ListView { id: view - model: PlasmaCore.SortFilterModel { + model: CallHistorySortFilterModel { id: filterModel sourceModel: historyModel - filterRole: "callType" - sortRole: "time" - sortOrder: Qt.DescendingOrder + filterRole: CallHistoryModel.CallTypeRole + sortRole: CallHistoryModel.TimeRole + Component.onCompleted: sort(0, Qt.DescendingOrder) } section { property: "date" diff --git a/dialer/src/qml/Dialer/HistoryDelegate.qml b/dialer/src/qml/Dialer/HistoryDelegate.qml index 74b3304e..876fdc50 100644 --- a/dialer/src/qml/Dialer/HistoryDelegate.qml +++ b/dialer/src/qml/Dialer/HistoryDelegate.qml @@ -24,7 +24,6 @@ import QtQuick.Controls 2.2 as Controls import org.kde.kirigami 2.2 as Kirigami import org.kde.plasma.components 2.0 as PlasmaComponents - Item { id: delegateParent width: view.width @@ -50,7 +49,7 @@ Item { easing.type: Easing.InOutQuad } ScriptAction { - script: removeCallFromHistory(model.originalIndex); + script: filterModel.remove(model.index) } } diff --git a/dialer/src/qml/main.qml b/dialer/src/qml/main.qml index d5402ba4..441c2583 100644 --- a/dialer/src/qml/main.qml +++ b/dialer/src/qml/main.qml @@ -26,6 +26,8 @@ import QtQuick.LocalStorage 2.0 import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kirigami 2.0 as Kirigami +import org.kde.plasma.dialer 1.0 + ApplicationWindow { id: root @@ -57,7 +59,7 @@ ApplicationWindow { } else { callType = 2; } - insertCallInHistory(callContactNumber, callDuration, callType); + historyModel.addCall(callContactNumber, callDuration, callType) } } @@ -71,86 +73,10 @@ ApplicationWindow { function call(number) { dialerUtils.dial(number); } - - function insertCallInHistory(number, duration, callType) { - //DATABSE - var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); - - db.transaction( - function(tx) { - var rs = tx.executeSql("INSERT INTO History VALUES(NULL, ?, datetime('now'), ?, ? )", [number, duration, callType]); - - var rs = tx.executeSql('SELECT * FROM History where id=?', [rs.insertId]); - - for(var i = 0; i < rs.rows.length; i++) { - var row = rs.rows.item(i); - row.date = Qt.formatDate(row.time, "yyyy-MM-dd"); - row.originalIndex = historyModel.count; - historyModel.append(row); - } - } - ) - } - - //index is historyModel row number, not db id and not sortmodel row number - function removeCallFromHistory(index) { - var item = historyModel.get(index); - - if (!item) { - return; - } - - var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); - - db.transaction( - function(tx) { - tx.executeSql("DELETE from History WHERE id=?", [item.id]); - } - ) - - historyModel.remove(index); - } - - function clearHistory() { - var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); - - db.transaction( - function(tx) { - tx.executeSql("DELETE from History"); - } - ) - - historyModel.clear(); - } - //END FUNCTIONS -//BEGIN DATABASE - Component.onCompleted: { - //DATABSE - var db = LocalStorage.openDatabaseSync("PlasmaPhoneDialer", "1.0", "Call history of the Plasma Phone dialer", 1000000); - - db.transaction( - function(tx) { - // Create the database if it doesn't already exist - //callType: whether is incoming, outgoing, unanswered - tx.executeSql('CREATE TABLE IF NOT EXISTS History(id INTEGER PRIMARY KEY AUTOINCREMENT, number TEXT, time DATETIME, duration INTEGER, callType INTEGER)'); - - var rs = tx.executeSql('SELECT * FROM History'); - - for(var i = 0; i < rs.rows.length; i++) { - var row = rs.rows.item(i); - row.date = Qt.formatDate(row.time, "yyyy-MM-dd"); - row.originalIndex = historyModel.count; - historyModel.append(row); - } - } - ) - } -//END DATABASE - //BEGIN MODELS - ListModel { + CallHistoryModel { id: historyModel }