From 0b3d6bee85de52e228b29df6b68b8d8eca1527ff Mon Sep 17 00:00:00 2001 From: Aniket Kumar Date: Mon, 10 Aug 2020 00:30:57 +0530 Subject: [PATCH] Implementing Attachment class and adding support to display the thumbnails of attachments in QML. --- interfaces/conversationmessage.cpp | 26 +++++++- interfaces/conversationmessage.h | 67 ++++++++++++++++++++- plugins/sms/smsplugin.h | 10 ++++ smsapp/CMakeLists.txt | 2 + smsapp/attachmentinfo.cpp | 30 ++++++++++ smsapp/attachmentinfo.h | 51 ++++++++++++++++ smsapp/conversationlistmodel.cpp | 9 ++- smsapp/conversationmodel.cpp | 38 ++++++++++-- smsapp/conversationmodel.h | 3 + smsapp/main.cpp | 2 + smsapp/qml/ChatMessage.qml | 24 +++++++- smsapp/qml/ConversationDisplay.qml | 1 + smsapp/qml/MessageAttachments.qml | 96 ++++++++++++++++++++++++++++++ smsapp/resources.qrc | 15 ++--- smsapp/thumbnailsprovider.cpp | 44 ++++++++++++++ smsapp/thumbnailsprovider.h | 41 +++++++++++++ 16 files changed, 438 insertions(+), 21 deletions(-) create mode 100644 smsapp/attachmentinfo.cpp create mode 100644 smsapp/attachmentinfo.h create mode 100644 smsapp/qml/MessageAttachments.qml create mode 100644 smsapp/thumbnailsprovider.cpp create mode 100644 smsapp/thumbnailsprovider.h diff --git a/interfaces/conversationmessage.cpp b/interfaces/conversationmessage.cpp index 0877597ec..77ed7b915 100644 --- a/interfaces/conversationmessage.cpp +++ b/interfaces/conversationmessage.cpp @@ -33,7 +33,6 @@ ConversationMessage::ConversationMessage(const QVariantMap& args) m_threadID(args[QStringLiteral("thread_id")].toLongLong()), m_uID(args[QStringLiteral("_id")].toInt()) { - QString test = QLatin1String(args[QStringLiteral("addresses")].typeName()); QVariantList jsonAddresses = args[QStringLiteral("addresses")].toList(); for (const QVariant& addressField : jsonAddresses) { const auto& rawAddress = addressField.toMap(); @@ -41,6 +40,18 @@ ConversationMessage::ConversationMessage(const QVariantMap& args) } QVariantMap::const_iterator subID_it = args.find(QStringLiteral("sub_id")); m_subID = subID_it == args.end() ? -1 : subID_it->toLongLong(); + + if (args.contains(QStringLiteral("attachments"))) { + QVariant attachment = args.value(QStringLiteral("attachments")); + const QVariantList jsonAttachments = attachment.toList(); + for (const QVariant& attachmentField : jsonAttachments) { + const auto& rawAttachment = attachmentField.toMap(); + m_attachments.append(Attachment(rawAttachment[QStringLiteral("part_id")].value(), + rawAttachment[QStringLiteral("mime_type")].value(), + rawAttachment[QStringLiteral("encoded_thumbnail")].value(), + rawAttachment[QStringLiteral("unique_identifier")].value())); + } + } } ConversationMessage::ConversationMessage (const qint32& eventField, const QString& body, @@ -48,7 +59,8 @@ ConversationMessage::ConversationMessage (const qint32& eventField, const QStrin const qint32& type, const qint32& read, const qint64& threadID, const qint32& uID, - const qint64& subID) + const qint64& subID, + const QList& attachments) : m_eventField(eventField) , m_body(body) , m_addresses(addresses) @@ -58,6 +70,7 @@ ConversationMessage::ConversationMessage (const qint32& eventField, const QStrin , m_threadID(threadID) , m_uID(uID) , m_subID(subID) + , m_attachments(attachments) { } @@ -73,6 +86,13 @@ ConversationAddress::ConversationAddress(QString address) : m_address(address) {} +Attachment::Attachment(qint64 partID, QString mimeType, QString base64EncodedFile, QString uniqueIdentifier) + : m_partID(partID) + , m_mimeType(mimeType) + , m_base64EncodedFile(base64EncodedFile) + , m_uniqueIdentifier(uniqueIdentifier) +{} + void ConversationMessage::registerDbusType() { qDBusRegisterMetaType(); @@ -81,4 +101,6 @@ void ConversationMessage::registerDbusType() qRegisterMetaType(); qDBusRegisterMetaType>(); qRegisterMetaType>(); + qDBusRegisterMetaType(); + qRegisterMetaType(); } diff --git a/interfaces/conversationmessage.h b/interfaces/conversationmessage.h index 84b7a0c74..ac5242b5b 100644 --- a/interfaces/conversationmessage.h +++ b/interfaces/conversationmessage.h @@ -26,6 +26,7 @@ #include "kdeconnectinterfaces_export.h" class ConversationAddress; +class Attachment; class KDECONNECTINTERFACES_EXPORT ConversationMessage { @@ -61,7 +62,8 @@ public: ConversationMessage(const qint32& eventField, const QString& body, const QList& addresses, const qint64& date, const qint32& type, const qint32& read, - const qint64& threadID, const qint32& uID, const qint64& subID); + const qint64& threadID, const qint32& uID, const qint64& subID, + const QList& attachments); static ConversationMessage fromDBus(const QDBusVariant&); static void registerDbusType(); @@ -75,12 +77,14 @@ public: qint64 threadID() const { return m_threadID; } qint32 uID() const { return m_uID; } qint64 subID() const { return m_subID; } + QList attachments() const { return m_attachments; } bool containsTextBody() const { return (eventField() & ConversationMessage::EventTextMessage); } bool isMultitarget() const { return (eventField() & ConversationMessage::EventMultiTarget); } bool isIncoming() const { return type() == MessageTypeInbox; } bool isOutgoing() const { return type() == MessageTypeSent; } + bool containsAttachment() const { return !attachments().isEmpty(); } /** * Return the address of the other party of a single-target conversation @@ -135,6 +139,11 @@ protected: * Value which determines SIM id (optional) */ qint64 m_subID; + + /** + * Contains attachment related info of a MMS message (optional) + */ + QList m_attachments; }; class KDECONNECTINTERFACES_EXPORT ConversationAddress @@ -148,6 +157,24 @@ private: QString m_address; }; +class KDECONNECTINTERFACES_EXPORT Attachment +{ +public: + Attachment() {} + Attachment(qint64 prtID, QString mimeType, QString base64EncodedFile, QString uniqueIdentifier); + + qint64 partID() const { return m_partID; } + QString mimeType() const { return m_mimeType; } + QString base64EncodedFile() const { return m_base64EncodedFile; } + QString uniqueIdentifier() const { return m_uniqueIdentifier; } + +private: + qint64 m_partID; // Part ID of the attachment of the message + QString m_mimeType; // Type of attachment (image, video, audio etc.) + QString m_base64EncodedFile; // Base64 encoded string of a file + QString m_uniqueIdentifier; // unique name of the attachment +}; + inline QDBusArgument &operator<<(QDBusArgument &argument, const ConversationMessage &message) { argument.beginStructure(); @@ -159,7 +186,8 @@ inline QDBusArgument &operator<<(QDBusArgument &argument, const ConversationMess << message.read() << message.threadID() << message.uID() - << message.subID(); + << message.subID() + << message.attachments(); argument.endStructure(); return argument; } @@ -175,6 +203,7 @@ inline const QDBusArgument &operator>>(const QDBusArgument &argument, Conversati qint64 threadID; qint32 uID; qint64 m_subID; + QList attachments; argument.beginStructure(); argument >> event; @@ -186,9 +215,10 @@ inline const QDBusArgument &operator>>(const QDBusArgument &argument, Conversati argument >> threadID; argument >> uID; argument >> m_subID; + argument >> attachments; argument.endStructure(); - message = ConversationMessage(event, body, addresses, date, type, read, threadID, uID, m_subID); + message = ConversationMessage(event, body, addresses, date, type, read, threadID, uID, m_subID, attachments); return argument; } @@ -214,7 +244,38 @@ inline const QDBusArgument& operator>>(const QDBusArgument& argument, Conversati return argument; } +inline QDBusArgument& operator<<(QDBusArgument& argument, const Attachment& attachment) +{ + argument.beginStructure(); + argument << attachment.partID() + << attachment.mimeType() + << attachment.base64EncodedFile() + << attachment.uniqueIdentifier(); + argument.endStructure(); + return argument; +} + +inline const QDBusArgument& operator>>(const QDBusArgument& argument, Attachment& attachment) +{ + qint64 partID; + QString mimeType; + QString encodedFile; + QString uniqueIdentifier; + + argument.beginStructure(); + argument >> partID; + argument >> mimeType; + argument >> encodedFile; + argument >> uniqueIdentifier; + argument.endStructure(); + + attachment = Attachment(partID, mimeType, encodedFile, uniqueIdentifier); + + return argument; +} + Q_DECLARE_METATYPE(ConversationMessage) Q_DECLARE_METATYPE(ConversationAddress) +Q_DECLARE_METATYPE(Attachment) #endif /* PLUGINS_TELEPHONY_CONVERSATIONMESSAGE_H_ */ diff --git a/plugins/sms/smsplugin.h b/plugins/sms/smsplugin.h index 6e3f9271d..6a1622573 100644 --- a/plugins/sms/smsplugin.h +++ b/plugins/sms/smsplugin.h @@ -69,6 +69,16 @@ * // If this value is not defined or if it does not match a valid subscriber_id known by * // Android, we will use whatever subscriber ID Android gives us as the default * + * "attachments": > // List of Attachment objects, one for each attached file in the message. + * + * An Attachment object looks like: + * { + * "part_id": // part_id of the attachment used to read the file from MMS database + * "mime_type": // contains the mime type of the file (image, video, audio, etc.) + * "encoded_thumbnail": // Optional base64-encoded thumbnail preview of the content for types which support it + * "unique_identifier": // Unique name of te file + * } + * * An Address object looks like: * { * "address": // Address (phone number, email address, etc.) of this object diff --git a/smsapp/CMakeLists.txt b/smsapp/CMakeLists.txt index f7ef1e5c8..df22fb6e3 100644 --- a/smsapp/CMakeLists.txt +++ b/smsapp/CMakeLists.txt @@ -41,6 +41,8 @@ add_executable(kdeconnect-sms conversationmodel.cpp conversationssortfilterproxymodel.cpp resources.qrc + thumbnailsprovider.cpp + attachmentinfo.cpp ${sms_debug_files_SRCS}) target_link_libraries(kdeconnect-sms diff --git a/smsapp/attachmentinfo.cpp b/smsapp/attachmentinfo.cpp new file mode 100644 index 000000000..e4872c53e --- /dev/null +++ b/smsapp/attachmentinfo.cpp @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2020 Aniket Kumar + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . + */ + +#include "attachmentinfo.h" + +AttachmentInfo::AttachmentInfo() +{} + +AttachmentInfo::AttachmentInfo(const Attachment& attachment) + : m_partID(attachment.partID()), + m_mimeType(attachment.mimeType()), + m_uniqueIdentifier(attachment.uniqueIdentifier()) +{} diff --git a/smsapp/attachmentinfo.h b/smsapp/attachmentinfo.h new file mode 100644 index 000000000..458b3495b --- /dev/null +++ b/smsapp/attachmentinfo.h @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2020 Aniket Kumar + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . + */ + +#ifndef ATTACHMENTINFO_H +#define ATTACHMENTINFO_H + +#include "conversationmessage.h" + +/** + * This class is a compressed version of Attachment class + * as it will be exposed to the QML in a list object + */ +class AttachmentInfo +{ + Q_GADGET + Q_PROPERTY(qint64 partID READ partID) + Q_PROPERTY(QString mimeType READ mimeType) + Q_PROPERTY(QString uniqueIdentifier READ uniqueIdentifier) + +public: + AttachmentInfo(); + AttachmentInfo(const Attachment& attachment); + + qint64 partID() const { return m_partID; } + QString mimeType() const { return m_mimeType; } + QString uniqueIdentifier() const { return m_uniqueIdentifier; } + +private: + qint64 m_partID; + QString m_mimeType; + QString m_uniqueIdentifier; +}; + +#endif // ATTACHMENTINFO_H diff --git a/smsapp/conversationlistmodel.cpp b/smsapp/conversationlistmodel.cpp index b1ae20dd3..331578600 100644 --- a/smsapp/conversationlistmodel.cpp +++ b/smsapp/conversationlistmodel.cpp @@ -200,7 +200,14 @@ void ConversationListModel::createRowFromMessage(const ConversationMessage& mess // TODO: Upgrade to support other kinds of media // Get the body that we should display - QString displayBody = message.containsTextBody() ? message.body() : i18n("(Unsupported Message Type)"); + QString displayBody; + if (message.containsTextBody()) { + displayBody = message.body(); + } else if (message.containsAttachment()) { + const QString mimeType = message.attachments().last().mimeType(); + const QString type = QStringLiteral("\"") + mimeType.left(mimeType.indexOf(QStringLiteral("/"))) + QStringLiteral(" file\""); + displayBody = type; + } // For displaying single line subtitle out of the multiline messages to keep the ListItems consistent displayBody = displayBody.mid(0, displayBody.indexOf(QStringLiteral("\n"))); diff --git a/smsapp/conversationmodel.cpp b/smsapp/conversationmodel.cpp index 6c154e529..de4f99062 100644 --- a/smsapp/conversationmodel.cpp +++ b/smsapp/conversationmodel.cpp @@ -22,9 +22,12 @@ #include "conversationmodel.h" #include +#include +#include #include "interfaces/conversationmessage.h" #include "smshelper.h" +#include "attachmentinfo.h" #include "sms_conversation_debug.h" @@ -37,6 +40,7 @@ ConversationModel::ConversationModel(QObject* parent) roles.insert(DateRole, "date"); roles.insert(SenderRole, "sender"); roles.insert(AvatarRole, "avatar"); + roles.insert(AttachmentsRole, "attachments"); setItemRoleNames(roles); } @@ -59,6 +63,7 @@ void ConversationModel::setThreadId(const qint64& threadId) knownMessageIDs.clear(); if (m_threadId != INVALID_THREAD_ID && !m_deviceId.isEmpty()) { requestMoreMessages(); + m_thumbnailsProvider->clear(); } } @@ -81,6 +86,12 @@ void ConversationModel::setDeviceId(const QString& deviceId) connect(m_conversationsInterface, SIGNAL(conversationUpdated(QDBusVariant)), this, SLOT(handleConversationUpdate(QDBusVariant))); connect(m_conversationsInterface, SIGNAL(conversationLoaded(qint64, quint64)), this, SLOT(handleConversationLoaded(qint64, quint64))); connect(m_conversationsInterface, SIGNAL(conversationCreated(QDBusVariant)), this, SLOT(handleConversationCreated(QDBusVariant))); + + QQmlApplicationEngine* engine = qobject_cast(QQmlEngine::contextForObject(this)->engine()); + m_thumbnailsProvider = dynamic_cast(engine->imageProvider(QStringLiteral("thumbnailsProvider"))); + + // Clear any previous data on device change + m_thumbnailsProvider->clear(); } void ConversationModel::setAddressList(const QList& addressList) { @@ -109,7 +120,7 @@ void ConversationModel::requestMoreMessages(const quint32& howMany) if (m_threadId == INVALID_THREAD_ID) { return; } - const auto& numMessages = rowCount(); + const auto& numMessages = knownMessageIDs.size(); m_conversationsInterface->requestConversation(m_threadId, numMessages, numMessages + howMany); } @@ -132,18 +143,35 @@ void ConversationModel::createRowFromMessage(const ConversationMessage& message, return; } - // TODO: Upgrade to support other kinds of media - // Get the body that we should display - QString displayBody = message.containsTextBody() ? message.body() : i18n("(Unsupported Message Type)"); - ConversationAddress sender = message.addresses().first(); QString senderName = message.isIncoming() ? SmsHelper::getTitleForAddresses({sender}) : QString(); + QString displayBody = message.body(); auto item = new QStandardItem; item->setText(displayBody); item->setData(message.isOutgoing(), FromMeRole); item->setData(message.date(), DateRole); item->setData(senderName, SenderRole); + + QList attachmentInfoList; + const QList attachmentList = message.attachments(); + + for (const Attachment& attachment : attachmentList) { + AttachmentInfo attachmentInfo(attachment); + attachmentInfoList.append(QVariant::fromValue(attachmentInfo)); + + if (attachment.mimeType().startsWith(QLatin1String("image")) || attachment.mimeType().startsWith(QLatin1String("video"))) { + // The message contains thumbnail as Base64 String, convert it back into image thumbnail + const QByteArray byteArray = attachment.base64EncodedFile().toUtf8(); + QPixmap thumbnail; + thumbnail.loadFromData(QByteArray::fromBase64(byteArray)); + + m_thumbnailsProvider->addImage(attachment.uniqueIdentifier(), thumbnail.toImage()); + } + } + + item->setData(attachmentInfoList, AttachmentsRole); + insertRow(pos, item); knownMessageIDs.insert(message.uID()); } diff --git a/smsapp/conversationmodel.h b/smsapp/conversationmodel.h index 2d73184bf..238e28758 100644 --- a/smsapp/conversationmodel.h +++ b/smsapp/conversationmodel.h @@ -27,6 +27,7 @@ #include "interfaces/conversationmessage.h" #include "interfaces/dbusinterfaces.h" +#include "thumbnailsprovider.h" #define INVALID_THREAD_ID -1 @@ -47,6 +48,7 @@ public: SenderRole, // The sender of the message. Undefined if this is an outgoing message DateRole, AvatarRole, // URI to the avatar of the sender of the message. Undefined if outgoing. + AttachmentsRole, // The list of attachments. Undefined if there is no attachment in a message }; Q_ENUM(Roles) @@ -78,6 +80,7 @@ private: void createRowFromMessage(const ConversationMessage &message, int pos); DeviceConversationsDbusInterface* m_conversationsInterface; + ThumbnailsProvider* m_thumbnailsProvider; QString m_deviceId; qint64 m_threadId = INVALID_THREAD_ID; QList m_addressList; diff --git a/smsapp/main.cpp b/smsapp/main.cpp index 98caf8211..6d344fee3 100644 --- a/smsapp/main.cpp +++ b/smsapp/main.cpp @@ -22,6 +22,7 @@ #include "conversationmodel.h" #include "conversationlistmodel.h" #include "conversationssortfilterproxymodel.h" +#include "thumbnailsprovider.h" #include "kdeconnect-version.h" #include @@ -74,6 +75,7 @@ int main(int argc, char *argv[]) QQmlApplicationEngine engine; engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); + engine.addImageProvider(QStringLiteral("thumbnailsProvider"), new ThumbnailsProvider); engine.rootContext()->setContextProperties({ { QStringLiteral("initialMessage"), initialMessage }, { QStringLiteral("initialDevice"), deviceid }, diff --git a/smsapp/qml/ChatMessage.qml b/smsapp/qml/ChatMessage.qml index 3b02c10fe..fdaa14072 100644 --- a/smsapp/qml/ChatMessage.qml +++ b/smsapp/qml/ChatMessage.qml @@ -35,6 +35,7 @@ Item { property date dateTime property string name property bool multiTarget + property var attachmentList signal messageCopyRequested(string message) @@ -60,6 +61,7 @@ Item { height: messageColumn.height Rectangle { + id: messageBox Layout.maximumWidth: applicationWindow().wideScreen ? Math.min(messageColumn.contentWidth, root.width * 0.6) : messageColumn.contentWidth Layout.fillWidth: true Layout.alignment: root.sentByMe ? Qt.AlignRight : Qt.AlignLeft @@ -103,7 +105,7 @@ Item { width: parent.width height: childrenRect.height - property int contentWidth: Math.max(messageLabel.implicitWidth, dateLabel.implicitWidth) + property int contentWidth: Math.max(Math.max(messageLabel.implicitWidth, attachmentGrid.implicitWidth), dateLabel.implicitWidth) Label { id: authorLabel width: parent.width @@ -115,8 +117,26 @@ Item { horizontalAlignment: messageLabel.horizontalAlignment } + Grid { + id: attachmentGrid + columns: 2 + padding: attachmentList.length > 0 ? Kirigami.Units.largeSpacing : 0 + layoutDirection: root.sentByMe ? Qt.RightToLeft : Qt.LeftToRight + + Repeater { + model: attachmentList + + delegate: MessageAttachments { + mimeType: modelData.mimeType + partID: modelData.partID + uniqueIdentifier: modelData.uniqueIdentifier + } + } + } + TextEdit { id: messageLabel + visible: messageBody != "" selectByMouse: true readOnly: true leftPadding: Kirigami.Units.largeSpacing @@ -141,8 +161,6 @@ Item { } } - - Menu { id: contextMenu exit: Transition {PropertyAction { target: messageLabel; property: "persistentSelection"; value: false }} diff --git a/smsapp/qml/ConversationDisplay.qml b/smsapp/qml/ConversationDisplay.qml index 245b96f60..5621daa18 100644 --- a/smsapp/qml/ConversationDisplay.qml +++ b/smsapp/qml/ConversationDisplay.qml @@ -124,6 +124,7 @@ Kirigami.ScrollablePage sentByMe: model.fromMe dateTime: new Date(model.date) multiTarget: isMultitarget + attachmentList: model.attachments width: viewport.width diff --git a/smsapp/qml/MessageAttachments.qml b/smsapp/qml/MessageAttachments.qml new file mode 100644 index 000000000..1c9ad1a01 --- /dev/null +++ b/smsapp/qml/MessageAttachments.qml @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2020 Aniket Kumar + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import QtGraphicalEffects 1.12 +import org.kde.kirigami 2.13 as Kirigami +import QtMultimedia 5.12 + +Item { + id: root + property int partID + property string mimeType + property string uniqueIdentifier + property string sourcePath + + readonly property int elementWidth: 100 + readonly property int elementHeight: 100 + + width: thumbnailElement.visible ? thumbnailElement.width : elementWidth + height: thumbnailElement.visible ? thumbnailElement.height : elementHeight + + Image { + id: thumbnailElement + visible: mimeType.match("image") || mimeType.match("video") + source: visible ? "image://thumbnailsProvider/" + root.uniqueIdentifier : "" + + property bool rounded: true + property bool adapt: true + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + layer.enabled: rounded + layer.effect: OpacityMask { + maskSource: Item { + width: thumbnailElement.width + height: thumbnailElement.height + Rectangle { + anchors.centerIn: parent + width: thumbnailElement.adapt ? thumbnailElement.width : Math.min(thumbnailElement.width, thumbnailElement.height) + height: thumbnailElement.adapt ? thumbnailElement.height : width + radius: messageBox.radius + } + } + } + + Button { + icon.name: "media-playback-start" + visible: root.mimeType.match("video") + anchors.horizontalCenter: thumbnailElement.horizontalCenter + anchors.verticalCenter: thumbnailElement.verticalCenter + } + } + + Rectangle { + id: audioElement + visible: root.mimeType.match("audio") + anchors.fill: parent + radius: messageBox.radius + color: "lightgrey" + + ColumnLayout { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + spacing: Kirigami.Units.largeSpacing + + Button { + id : audioPlayButton + icon.name: "media-playback-start" + Layout.alignment: Qt.AlignCenter + } + + Label { + text: i18nd("kdeconnect-sms", "Audio clip") + } + } + } +} diff --git a/smsapp/resources.qrc b/smsapp/resources.qrc index 8e7394e11..3c977e18d 100644 --- a/smsapp/resources.qrc +++ b/smsapp/resources.qrc @@ -1,8 +1,9 @@ - - - qml/main.qml - qml/ConversationList.qml - qml/ConversationDisplay.qml - qml/ChatMessage.qml - + + + qml/main.qml + qml/ConversationList.qml + qml/ConversationDisplay.qml + qml/ChatMessage.qml + qml/MessageAttachments.qml + diff --git a/smsapp/thumbnailsprovider.cpp b/smsapp/thumbnailsprovider.cpp new file mode 100644 index 000000000..901d07611 --- /dev/null +++ b/smsapp/thumbnailsprovider.cpp @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2020 Aniket Kumar + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . + */ + +#include "thumbnailsprovider.h" + +ThumbnailsProvider::ThumbnailsProvider() : QQuickImageProvider(QQuickImageProvider::Image) +{ +} + +QImage ThumbnailsProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize) { + Q_UNUSED(size) + Q_UNUSED(requestedSize) + + if (m_thumbnails.contains(id)) { + return m_thumbnails.value(id); + } + + return QImage(); +} + +void ThumbnailsProvider::addImage(const QString& id, const QImage& image) { + m_thumbnails.insert(id, image); +} + +void ThumbnailsProvider::clear() { + m_thumbnails.clear(); +} diff --git a/smsapp/thumbnailsprovider.h b/smsapp/thumbnailsprovider.h new file mode 100644 index 000000000..f3bba9241 --- /dev/null +++ b/smsapp/thumbnailsprovider.h @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2020 Aniket Kumar + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . + */ + +#ifndef THUMBNAILSPROVIDER_H +#define THUMBNAILSPROVIDER_H + +#include + +class ThumbnailsProvider : public QQuickImageProvider +{ +public: + ThumbnailsProvider(); + + QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; + + void addImage(const QString& id, const QImage& image); + + void clear(); + +private: + QHash m_thumbnails; +}; + +#endif // THUMBNAILSPROVIDER_H