From 706fc314fbc8b4327b1c5bb84c1a5b834616c32f Mon Sep 17 00:00:00 2001 From: Aniket Kumar Date: Mon, 31 Aug 2020 15:35:25 +0530 Subject: [PATCH] Adding support to send attachments to the remote device. --- cli/kdeconnect-cli.cpp | 5 +- interfaces/conversationmessage.cpp | 9 + interfaces/conversationmessage.h | 2 +- plugins/sms/conversationsdbusinterface.cpp | 8 +- plugins/sms/conversationsdbusinterface.h | 4 +- plugins/sms/smsplugin.cpp | 59 +++++- plugins/sms/smsplugin.h | 28 ++- smsapp/conversationmodel.cpp | 21 ++- smsapp/conversationmodel.h | 4 +- smsapp/qml/ConversationDisplay.qml | 100 +--------- smsapp/qml/SendingArea.qml | 208 +++++++++++++++++++++ smsapp/resources.qrc | 1 + smsapp/smshelper.cpp | 11 ++ smsapp/smshelper.h | 5 + 14 files changed, 343 insertions(+), 122 deletions(-) create mode 100644 smsapp/qml/SendingArea.qml diff --git a/cli/kdeconnect-cli.cpp b/cli/kdeconnect-cli.cpp index 96c4f9584..cfdb145cf 100644 --- a/cli/kdeconnect-cli.cpp +++ b/cli/kdeconnect-cli.cpp @@ -53,6 +53,7 @@ int main(int argc, char** argv) parser.addOption(QCommandLineOption(QStringLiteral("lock"), i18n("Lock the specified device"))); parser.addOption(QCommandLineOption(QStringLiteral("send-sms"), i18n("Sends an SMS. Requires destination"), i18n("message"))); parser.addOption(QCommandLineOption(QStringLiteral("destination"), i18n("Phone number to send the message"), i18n("phone number"))); + parser.addOption(QCommandLineOption(QStringLiteral("attachment"), i18n("File urls to send attachments with the message"), i18n("file urls"))); parser.addOption(QCommandLineOption(QStringList(QStringLiteral("device")) << QStringLiteral("d"), i18n("Device ID"), QStringLiteral("dev"))); parser.addOption(QCommandLineOption(QStringList(QStringLiteral("name")) << QStringLiteral("n"), i18n("Device Name"), QStringLiteral("name"))); parser.addOption(QCommandLineOption(QStringLiteral("encryption-info"), i18n("Get encryption info about said device"))); @@ -258,9 +259,11 @@ int main(int argc, char** argv) addresses << QVariant::fromValue(address); } + const QStringList urlList = parser.value(QStringLiteral("attachment")).split(QRegularExpression(QStringLiteral("\\s+"))); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/sms"), QStringLiteral("org.kde.kdeconnect.device.sms"), QStringLiteral("sendSms")); const QString text = parser.value(QStringLiteral("send-sms")); - msg.setArguments(QVariantList() << QVariant::fromValue(addresses) << text); + msg.setArguments(QVariantList() << QVariant::fromValue(addresses) << text << QVariant(urlList)); blockOnReply(DBusHelper::sessionBus().asyncCall(msg)); } else { QTextStream(stderr) << i18n("error: should specify the SMS's recipient by passing --destination "); diff --git a/interfaces/conversationmessage.cpp b/interfaces/conversationmessage.cpp index f0362f198..998dd6a7b 100644 --- a/interfaces/conversationmessage.cpp +++ b/interfaces/conversationmessage.cpp @@ -72,6 +72,15 @@ ConversationAddress::ConversationAddress(QString address) : m_address(address) {} +bool ConversationMessage::isOutgoing() const +{ + return type() == MessageTypeSent + || type() == MessageTypeOutbox + || type() == MessageTypeDraft + || type() == MessageTypeFailed + || type() == MessageTypeQueued; +} + Attachment::Attachment(qint64 partID, QString mimeType, QString base64EncodedFile, QString uniqueIdentifier) : m_partID(partID) , m_mimeType(mimeType) diff --git a/interfaces/conversationmessage.h b/interfaces/conversationmessage.h index 2c66a1b28..05a19b5ae 100644 --- a/interfaces/conversationmessage.h +++ b/interfaces/conversationmessage.h @@ -69,7 +69,7 @@ public: bool isMultitarget() const { return (eventField() & ConversationMessage::EventMultiTarget); } bool isIncoming() const { return type() == MessageTypeInbox; } - bool isOutgoing() const { return type() == MessageTypeSent; } + bool isOutgoing() const; bool containsAttachment() const { return !attachments().isEmpty(); } /** diff --git a/plugins/sms/conversationsdbusinterface.cpp b/plugins/sms/conversationsdbusinterface.cpp index cd43e6511..78e136e85 100644 --- a/plugins/sms/conversationsdbusinterface.cpp +++ b/plugins/sms/conversationsdbusinterface.cpp @@ -177,7 +177,7 @@ void ConversationsDbusInterface::updateConversation(const qint64& conversationID waitingForMessagesLock.unlock(); } -void ConversationsDbusInterface::replyToConversation(const qint64& conversationID, const QString& message) +void ConversationsDbusInterface::replyToConversation(const qint64& conversationID, const QString& message, const QVariantList& attachmentUrls) { const auto messagesList = m_conversations[conversationID]; if (messagesList.isEmpty()) { @@ -192,11 +192,11 @@ void ConversationsDbusInterface::replyToConversation(const qint64& conversationI addresses << QVariant::fromValue(address); } - m_smsInterface.sendSms(addresses, message, messagesList.first().subID()); + m_smsInterface.sendSms(addresses, message, attachmentUrls, messagesList.first().subID()); } -void ConversationsDbusInterface::sendWithoutConversation(const QVariantList& addresses, const QString& message) { - m_smsInterface.sendSms(addresses, message); +void ConversationsDbusInterface::sendWithoutConversation(const QVariantList& addresses, const QString& message, const QVariantList& attachmentUrls) { + m_smsInterface.sendSms(addresses, message, attachmentUrls); } void ConversationsDbusInterface::requestAllConversationThreads() diff --git a/plugins/sms/conversationsdbusinterface.h b/plugins/sms/conversationsdbusinterface.h index 9a5be8e9e..732744b08 100644 --- a/plugins/sms/conversationsdbusinterface.h +++ b/plugins/sms/conversationsdbusinterface.h @@ -77,12 +77,12 @@ public Q_SLOTS: /** * Send a new message to this conversation */ - void replyToConversation(const qint64& conversationID, const QString& message); + void replyToConversation(const qint64& conversationID, const QString& message, const QVariantList& attachmentUrls); /** * Send a new message to the contact having no previous coversation with */ - void sendWithoutConversation(const QVariantList& addressList, const QString& message); + void sendWithoutConversation(const QVariantList& addressList, const QString& message, const QVariantList& attachmentUrls); /** * Send the request to the Telephony plugin to update the list of conversation threads diff --git a/plugins/sms/smsplugin.cpp b/plugins/sms/smsplugin.cpp index 9bf842180..50faaf4a2 100644 --- a/plugins/sms/smsplugin.cpp +++ b/plugins/sms/smsplugin.cpp @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -27,6 +31,7 @@ SmsPlugin::SmsPlugin(QObject* parent, const QVariantList& args) , m_telepathyInterface(QStringLiteral("org.freedesktop.Telepathy.ConnectionManager.kdeconnect"), QStringLiteral("/kdeconnect")) , m_conversationInterface(new ConversationsDbusInterface(this)) { + m_codec = QTextCodec::codecForName(CODEC_NAME); } SmsPlugin::~SmsPlugin() @@ -47,7 +52,7 @@ bool SmsPlugin::receivePacket(const NetworkPacket& np) return true; } -void SmsPlugin::sendSms(const QVariantList& addresses, const QString& messageBody, const qint64 subID) +void SmsPlugin::sendSms(const QVariantList& addresses, const QString& textMessage, const QVariantList& attachmentUrls, const qint64 subID) { QVariantList addressMapList; for (const QVariant& address : addresses) { @@ -56,13 +61,35 @@ void SmsPlugin::sendSms(const QVariantList& addresses, const QString& messageBod } QVariantMap packetMap({ - {QStringLiteral("sendSms"), true}, - {QStringLiteral("addresses"), addressMapList}, - {QStringLiteral("messageBody"), messageBody} + {QStringLiteral("version"), 2}, + {QStringLiteral("addresses"), addressMapList} }); + + // If there is any text message add it to the network packet + if (textMessage != QStringLiteral("")) { + packetMap[QStringLiteral("textMessage")] = textMessage; + } + if (subID != -1) { packetMap[QStringLiteral("subID")] = subID; } + + QVariantList attachmentMapList; + for (const QVariant& attachmentUrl : attachmentUrls) { + const Attachment attachment = createAttachmentFromUrl(attachmentUrl.toString()); + QVariantMap attachmentMap({ + {QStringLiteral("fileName"), attachment.uniqueIdentifier()}, + {QStringLiteral("base64EncodedFile"), attachment.base64EncodedFile()}, + {QStringLiteral("mimeType"), attachment.mimeType()} + }); + attachmentMapList.append(attachmentMap); + } + + // If there is any attachment add it to the network packet + if (!attachmentMapList.isEmpty()) { + packetMap[QStringLiteral("attachments")] = attachmentMapList; + } + NetworkPacket np(PACKET_TYPE_SMS_REQUEST, packetMap); qCDebug(KDECONNECT_PLUGIN_SMS) << "Dispatching SMS send request to remote"; sendPacket(np); @@ -185,6 +212,30 @@ void SmsPlugin::getAttachment(const qint64& partID, const QString& uniqueIdentif } } +Attachment SmsPlugin::createAttachmentFromUrl(const QString& url) +{ + QFile file(url); + file.open(QIODevice::ReadOnly); + + if (!file.exists()) { + return Attachment(); + } + + QFileInfo fileInfo(file); + QString fileName(fileInfo.fileName()); + + QByteArray byteArray = file.readAll().toBase64(); + file.close(); + + QString base64EncodedFile = m_codec->toUnicode(byteArray); + + QMimeDatabase mimeDatabase; + QString mimeType = mimeDatabase.mimeTypeForFile(url).name(); + + Attachment attachment(-1, mimeType, base64EncodedFile, fileName); + return attachment; +} + QString SmsPlugin::dbusPath() const { diff --git a/plugins/sms/smsplugin.h b/plugins/sms/smsplugin.h index 531b559b6..876df8bcd 100644 --- a/plugins/sms/smsplugin.h +++ b/plugins/sms/smsplugin.h @@ -75,15 +75,21 @@ /** * Packet sent to request a message be sent * - * This will almost certainly need to be replaced or augmented to support MMS, - * but be sure the Android side remains compatible with old desktop apps! - * * The body should look like so: - * { "sendSms": true, + * { "version": 2, * "addresses": - * "messageBody": "Hi mom!", + * "textMessage": "Hi mom!", + * "attachments": * "sub_id": "3859358340534" * } + * + * An AttachmentContainer object looks like: + * { + * "fileName": // Name of the file + * "base64EncodedFile": // Base64 encoded file + * "mimeType": // File type (eg: image/jpg, video/mp4 etc.) + * } + * */ #define PACKET_TYPE_SMS_REQUEST QStringLiteral("kdeconnect.sms.request") @@ -123,6 +129,10 @@ Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SMS) +#define CODEC_NAME "CP1251" + +class QTextCodec; + class Q_DECL_EXPORT SmsPlugin : public KdeConnectPlugin { @@ -139,7 +149,7 @@ public: QString dbusPath() const override; public Q_SLOTS: - Q_SCRIPTABLE void sendSms(const QVariantList& addresses, const QString& messageBody, const qint64 subID = -1); + Q_SCRIPTABLE void sendSms(const QVariantList& addresses, const QString& textMessage, const QVariantList& attachmentUrls, const qint64 subID = -1); /** * Send a request to the remote for all of its conversations @@ -183,8 +193,14 @@ private: */ bool handleSmsAttachmentFile(const NetworkPacket& np); + /** + * Encode a local file so it can be sent to the remote device as part of an MMS message. + */ + Attachment createAttachmentFromUrl(const QString& url); + QDBusInterface m_telepathyInterface; ConversationsDbusInterface* m_conversationInterface; + QTextCodec *m_codec; }; #endif diff --git a/smsapp/conversationmodel.cpp b/smsapp/conversationmodel.cpp index 7bf74ceae..7cd80abc7 100644 --- a/smsapp/conversationmodel.cpp +++ b/smsapp/conversationmodel.cpp @@ -86,13 +86,18 @@ void ConversationModel::setAddressList(const QList& address m_addressList = addressList; } -void ConversationModel::sendReplyToConversation(const QString& message) +bool ConversationModel::sendReplyToConversation(const QString& textMessage, QList attachmentUrls) { - //qCDebug(KDECONNECT_SMS_CONVERSATION_MODEL) << "Trying to send" << message << "to conversation with ID" << m_threadId; - m_conversationsInterface->replyToConversation(m_threadId, message); + QVariantList fileUrls; + for (const auto& url : attachmentUrls) { + fileUrls << QVariant::fromValue(url.toLocalFile()); + } + + m_conversationsInterface->replyToConversation(m_threadId, textMessage, fileUrls); + return true; } -void ConversationModel::startNewConversation(const QString& message, const QList& addressList) +bool ConversationModel::startNewConversation(const QString& textMessage, const QList& addressList, QList attachmentUrls) { QVariantList addresses; @@ -100,7 +105,13 @@ void ConversationModel::startNewConversation(const QString& message, const QList addresses << QVariant::fromValue(address); } - m_conversationsInterface->sendWithoutConversation(addresses, message); + QVariantList fileUrls; + for (const auto& url : attachmentUrls) { + fileUrls << QVariant::fromValue(url.toLocalFile()); + } + + m_conversationsInterface->sendWithoutConversation(addresses, textMessage, fileUrls); + return true; } void ConversationModel::requestMoreMessages(const quint32& howMany) diff --git a/smsapp/conversationmodel.h b/smsapp/conversationmodel.h index 07469fe1c..853a36450 100644 --- a/smsapp/conversationmodel.h +++ b/smsapp/conversationmodel.h @@ -48,8 +48,8 @@ public: QList addressList() const { return m_addressList; } void setAddressList(const QList& addressList); - Q_INVOKABLE void sendReplyToConversation(const QString& message); - Q_INVOKABLE void startNewConversation(const QString& message, const QList& addressList); + Q_INVOKABLE bool sendReplyToConversation(const QString& textMessage, QList attachmentUrls); + Q_INVOKABLE bool startNewConversation(const QString& textMessage, const QList& addressList, QList attachmentUrls); Q_INVOKABLE void requestMoreMessages(const quint32& howMany = 10); Q_INVOKABLE QString getCharCountInfo(const QString& message) const; diff --git a/smsapp/qml/ConversationDisplay.qml b/smsapp/qml/ConversationDisplay.qml index 8f80d2480..e51148b72 100644 --- a/smsapp/qml/ConversationDisplay.qml +++ b/smsapp/qml/ConversationDisplay.qml @@ -168,102 +168,8 @@ Kirigami.ScrollablePage } } - footer: Controls.Pane { - id: sendingArea - enabled: page.deviceConnected - layer.enabled: sendingArea.enabled - layer.effect: DropShadow { - verticalOffset: 1 - color: Kirigami.Theme.disabledTextColor - samples: 20 - spread: 0.3 - } - Layout.fillWidth: true - padding: 0 - wheelEnabled: true - background: Rectangle { - color: Kirigami.Theme.viewBackgroundColor - } - - RowLayout { - anchors.fill: parent - - Controls.ScrollView { - Layout.fillWidth: true - Layout.maximumHeight: page.height > 300 ? page.height / 3 : 2 * page.height / 3 - contentWidth: page.width - sendButtonArea.width - clip: true - Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff - - Controls.TextArea { - anchors.fill: parent - id: messageField - placeholderText: i18nd("kdeconnect-sms", "Compose message") - wrapMode: TextEdit.Wrap - topPadding: Kirigami.Units.gridUnit * 0.5 - bottomPadding: topPadding - selectByMouse: true - topInset: height * 2 // This removes background (frame) of the TextArea. Setting `background: Item {}` would cause segfault. - Keys.onReturnPressed: { - if (event.key === Qt.Key_Return) { - if (event.modifiers & Qt.ShiftModifier) { - messageField.append("") - } else { - sendButton.onClicked() - event.accepted = true - } - } - } - } - } - - ColumnLayout { - id: sendButtonArea - - Controls.ToolButton { - id: sendButton - Layout.preferredWidth: Kirigami.Units.gridUnit * 2 - Layout.preferredHeight: Kirigami.Units.gridUnit * 2 - padding: 0 - Kirigami.Icon { - source: "document-send" - enabled: sendButton.enabled - isMask: true - smooth: true - anchors.centerIn: parent - width: Kirigami.Units.gridUnit * 1.5 - height: width - } - onClicked: { - // don't send empty messages - if (!messageField.text.length) { - return - } - - // disable the button to prevent sending - // the same message several times - sendButton.enabled = false - - // send the message - if (page.conversationId == page.invalidId) { - conversationModel.startNewConversation(messageField.text, addresses) - } else { - conversationModel.sendReplyToConversation(messageField.text) - } - messageField.text = "" - - // re-enable the button - sendButton.enabled = true - } - } - - Controls.Label { - id: "charCount" - text: conversationModel.getCharCountInfo(messageField.text) - visible: text.length > 0 - Layout.minimumWidth: Math.max(Layout.minimumWidth, width) // Make this label only grow, never shrink - } - } - } + footer: SendingArea { + width: parent.width + addresses: page.addresses } } diff --git a/smsapp/qml/SendingArea.qml b/smsapp/qml/SendingArea.qml new file mode 100644 index 000000000..c5a8fab5b --- /dev/null +++ b/smsapp/qml/SendingArea.qml @@ -0,0 +1,208 @@ +/** + * 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.1 +import QtQuick.Controls 2.2 as Controls +import QtQuick.Layouts 1.1 +import org.kde.kirigami 2.4 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Dialogs 1.1 +import org.kde.kdeconnect.sms 1.0 + +ColumnLayout { + id: root + property var addresses + property var selectedFileUrls: [] + readonly property int maxMessageSize: 600000 + + MessageDialog { + id: messageDialog + title: i18nd("kdeconnect-sms", "Failed to send") + text: i18nd("kdeconnect-sms", "Max message size limit exceeded.") + + onAccepted: { + messageDialog.close() + } + } + + FileDialog { + id: fileDialog + folder: shortcuts.home + selectMultiple: true + + onAccepted: { + root.selectedFileUrls = fileDialog.fileUrls + fileDialog.close() + } + } + + Controls.Pane { + id: sendingArea + enabled: page.deviceConnected + layer.enabled: sendingArea.enabled + layer.effect: DropShadow { + verticalOffset: 1 + color: Kirigami.Theme.disabledTextColor + samples: 20 + spread: 0.3 + } + Layout.fillWidth: true + padding: 0 + wheelEnabled: true + background: Rectangle { + color: Kirigami.Theme.viewBackgroundColor + } + + RowLayout { + anchors.fill: parent + + Controls.ScrollView { + Layout.fillWidth: true + Layout.maximumHeight: page.height > 300 ? page.height / 3 : 2 * page.height / 3 + contentWidth: page.width - sendButtonArea.width + clip: true + Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff + + Controls.TextArea { + anchors.fill: parent + id: messageField + placeholderText: i18nd("kdeconnect-sms", "Compose message") + wrapMode: TextEdit.Wrap + topPadding: Kirigami.Units.gridUnit * 0.5 + bottomPadding: topPadding + selectByMouse: true + topInset: height * 2 // This removes background (frame) of the TextArea. Setting `background: Item {}` would cause segfault. + Keys.onReturnPressed: { + if (event.key === Qt.Key_Return) { + if (event.modifiers & Qt.ShiftModifier) { + messageField.append("") + } else { + sendButton.onClicked() + event.accepted = true + } + } + } + } + } + + ColumnLayout { + id: sendButtonArea + + RowLayout { + Controls.ToolButton { + id: sendButton + enabled: messageField.text.length || selectedFileUrls.length + Layout.preferredWidth: Kirigami.Units.gridUnit * 2 + Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + padding: 0 + Kirigami.Icon { + source: "document-send" + enabled: sendButton.enabled + isMask: true + smooth: true + anchors.centerIn: parent + width: Kirigami.Units.gridUnit * 1.5 + height: width + } + + property bool messageSent: false + + onClicked: { + // disable the button to prevent sending + // the same message several times + sendButton.enabled = false + + if (SmsHelper.totalMessageSize(selectedFileUrls, messageField.text) > maxMessageSize) { + messageDialog.visible = true + } else if (page.conversationId === page.invalidId) { + messageSent = conversationModel.startNewConversation(messageField.text, addresses, selectedFileUrls) + } else { + messageSent = conversationModel.sendReplyToConversation(messageField.text, selectedFileUrls) + } + + if (messageSent) { + messageField.text = "" + selectedFileUrls = [] + sendButton.enabled = false + } + } + } + + Controls.ToolButton { + id: attachFilesButton + enabled: true + Layout.preferredWidth: Kirigami.Units.gridUnit * 2 + Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + padding: 0 + Text { + id: attachedFilesCount + text: selectedFileUrls.length + color: "red" + visible: selectedFileUrls.length > 0 + } + Kirigami.Icon { + source: "insert-image" + isMask: true + smooth: true + anchors.centerIn: parent + width: Kirigami.Units.gridUnit * 1.5 + height: width + } + + onClicked: { + fileDialog.open() + } + } + + Controls.ToolButton { + id: clearAttachmentButton + visible: selectedFileUrls.length > 0 + Layout.preferredWidth: Kirigami.Units.gridUnit * 2 + Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + padding: 0 + Kirigami.Icon { + id: cancelIcon + source: "edit-clear" + isMask: true + smooth: true + anchors.centerIn: parent + width: Kirigami.Units.gridUnit * 1.5 + height: width + } + + onClicked: { + selectedFileUrls = [] + if (messageField.text == "") { + sendButton.enabled = false + } + } + } + } + + Controls.Label { + id: "charCount" + text: conversationModel.getCharCountInfo(messageField.text) + visible: text.length > 0 + Layout.minimumWidth: Math.max(Layout.minimumWidth, width) // Make this label only grow, never shrink + } + } + } + } +} diff --git a/smsapp/resources.qrc b/smsapp/resources.qrc index 751b80abd..3d2693c6e 100644 --- a/smsapp/resources.qrc +++ b/smsapp/resources.qrc @@ -6,5 +6,6 @@ qml/ChatMessage.qml qml/MessageAttachments.qml qml/AttachmentViewer.qml + qml/SendingArea.qml diff --git a/smsapp/smshelper.cpp b/smsapp/smshelper.cpp index e0a3a0bde..2be06c54a 100644 --- a/smsapp/smshelper.cpp +++ b/smsapp/smshelper.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -442,3 +443,13 @@ bool SmsHelper::isInGsmAlphabetExtension(const QChar& ch) } return false; } + +quint64 SmsHelper::totalMessageSize(const QList& urls, const QString& text) { + quint64 totalSize = text.size(); + for (QUrl url : urls) { + QFileInfo fileInfo(url.toLocalFile()); + totalSize += fileInfo.size(); + } + + return totalSize; +} diff --git a/smsapp/smshelper.h b/smsapp/smshelper.h index 196ac20cf..67c08cf9c 100644 --- a/smsapp/smshelper.h +++ b/smsapp/smshelper.h @@ -115,6 +115,11 @@ public: */ Q_INVOKABLE static bool isAddressValid(const QString& address); + /** + * Return the total size of the message + */ + Q_INVOKABLE static quint64 totalMessageSize(const QList& urls, const QString& text); + private: static bool isInGsmAlphabet(const QChar& ch); static bool isInGsmAlphabetExtension(const QChar& ch);