From 8dd0111d99e293edf7641afd533b46bf5c5284b7 Mon Sep 17 00:00:00 2001 From: Simon Redman Date: Sun, 27 Sep 2020 03:51:17 +0000 Subject: [PATCH] [SMS App] Add thumbnail preview to ConversationList --- smsapp/conversationlistmodel.cpp | 18 +++++++++++++----- smsapp/conversationlistmodel.h | 1 + smsapp/qml/ConversationList.qml | 25 +++++++++++++++++++++++++ smsapp/smshelper.cpp | 24 ++++++++++++++++++++++++ smsapp/smshelper.h | 5 +++++ 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/smsapp/conversationlistmodel.cpp b/smsapp/conversationlistmodel.cpp index 6682fe66f..50e3f365f 100644 --- a/smsapp/conversationlistmodel.cpp +++ b/smsapp/conversationlistmodel.cpp @@ -32,6 +32,7 @@ ConversationListModel::ConversationListModel(QObject* parent) roles.insert(AddressesRole, "addresses"); roles.insert(ConversationIdRole, "conversationId"); roles.insert(MultitargetRole, "isMultitarget"); + roles.insert(AttachmentPreview, "attachmentPreview"); setItemRoleNames(roles); ConversationMessage::registerDbusType(); @@ -190,19 +191,23 @@ void ConversationListModel::createRowFromMessage(const ConversationMessage& mess displayBody = message.body(); } else if (message.containsAttachment()) { const QString mimeType = message.attachments().last().mimeType(); - QString type; if (mimeType.startsWith(QStringLiteral("image"))) { - type = i18nc("Used as a text placeholder when the most-recent message is an image", "Picture"); + displayBody = i18nc("Used as a text placeholder when the most-recent message is an image", "Picture"); } else if (mimeType.startsWith(QStringLiteral("video"))) { - type = i18nc("Used as a text placeholder when the most-recent message is a video", "Video"); + displayBody = i18nc("Used as a text placeholder when the most-recent message is a video", "Video"); } else { // Craft a somewhat-descriptive string, like "pdf file" - type = i18nc("Used as a text placeholder when the most-recent message is an arbitrary attachment, resulting in something like \"pdf file\"", + displayBody = i18nc("Used as a text placeholder when the most-recent message is an arbitrary attachment, resulting in something like \"pdf file\"", "%1 file", mimeType.right(mimeType.indexOf(QStringLiteral("/")))); } - displayBody = type; + } + + // Get the preview from the attachment, if it exists + QIcon attachmentPreview; + if (message.containsAttachment()) { + attachmentPreview = SmsHelper::getThumbnailForAttachment(message.attachments().last()); } // For displaying single line subtitle out of the multiline messages to keep the ListItems consistent @@ -231,6 +236,9 @@ void ConversationListModel::createRowFromMessage(const ConversationMessage& mess item->setData(displayBody, Qt::ToolTipRole); item->setData(message.date(), DateRole); item->setData(message.isMultitarget(), MultitargetRole); + if (!attachmentPreview.isNull()) { + item->setData(attachmentPreview, AttachmentPreview); + } } if (toadd) diff --git a/smsapp/conversationlistmodel.h b/smsapp/conversationlistmodel.h index 6a8ee03cb..1f913c10b 100644 --- a/smsapp/conversationlistmodel.h +++ b/smsapp/conversationlistmodel.h @@ -32,6 +32,7 @@ public: AddressesRole, // The Addresses involved in the conversation ConversationIdRole, // The ThreadID of the conversation MultitargetRole, // Indicate that this conversation is multitarget + AttachmentPreview, // A thumbnail of the attachment of the message, if any }; Q_ENUM(Roles) diff --git a/smsapp/qml/ConversationList.qml b/smsapp/qml/ConversationList.qml index f6072f571..3a8f97234 100644 --- a/smsapp/qml/ConversationList.qml +++ b/smsapp/qml/ConversationList.qml @@ -221,6 +221,7 @@ Kirigami.ScrollablePage label: display subtitle: toolTip + property var thumbnail: attachmentPreview function startChat() { applicationWindow().pageStack.push(chatView, { @@ -236,6 +237,30 @@ Kirigami.ScrollablePage startChat(); view.currentIndex = index } + + Kirigami.Icon { + id: thumbnailItem + source: { + if (!listItem.thumbnail) { + return undefined + } + if (listItem.thumbnail.hasOwnProperty) { + if (listItem.thumbnail.hasOwnProperty("name") && listItem.thumbnail.name !== "") + return listItem.thumbnail.name; + if (listItem.thumbnail.hasOwnProperty("source")) + return listItem.thumbnail.source; + } + return listItem.thumbnail; + } + property int size: Kirigami.Units.iconSizes.huge + Layout.minimumHeight: size + Layout.maximumHeight: size + Layout.minimumWidth: size + selected: (listItem.highlighted || listItem.checked || (listItem.pressed && listItem.supportsMouseEvents)) + opacity: 1 + visible: source != undefined + } + // Keep the currently-open chat highlighted even if this element is not focused highlighted: view.currentIndex == index } diff --git a/smsapp/smshelper.cpp b/smsapp/smshelper.cpp index 2be06c54a..d3d3a5125 100644 --- a/smsapp/smshelper.cpp +++ b/smsapp/smshelper.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -453,3 +455,25 @@ quint64 SmsHelper::totalMessageSize(const QList& urls, const QString& text return totalSize; } + +QIcon SmsHelper::getThumbnailForAttachment(const Attachment& attachment) { + static const QMimeDatabase mimeDatabase; + const QByteArray rawData = QByteArray::fromBase64(attachment.base64EncodedFile().toUtf8()); + + if (attachment.mimeType().startsWith(QStringLiteral("image")) + || attachment.mimeType().startsWith(QStringLiteral("video"))) { + QPixmap preview; + preview.loadFromData(rawData); + return QIcon(preview); + } else { + const QMimeType mimeType = mimeDatabase.mimeTypeForData(rawData); + const QIcon mimeIcon = QIcon::fromTheme(mimeType.iconName()); + if (mimeIcon.isNull()) { + // I am not sure if QIcon::isNull will actually tell us what we care about but I don't + // know how to trigger the case where we need to use genericIconName instead of iconName + return QIcon::fromTheme(mimeType.genericIconName()); + } else { + return mimeIcon; + } + } +} diff --git a/smsapp/smshelper.h b/smsapp/smshelper.h index 67c08cf9c..806a3d0af 100644 --- a/smsapp/smshelper.h +++ b/smsapp/smshelper.h @@ -120,6 +120,11 @@ public: */ Q_INVOKABLE static quint64 totalMessageSize(const QList& urls, const QString& text); + /** + * Gets a thumbnail for the given attachment + */ + Q_INVOKABLE static QIcon getThumbnailForAttachment(const Attachment& attachment); + private: static bool isInGsmAlphabet(const QChar& ch); static bool isInGsmAlphabetExtension(const QChar& ch);