From bcee4d9d199cc02abc07398d32ccded6ecf57572 Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Fri, 25 Nov 2016 17:32:21 +0100 Subject: [PATCH] unstable, supports calls in chat --- tests/CMakeLists.txt | 2 + tests/assets/languages/en.ts | 4 + tests/assets/languages/fr.ts | 4 + tests/src/app/App.cpp | 3 +- tests/src/components/chat/ChatModel.cpp | 75 +++++++++++++++---- tests/src/components/chat/ChatModel.hpp | 30 +++++++- tests/src/components/chat/ChatProxyModel.cpp | 13 ++++ tests/src/components/chat/ChatProxyModel.hpp | 44 +++++++++++ tests/ui/modules/Linphone/Chat/Chat.qml | 9 ++- tests/ui/modules/Linphone/Chat/Event.qml | 16 +++- .../ui/views/App/MainWindow/Conversation.qml | 6 +- 11 files changed, 181 insertions(+), 25 deletions(-) create mode 100644 tests/src/components/chat/ChatProxyModel.cpp create mode 100644 tests/src/components/chat/ChatProxyModel.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 00f123e32..6328be1b0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,6 +34,7 @@ set(SOURCES src/app/Database.cpp src/app/Logger.cpp src/components/chat/ChatModel.cpp + src/components/chat/ChatProxyModel.cpp src/components/contacts/ContactModel.cpp src/components/contacts/ContactsListModel.cpp src/components/contacts/ContactsListProxyModel.cpp @@ -52,6 +53,7 @@ set(HEADERS src/app/Database.hpp src/app/Logger.hpp src/components/chat/ChatModel.hpp + src/components/chat/ChatProxyModel.hpp src/components/contacts/ContactModel.hpp src/components/contacts/ContactsListModel.hpp src/components/contacts/ContactsListProxyModel.hpp diff --git a/tests/assets/languages/en.ts b/tests/assets/languages/en.ts index 8a6e05fdb..251a18aff 100644 --- a/tests/assets/languages/en.ts +++ b/tests/assets/languages/en.ts @@ -194,6 +194,10 @@ endCall + + outgoingCall + + Home diff --git a/tests/assets/languages/fr.ts b/tests/assets/languages/fr.ts index e6c13f001..945d1f9af 100644 --- a/tests/assets/languages/fr.ts +++ b/tests/assets/languages/fr.ts @@ -194,6 +194,10 @@ endCall + + outgoingCall + + Home diff --git a/tests/src/app/App.cpp b/tests/src/app/App.cpp index a060892aa..4362886a4 100644 --- a/tests/src/app/App.cpp +++ b/tests/src/app/App.cpp @@ -4,7 +4,7 @@ #include #include -#include "../components/chat/ChatModel.hpp" +#include "../components/chat/ChatProxyModel.hpp" #include "../components/contacts/ContactModel.hpp" #include "../components/contacts/ContactsListModel.hpp" #include "../components/contacts/ContactsListProxyModel.hpp" @@ -99,6 +99,7 @@ void App::registerTypes () { qmlRegisterType("Linphone", 1, 0, "ContactsListProxyModel"); qmlRegisterType("Linphone", 1, 0, "ChatModel"); + qmlRegisterType("Linphone", 1, 0, "ChatProxyModel"); // Register singletons. qmlRegisterSingletonType( diff --git a/tests/src/components/chat/ChatModel.cpp b/tests/src/components/chat/ChatModel.cpp index 97d2fe877..0671e7457 100644 --- a/tests/src/components/chat/ChatModel.cpp +++ b/tests/src/components/chat/ChatModel.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -79,6 +81,38 @@ void ChatModel::removeAllEntries () { // ------------------------------------------------------------------- +void ChatModel::fillMessageEntry ( + QVariantMap &dest, + const shared_ptr &message +) { + dest["type"] = EntryType::MessageEntry; + dest["timestamp"] = QDateTime::fromTime_t(message->getTime()); + dest["content"] = Utils::linphoneStringToQString( + message->getText() + ); + dest["isOutgoing"] = message->isOutgoing(); +} + +void ChatModel::fillCallStartEntry ( + QVariantMap &dest, + const std::shared_ptr &call_log +) { + QDateTime timestamp = QDateTime::fromTime_t(call_log->getStartDate()); + + dest["type"] = EntryType::CallEntry; + dest["timestamp"] = timestamp; + dest["isOutgoing"] = call_log->getDir() == linphone::CallDirOutgoing; + dest["status"] = call_log->getStatus(); +} + +void ChatModel::fillCallEndEntry ( + QVariantMap &dest, + const std::shared_ptr &call_log +) { + + +} + void ChatModel::removeEntry (ChatEntryData &pair) { int type = pair.first["type"].toInt(); @@ -89,7 +123,9 @@ void ChatModel::removeEntry (ChatEntryData &pair) { ); break; case ChatModel::CallEntry: - + CoreManager::getInstance()->getCore()->removeCallLog( + static_pointer_cast(pair.second) + ); break; default: qWarning() << "Unknown chat entry type:" << type; @@ -114,29 +150,42 @@ void ChatModel::setSipAddress (const QString &sip_address) { // Invalid old sip address entries. m_entries.clear(); - m_chat_room = - CoreManager::getInstance()->getCore()->getChatRoomFromUri( - Utils::qStringToLinphoneString(sip_address) - ); + shared_ptr core = CoreManager::getInstance()->getCore(); + string std_sip_address = Utils::qStringToLinphoneString(sip_address); + + m_chat_room = core->getChatRoomFromUri(std_sip_address); // Get messages. for (auto &message : m_chat_room->getHistory(0)) { QVariantMap map; - map["type"] = EntryType::MessageEntry; - map["timestamp"] = QDateTime::fromTime_t(message->getTime()); - map["content"] = Utils::linphoneStringToQString( - message->getText() - ); - map["isOutgoing"] = message->isOutgoing(); - + fillMessageEntry(map, message); m_entries << qMakePair(map, static_pointer_cast(message)); } // Get calls. - // TODO. + for (auto &call_log : core->getCallHistoryForAddress(m_chat_room->getPeerAddress())) { + QVariantMap start, end; + fillCallStartEntry(start, call_log); + + ChatEntryData pair = qMakePair(start, static_pointer_cast(call_log)); + + auto it = lower_bound( + m_entries.begin(), m_entries.end(), pair, + [](const ChatEntryData &a, const ChatEntryData &b) { + return a.first["timestamp"] < b.first["timestamp"]; + } + ); + + m_entries.insert(it, pair); + } endResetModel(); emit sipAddressChanged(sip_address); } + + // QT_TR_NOOP('endCall'), + // QT_TR_NOOP('incomingCall'), + // QT_TR_NOOP('lostIncomingCall'), + // QT_TR_NOOP('lostOutgoingCall') diff --git a/tests/src/components/chat/ChatModel.hpp b/tests/src/components/chat/ChatModel.hpp index 398454684..4df253047 100644 --- a/tests/src/components/chat/ChatModel.hpp +++ b/tests/src/components/chat/ChatModel.hpp @@ -7,6 +7,8 @@ // =================================================================== class ChatModel : public QAbstractListModel { + friend class ChatProxyModel; + Q_OBJECT; Q_PROPERTY( @@ -16,6 +18,9 @@ class ChatModel : public QAbstractListModel { NOTIFY sipAddressChanged ); +signals: + void sipAddressChanged (const QString &sipAddress); + public: enum Roles { ChatEntry = Qt::DisplayRole, @@ -28,6 +33,13 @@ public: }; Q_ENUM(EntryType); + enum CallStatus { + CallStatusDeclined = linphone::CallStatusDeclined, + CallStatusMissed = linphone::CallStatusMissed, + CallStatusSuccess = linphone::CallStatusSuccess + }; + Q_ENUM(CallStatus); + ChatModel (QObject *parent = Q_NULLPTR) : QAbstractListModel(parent) {} int rowCount (const QModelIndex &index = QModelIndex()) const { @@ -44,12 +56,24 @@ public slots: void removeEntry (int id); void removeAllEntries (); -signals: - void sipAddressChanged (const QString &sipAddress); - private: typedef QPair > ChatEntryData; + void fillMessageEntry ( + QVariantMap &dest, + const std::shared_ptr &message + ); + + void fillCallStartEntry ( + QVariantMap &dest, + const std::shared_ptr &call_log + ); + + void fillCallEndEntry ( + QVariantMap &dest, + const std::shared_ptr &call_log + ); + void removeEntry (ChatEntryData &pair); QString getSipAddress () const; diff --git a/tests/src/components/chat/ChatProxyModel.cpp b/tests/src/components/chat/ChatProxyModel.cpp new file mode 100644 index 000000000..80173afa3 --- /dev/null +++ b/tests/src/components/chat/ChatProxyModel.cpp @@ -0,0 +1,13 @@ +#include "ChatProxyModel.hpp" + +// =================================================================== + +ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { + m_chat_model.setParent(this); + setSourceModel(&m_chat_model); + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + +bool ChatProxyModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const { + return true; // TODO. +} diff --git a/tests/src/components/chat/ChatProxyModel.hpp b/tests/src/components/chat/ChatProxyModel.hpp new file mode 100644 index 000000000..be3a0198d --- /dev/null +++ b/tests/src/components/chat/ChatProxyModel.hpp @@ -0,0 +1,44 @@ +#ifndef CHAT_PROXY_MODEL_H_ +#define CHAT_PROXY_MODEL_H_ + +#include + +#include "ChatModel.hpp" + +class ChatProxyModel : public QSortFilterProxyModel { + Q_OBJECT; + + Q_PROPERTY( + QString sipAddress + READ getSipAddress + WRITE setSipAddress + NOTIFY sipAddressChanged + ); + +signals: + void sipAddressChanged (const QString &sipAddress); + +public: + ChatProxyModel (QObject *parent = Q_NULLPTR); + +public slots: + ChatModel *getChatModel () { + return &m_chat_model; + } + +protected: + bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const; + +private: + QString getSipAddress () const { + return m_chat_model.getSipAddress(); + } + + void setSipAddress (const QString &sip_address) { + m_chat_model.setSipAddress(sip_address); + } + + ChatModel m_chat_model; +}; + +#endif // CHAT_PROXY_MODEL_H_ diff --git a/tests/ui/modules/Linphone/Chat/Chat.qml b/tests/ui/modules/Linphone/Chat/Chat.qml index 340fdc4e2..1a4aad23d 100644 --- a/tests/ui/modules/Linphone/Chat/Chat.qml +++ b/tests/ui/modules/Linphone/Chat/Chat.qml @@ -11,6 +11,7 @@ import Linphone.Styles 1.0 ColumnLayout { property var contact + // Can be a model or a proxy chat model. property alias model: chat.model // ----------------------------------------------------------------- @@ -80,12 +81,18 @@ ColumnLayout { delegate: Rectangle { id: entry + // Chat supports model and proxy model. + function getModel () { + var model = chat.model + return model.getChatModel ? model.getChatModel() : model + } + function isHoverEntry () { return mouseArea.containsMouse } function removeEntry () { - chat.model.removeEntry(index) + getModel().removeEntry(index) } anchors { diff --git a/tests/ui/modules/Linphone/Chat/Event.qml b/tests/ui/modules/Linphone/Chat/Event.qml index 94eb696a4..06722836e 100644 --- a/tests/ui/modules/Linphone/Chat/Event.qml +++ b/tests/ui/modules/Linphone/Chat/Event.qml @@ -1,18 +1,27 @@ import QtQuick 2.7 import Common 1.0 +import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 // =================================================================== Row { + property string _type: { + if ($chatEntry.status === ChatModel.CallStatusSuccess) { + return $chatEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' + } + + return '' + } + height: ChatStyle.entry.lineHeight spacing: ChatStyle.entry.message.extraContent.spacing Icon { height: parent.height - icon: $content + icon: _type iconSize: ChatStyle.entry.event.iconSize width: ChatStyle.entry.metaWidth } @@ -25,8 +34,7 @@ Row { property var i18n: [ QT_TR_NOOP('endCall'), QT_TR_NOOP('incomingCall'), - QT_TR_NOOP('lostIncomingCall'), - QT_TR_NOOP('lostOutgoingCall') + QT_TR_NOOP('outgoingCall') ] } } @@ -37,7 +45,7 @@ Row { pointSize: ChatStyle.entry.event.text.fontSize } height: parent.height - text: qsTr(Utils.snakeToCamel($content)) + text: qsTr(Utils.snakeToCamel(_type)) verticalAlignment: Text.AlignVCenter } diff --git a/tests/ui/views/App/MainWindow/Conversation.qml b/tests/ui/views/App/MainWindow/Conversation.qml index 239dbcbaf..6cc25ecea 100644 --- a/tests/ui/views/App/MainWindow/Conversation.qml +++ b/tests/ui/views/App/MainWindow/Conversation.qml @@ -23,7 +23,7 @@ ColumnLayout { descriptionText: qsTr('removeAllEntriesDescription'), exitHandler: function (status) { if (status) { - chatModel.removeAllEntries() + chatProxyModel.removeAllEntries() } }, title: qsTr('removeAllEntriesTitle') @@ -145,8 +145,8 @@ ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true contact: parent._contact - model: ChatModel { - id: chatModel + model: ChatProxyModel { + id: chatProxyModel sipAddress: conversation.sipAddress }