[Merge] lp:telephony-service/staging into lp:telephony-service

Tiago Salem Herrmann tiago.herrmann at canonical.com
Wed Nov 23 13:53:01 UTC 2016


Review: Needs Fixing

just a few remarks.

Diff comments:

> 
> === added file 'handler/messagesendingjob.cpp'
> --- handler/messagesendingjob.cpp	1970-01-01 00:00:00 +0000
> +++ handler/messagesendingjob.cpp	2016-11-11 14:02:39 +0000
> @@ -0,0 +1,386 @@
> +/*
> + * Copyright (C) 2016 Canonical, Ltd.
> + *
> + * Authors:
> + *  Tiago Salem Herrmann <tiago.herrmann at canonical.com>
> + *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> + *
> + * This file is part of telephony-service.
> + *
> + * telephony-service 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; version 3.
> + *
> + * telephony-service 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 <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "accountentry.h"
> +#include "chatstartingjob.h"
> +#include "messagesendingjob.h"
> +#include "messagesendingjobadaptor.h"
> +#include "telepathyhelper.h"
> +#include "texthandler.h"
> +#include <TelepathyQt/ContactManager>
> +#include <TelepathyQt/PendingContacts>
> +#include <QImage>
> +
> +#define SMIL_TEXT_REGION "<region id=\"Text\" width=\"100%\" height=\"100%\" fit=\"scroll\" />"
> +#define SMIL_IMAGE_REGION "<region id=\"Image\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
> +#define SMIL_VIDEO_REGION "<region id=\"Video\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
> +#define SMIL_AUDIO_REGION "<region id=\"Audio\" width=\"100%\" height=\"100%\" fit=\"meet\" />"
> +#define SMIL_TEXT_PART "<par dur=\"3s\">\
> +       <text src=\"cid:%1\" region=\"Text\" />\
> +     </par>"
> +#define SMIL_IMAGE_PART "<par dur=\"5000ms\">\
> +       <img src=\"cid:%1\" region=\"Image\" />\
> +     </par>"
> +#define SMIL_VIDEO_PART "<par>\
> +       <video src=\"cid:%1\" region=\"Video\" />\
> +     </par>"
> +#define SMIL_AUDIO_PART "<par>\
> +       <audio src=\"cid:%1\" region=\"Audio\" />\
> +     </par>"
> +
> +#define SMIL_FILE "<smil>\
> +   <head>\
> +     <layout>\
> +         %1\
> +     </layout>\
> +   </head>\
> +   <body>\
> +       %2\
> +   </body>\
> + </smil>"
> +
> +MessageSendingJob::MessageSendingJob(TextHandler *textHandler, PendingMessage message)
> +: MessageJob(textHandler), mTextHandler(textHandler), mMessage(message), mFinished(false)
> +{
> +    setAdaptorAndRegister(new MessageSendingJobAdaptor(this));
> +}
> +
> +MessageSendingJob::~MessageSendingJob()
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +}
> +
> +QString MessageSendingJob::accountId() const
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    return mAccountId;
> +}
> +
> +QString MessageSendingJob::messageId() const
> +{
> +    return mMessageId;
> +}
> +
> +QString MessageSendingJob::channelObjectPath() const
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    return mChannelObjectPath;
> +}
> +
> +QVariantMap MessageSendingJob::properties() const
> +{
> +    return mMessage.properties;
> +}
> +
> +void MessageSendingJob::startJob()
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    qDebug() << "Getting account for id:" << mMessage.accountId;
> +    AccountEntry *account = TelepathyHelper::instance()->accountForId(mMessage.accountId);
> +    if (!account) {
> +        setStatus(Failed);
> +        scheduleDeletion();
> +        return;
> +    }
> +
> +    setStatus(Running);
> +
> +    // check if the message should be sent via an overloaded account
> +    // if the target type is a room, do not overload.
> +    if (mMessage.properties["chatType"].toUInt() != Tp::HandleTypeRoom) {
> +        QList<AccountEntry*> overloadAccounts = TelepathyHelper::instance()->checkAccountOverload(account);
> +        for (auto newAccount : overloadAccounts) {
> +            // FIXME: check if we need to validate anything other than being connected
> +            if (newAccount->connected()) {
> +                account = newAccount;
> +                break;
> +            }
> +        }
> +    }
> +
> +    // save the account
> +    mAccount = account;
> +    setAccountId(mAccount->accountId());
> +
> +    if (!account->connected()) {
> +        connect(account, &AccountEntry::connectedChanged, [this, account]() {
> +            if (account->connected()) {
> +                findOrCreateChannel();
> +            }
> +        });
> +        return;
> +    }
> +
> +    findOrCreateChannel();
> +}
> +
> +void MessageSendingJob::findOrCreateChannel()
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    // now that we know what account to use, find existing channels or request a new one
> +    QList<Tp::TextChannelPtr> channels = mTextHandler->existingChannels(mAccount->accountId(), mMessage.properties);
> +    if (channels.isEmpty()) {
> +        ChatStartingJob *job = new ChatStartingJob(mTextHandler, mAccount->accountId(), mMessage.properties);
> +        connect(job, &MessageJob::finished, [this, job]() {
> +            if (job->status() == MessageJob::Failed) {
> +                setStatus(Failed);
> +                scheduleDeletion();
> +                return;
> +            }
> +
> +            mTextChannel = job->textChannel();
> +            sendMessage();
> +        });
> +        job->startJob();
> +        return;
> +    }
> +
> +    mTextChannel = channels.last();
> +    sendMessage();
> +}
> +
> +void MessageSendingJob::sendMessage()
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +
> +    Tp::MessagePartList messageParts = buildMessage(mMessage);
> +    Tp::PendingSendMessage *op = NULL;
> +    // some protocols can't sent multipart messages, so we check here
> +    // and split the parts if needed
> +    if (canSendMultiPartMessages()) {
> +        op = mTextChannel->send(messageParts);
> +    } else {
> +        bool messageSent = false;
> +        Tp::MessagePart header = messageParts.takeFirst();
> +        Q_FOREACH(const Tp::MessagePart &part, messageParts) {
> +            Tp::MessagePartList newMessage;
> +            newMessage << header;
> +            newMessage << part;
> +            Tp::PendingSendMessage *thisOp = mTextChannel->send(newMessage);
> +            if (messageSent) {
> +                continue;
> +            }
> +            messageSent = true;
> +            op = thisOp;
> +        }
> +    }
> +    connect(op, &Tp::PendingOperation::finished, [this, op]() {
> +        if (op->isError()) {
> +            setStatus(Failed);
> +            scheduleDeletion();
> +            return;
> +        }
> +
> +        setChannelObjectPath(mTextChannel->objectPath());
> +        setMessageId(op->sentMessageToken());
> +        setStatus(Finished);
> +        scheduleDeletion();
> +    });
> +}
> + 
> +bool MessageSendingJob::canSendMultiPartMessages()
> +{
> +    if (!mAccount) {
> +        return false;
> +    }
> +    switch (mAccount->type()) {
> +    case AccountEntry::PhoneAccount:
> +        return true;
> +    // TODO check in telepathy if multipart is supported
> +    // currently we just return false to be on the safe side
> +    case AccountEntry::GenericAccount:
> +    default:
> +        return false;
> +    }
> +    return false;
> +}
> +
> +void MessageSendingJob::setAccountId(const QString &accountId)
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    mAccountId = accountId;
> +    Q_EMIT accountIdChanged();
> +}
> +
> +void MessageSendingJob::setChannelObjectPath(const QString &objectPath)
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    mChannelObjectPath = objectPath;
> +    Q_EMIT channelObjectPathChanged();
> +}
> +
> +void MessageSendingJob::setMessageId(const QString &id)
> +{
> +    mMessageId = id;
> +    Q_EMIT messageIdChanged();
> +}
> +
> +Tp::MessagePartList MessageSendingJob::buildMessage(const PendingMessage &pendingMessage)
> +{
> +    qDebug() << __PRETTY_FUNCTION__;
> +    Tp::MessagePartList message;
> +    Tp::MessagePart header;
> +    QString smil, regions, parts;
> +    bool hasImage = false, hasText = false, hasVideo = false, hasAudio = false, isMMS = false;
> +    int chatType = pendingMessage.properties["chatType"].toUInt();
> +
> +    if (!mAccount) {
> +        // account does not exist
> +        return Tp::MessagePartList();
> +    }
> +
> +    bool temporaryFiles = (pendingMessage.properties.contains("x-canonical-tmp-files") &&
> +                           pendingMessage.properties["x-canonical-tmp-files"].toBool());
> +
> +    // add the remaining properties to the message header
> +    QVariantMap::const_iterator it = pendingMessage.properties.begin();
> +    for (; it != pendingMessage.properties.end(); ++it) {
> +        header[it.key()] = QDBusVariant(it.value());
> +    }
> +
> +    // check if this message should be sent as an MMS
> +    if (mAccount->type() == AccountEntry::PhoneAccount) {
> +        isMMS = (pendingMessage.attachments.size() > 0 ||
> +                 (pendingMessage.properties["chatType"].toUInt() == 2));

use Tp::HandleTypeRoom instead of 2.

> +    }
> +
> +    // this flag should not be in the message header, it's only useful for the handler
> +    header.remove("x-canonical-tmp-files");
> +    header.remove("chatType");
> +    header.remove("threadId");
> +    header.remove("participantIds");
> +
> +    header["message-type"] = QDBusVariant(0);
> +    message << header;
> +
> +    // convert AttachmentList struct into telepathy Message parts
> +    Q_FOREACH(const AttachmentStruct &attachment, pendingMessage.attachments) {
> +        QByteArray fileData;
> +        QString newFilePath = QString(attachment.filePath).replace("file://", "");
> +        QFile attachmentFile(newFilePath);
> +        if (!attachmentFile.open(QIODevice::ReadOnly)) {
> +            qWarning() << "fail to load attachment" << attachmentFile.errorString() << attachment.filePath;
> +            continue;
> +        }
> +        if (attachment.contentType.startsWith("image/")) {
> +            if (isMMS) {
> +                hasImage = true;
> +                parts += QString(SMIL_IMAGE_PART).arg(attachment.id);
> +                // check if we need to reduce de image size in case it's bigger than 300k
> +                // this check is only valid for MMS
> +                if (attachmentFile.size() > 307200) {
> +                    QImage scaledImage(newFilePath);
> +                    if (!scaledImage.isNull()) {
> +                        QBuffer buffer(&fileData);
> +                        buffer.open(QIODevice::WriteOnly);
> +                        scaledImage.scaled(640, 640, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(&buffer, "jpg");
> +                    }
> +                } else {
> +                    fileData = attachmentFile.readAll();
> +                }
> +            }
> +        } else if (attachment.contentType.startsWith("video/")) {
> +            if (isMMS) {
> +                hasVideo = true;
> +                parts += QString(SMIL_VIDEO_PART).arg(attachment.id);
> +            }
> +        } else if (attachment.contentType.startsWith("audio/")) {
> +            if (isMMS) {
> +                hasAudio = true;
> +                parts += QString(SMIL_AUDIO_PART).arg(attachment.id);
> +            }
> +        } else if (attachment.contentType.startsWith("text/plain")) {
> +            if (isMMS) {
> +                hasText = true;
> +                parts += QString(SMIL_TEXT_PART).arg(attachment.id);
> +            }
> +        } else if (attachment.contentType.startsWith("text/vcard") ||
> +                   attachment.contentType.startsWith("text/x-vcard")) {
> +        } else if (isMMS) {
> +            // for MMS we just support the contentTypes above
> +            if (temporaryFiles) {
> +                attachmentFile.remove();
> +            }
> +            continue;
> +        }
> +
> +        if (fileData.isEmpty()) {
> +            fileData = attachmentFile.readAll();
> +        }
> +
> +        if (temporaryFiles) {
> +            attachmentFile.remove();
> +        }
> +
> +        if (hasVideo) {
> +            regions += QString(SMIL_VIDEO_REGION);
> +        }
> +
> +        if (hasAudio) {
> +            regions += QString(SMIL_AUDIO_REGION);
> +        }
> +
> +        if (hasText) {
> +            regions += QString(SMIL_TEXT_REGION);
> +        }
> +        if (hasImage) {
> +            regions += QString(SMIL_IMAGE_REGION);
> +        }
> +
> +        Tp::MessagePart part;
> +        part["content-type"] =  QDBusVariant(attachment.contentType);
> +        part["identifier"] = QDBusVariant(attachment.id);
> +        part["content"] = QDBusVariant(fileData);
> +        part["size"] = QDBusVariant(fileData.size());
> +
> +        message << part;
> +    }
> +
> +    if (!pendingMessage.message.isEmpty()) {
> +        Tp::MessagePart part;
> +        QString tmpTextId("text_0.txt");
> +        part["content-type"] =  QDBusVariant(QString("text/plain"));
> +        part["identifier"] = QDBusVariant(tmpTextId);
> +        part["content"] = QDBusVariant(pendingMessage.message);
> +        part["size"] = QDBusVariant(pendingMessage.message.size());
> +        if (isMMS) {
> +            parts += QString(SMIL_TEXT_PART).arg(tmpTextId);
> +            regions += QString(SMIL_TEXT_REGION);
> +        }
> +        message << part;
> +    }
> +
> +    if (isMMS) {
> +        Tp::MessagePart smilPart;
> +        smil = QString(SMIL_FILE).arg(regions).arg(parts);
> +        smilPart["content-type"] =  QDBusVariant(QString("application/smil"));
> +        smilPart["identifier"] = QDBusVariant(QString("smil.xml"));
> +        smilPart["content"] = QDBusVariant(smil);
> +        smilPart["size"] = QDBusVariant(smil.size());
> +
> +        message << smilPart;
> +    }
> +
> +    return message;
> +}
> +
> +
> 
> === modified file 'handler/texthandler.cpp'
> --- handler/texthandler.cpp	2016-03-01 18:17:57 +0000
> +++ handler/texthandler.cpp	2016-11-11 14:02:39 +0000
> @@ -429,47 +158,17 @@
>  
>      QString accountId = account->accountId();
>      mChannels.append(channel);
> -
> -    // check for pending messages for this channel
> -    if (mPendingMessages.isEmpty()) {
> -        return;
> -    }
> -
> -    QList<PendingMessage>::iterator it = mPendingMessages.begin();
> -    while (it != mPendingMessages.end()) {
> -        bool found = false;
> -        Q_FOREACH(const Tp::TextChannelPtr &existingChannel, existingChannels(it->recipients, it->accountId)) {
> -            if (existingChannel == channel) {
> -                // FIXME: we can't trust recipients for group chats in regular IM accounts
> -                sendMessage(it->accountId, it->recipients, it->message, it->attachments, it->properties);
> -                it = mPendingMessages.erase(it);
> -                found = true;
> -                break;
> -            }
> -        }
> -        if (!found) {
> -            ++it;
> -        }
> -    }
> -}
> -
> -void TextHandler::onMessageSent(Tp::PendingOperation *op)
> -{
> -    Tp::PendingSendMessage *psm = qobject_cast<Tp::PendingSendMessage*>(op);
> -    if(!psm) {
> -        qWarning() << "The pending object was not a pending operation:" << op;
> -        return;
> -    }
> -
> -    if (psm->isError()) {
> -        qWarning() << "Error sending message:" << psm->errorName() << psm->errorMessage();
> -        return;
> -    }
> -}
> -
> -QList<Tp::TextChannelPtr> TextHandler::existingChannels(const QStringList &targetIds, const QString &accountId)
> +}
> +
> +QList<Tp::TextChannelPtr> TextHandler::existingChannels(const QString &accountId, const QVariantMap &properties)
>  {
>      QList<Tp::TextChannelPtr> channels;
> +    QStringList targetIds = properties["participantIds"].toStringList();
> +    int chatType = properties["chatType"].toUInt();
> +    if (chatType == 0 && targetIds.size() == 1) {

use Tp::HandleTypeNone instead of 0.

> +        chatType = 1;

use Tp::HandleTypeContact instead of 1.

> +    }
> +    QString roomId = properties["threadId"].toString();
>  
>      Q_FOREACH(const Tp::TextChannelPtr &channel, mChannels) {
>          int count = 0;
> @@ -479,6 +178,17 @@
>              continue;
>          }
>  
> +        if (chatType != channel->targetHandleType()) {
> +            continue;
> +        }
> +
> +        if (chatType == 2) {

use Tp::HandleTypeRoom instead of 2.

> +            if (!roomId.isEmpty() && channel->targetHandleType() == chatType && roomId == channel->targetId()) {
> +                channels.append(channel);
> +            }
> +            continue;
> +        }
> +
>          // this is a special case. We have to check if we are looking for a channel open with our self contact.
>          bool channelToSelfContact = channel->groupContacts(true).size() == 1 && targetIds.size() == 1 &&
>                            channel->targetHandleType() == Tp::HandleTypeContact &&


-- 
https://code.launchpad.net/~phablet-team/telephony-service/staging/+merge/309015
Your team Ubuntu Phablet Team is subscribed to branch lp:telephony-service.



More information about the Ubuntu-reviews mailing list