mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-07-26 14:24:45 +00:00
Use a proper model for the call history
This commit is contained in:
parent
d15262b394
commit
c377254bd1
8 changed files with 241 additions and 90 deletions
|
|
@ -25,7 +25,7 @@ include(ECMGenerateHeaders)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
include(FeatureSummary)
|
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 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma Service Declarative I18n KIO People)
|
||||||
find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons Notifications)
|
find_package(KF5 REQUIRED COMPONENTS PlasmaQuick DBusAddons Notifications)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ set(plasmaphonedialer_SRCS
|
||||||
main.cpp
|
main.cpp
|
||||||
dialerutils.cpp
|
dialerutils.cpp
|
||||||
call-handler.cpp
|
call-handler.cpp
|
||||||
|
callhistorymodel.cpp
|
||||||
call-manager.cpp
|
call-manager.cpp
|
||||||
resources.qrc
|
resources.qrc
|
||||||
)
|
)
|
||||||
|
|
@ -15,6 +16,7 @@ target_link_libraries(plasmaphonedialer
|
||||||
Qt5::Gui
|
Qt5::Gui
|
||||||
Qt5::Quick
|
Qt5::Quick
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
|
Qt5::Sql
|
||||||
KF5::Declarative
|
KF5::Declarative
|
||||||
KF5::I18n
|
KF5::I18n
|
||||||
KF5::QuickAddons
|
KF5::QuickAddons
|
||||||
|
|
|
||||||
149
dialer/src/callhistorymodel.cpp
Normal file
149
dialer/src/callhistorymodel.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2019 Nicolas Fella <nicolas.fella@gmx.de>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "callhistorymodel.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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<int, QByteArray> CallHistoryModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> 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);
|
||||||
|
}
|
||||||
69
dialer/src/callhistorymodel.h
Normal file
69
dialer/src/callhistorymodel.h
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2019 Nicolas Fella <nicolas.fella@gmx.de>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
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<int, QByteArray> 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<CallData> 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;
|
||||||
|
};
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "dialerutils.h"
|
#include "dialerutils.h"
|
||||||
#include "call-handler.h"
|
#include "call-handler.h"
|
||||||
|
#include "callhistorymodel.h"
|
||||||
|
|
||||||
#include <TelepathyQt/Types>
|
#include <TelepathyQt/Types>
|
||||||
#include <TelepathyQt/Debug>
|
#include <TelepathyQt/Debug>
|
||||||
|
|
@ -109,6 +110,9 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
|
||||||
|
qmlRegisterType<CallHistoryModel>("org.kde.plasma.dialer", 1, 0, "CallHistoryModel");
|
||||||
|
qmlRegisterType<CallHistorySortFilterModel>("org.kde.plasma.dialer", 1, 0, "CallHistorySortFilterModel");
|
||||||
|
|
||||||
Tp::registerTypes();
|
Tp::registerTypes();
|
||||||
|
|
||||||
Tp::AccountFactoryPtr accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(),
|
Tp::AccountFactoryPtr accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(),
|
||||||
|
|
|
||||||
|
|
@ -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.components 2.0 as PlasmaComponents
|
||||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import org.kde.plasma.dialer 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
||||||
function secondsToTimeString(seconds) {
|
function secondsToTimeString(seconds) {
|
||||||
|
|
@ -38,12 +40,12 @@ Item {
|
||||||
PlasmaComponents.Label {
|
PlasmaComponents.Label {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: i18n("No recent calls")
|
text: i18n("No recent calls")
|
||||||
visible: historyModel.count == 0
|
visible: view.count == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: historyModel.count > 0
|
visible: view.count > 0
|
||||||
PlasmaComponents.ToolBar {
|
PlasmaComponents.ToolBar {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
tools: RowLayout {
|
tools: RowLayout {
|
||||||
|
|
@ -55,7 +57,7 @@ Item {
|
||||||
text: i18n("All")
|
text: i18n("All")
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
filterModel.filterString = "";
|
filterModel.setFilterFixedString("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +66,7 @@ Item {
|
||||||
text: i18n("Missed")
|
text: i18n("Missed")
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
filterModel.filterString = "0";
|
filterModel.setFilterFixedString("0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +76,7 @@ Item {
|
||||||
}
|
}
|
||||||
PlasmaComponents.Button {
|
PlasmaComponents.Button {
|
||||||
text: i18n("Clear")
|
text: i18n("Clear")
|
||||||
onClicked: clearHistory();
|
onClicked: historyModel.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,12 +85,12 @@ Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
ListView {
|
ListView {
|
||||||
id: view
|
id: view
|
||||||
model: PlasmaCore.SortFilterModel {
|
model: CallHistorySortFilterModel {
|
||||||
id: filterModel
|
id: filterModel
|
||||||
sourceModel: historyModel
|
sourceModel: historyModel
|
||||||
filterRole: "callType"
|
filterRole: CallHistoryModel.CallTypeRole
|
||||||
sortRole: "time"
|
sortRole: CallHistoryModel.TimeRole
|
||||||
sortOrder: Qt.DescendingOrder
|
Component.onCompleted: sort(0, Qt.DescendingOrder)
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
property: "date"
|
property: "date"
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import QtQuick.Controls 2.2 as Controls
|
||||||
import org.kde.kirigami 2.2 as Kirigami
|
import org.kde.kirigami 2.2 as Kirigami
|
||||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: delegateParent
|
id: delegateParent
|
||||||
width: view.width
|
width: view.width
|
||||||
|
|
@ -50,7 +49,7 @@ Item {
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
}
|
}
|
||||||
ScriptAction {
|
ScriptAction {
|
||||||
script: removeCallFromHistory(model.originalIndex);
|
script: filterModel.remove(model.index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import QtQuick.LocalStorage 2.0
|
||||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
import org.kde.kirigami 2.0 as Kirigami
|
import org.kde.kirigami 2.0 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.dialer 1.0
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|
@ -57,7 +59,7 @@ ApplicationWindow {
|
||||||
} else {
|
} else {
|
||||||
callType = 2;
|
callType = 2;
|
||||||
}
|
}
|
||||||
insertCallInHistory(callContactNumber, callDuration, callType);
|
historyModel.addCall(callContactNumber, callDuration, callType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,86 +73,10 @@ ApplicationWindow {
|
||||||
function call(number) {
|
function call(number) {
|
||||||
dialerUtils.dial(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
|
//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
|
//BEGIN MODELS
|
||||||
ListModel {
|
CallHistoryModel {
|
||||||
id: historyModel
|
id: historyModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue