[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