first release
[groupsms] / sms / tpsession / tpsessionaccount.cpp
diff --git a/sms/tpsession/tpsessionaccount.cpp b/sms/tpsession/tpsessionaccount.cpp
new file mode 100755 (executable)
index 0000000..0626737
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * This file is part of TpSession
+ *
+ * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * Contact Kate Alhola  kate.alholanokia.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "tpsessionaccount.h"
+#include <TelepathyQt4/Message>
+
+/**
+ * \class TpSessionAccount
+ * \headerfile <tpsessionaccount.h>
+ *
+ * TpSessionAccount class represents every account you have. As example account for “Ring” connection manager represents your cellular
+ * account and you may send and receive SMS with it. Gabble represents your GoogleTalk account if you have defined them.
+ * TpSessionAccounts are created by TpSession class,they are not intended to be created stand-alone
+
+ */
+/**
+ * \fn void TpSessionAccount::accountReady(TpSessionAccount *);
+ *
+ * Emitted when the account becomes ready
+ *
+ * \param  TpSessionAccount  pointer to account become ready
+ */
+/**
+ * \fn void TpSessionAccount::channelReady(TpSessionAccount *);
+ *
+ * Emitted when the account Manager becomes ready
+ *
+ * \param  TpSession  pointer to TpSession class
+ */
+/**
+ * \fn void TpSessionAccount::messageReceived(const Tp::ReceivedMessage &,TpSessionAccount *);
+ *
+ * Emitted when any of Account Managers recived message
+ *
+ * \param  Tp::ReceivedMessage  Message received
+ * \param  TpSessionAccount  pointer to account received message
+ */
+
+/**
+ * \fn void TpSessionAccount::newChannel(TpSessionAccount *,QString CjhannelType,QString peerId,const Tp::ChannelDetails &);
+ * \param  TpSession  pointer to TpSession class
+ * \param  ChannelType type of Channel, TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT for text channel, TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA for steram media, as exmple for incoming call
+ * \param  peedId PeerId, as example caller telephone number
+ * \param channeDetails needed if you would like to create a channel. For text chanels TpSession creates channel automatically. For calls, Maemo Call UI handles callcreation
+ */
+
+/**
+ * Construct a new TpSessionAccount object. This constructor is called by TpSession class when new account is created or fetched from account manager. It is not inended to be used stand alone
+ *
+ * \param am          Telepathy-Qt4 account manager for this account
+ * \param objectPath  Dbus object path tonew account
+ */
+TpSessionAccount::TpSessionAccount(Tp::AccountManagerPtr am,const QString &objectPath):
+        mAcc(Tp::Account::create(am->dbusConnection(),am->busName(), objectPath))
+
+{
+    connect(mAcc->becomeReady(),SIGNAL(finished(Tp::PendingOperation *)),SLOT(onReady(Tp::PendingOperation *)));
+    ready=false;
+    //  qDebug() << "TpSessionAccount::TpSessionAccount objectPath=" << objectPath;
+};
+
+
+void TpSessionAccount::onReady(Tp::PendingOperation *op)
+{
+
+    acc = mAcc.data();
+    qDebug() << "TpSessionAccount::onReady cmName=" << acc->cmName() << "haveConnection=" <<
+        (acc->haveConnection()? ( acc->connection()->isReady() ? "Ready":"notReady"):"no");
+
+    if(acc->haveConnection())
+    {
+
+        connect(acc->connection()->becomeReady(Tp::Connection::FeatureRoster | Tp::Connection::FeatureSelfContact ),
+                SIGNAL(finished(Tp::PendingOperation *)),
+                SLOT(onContactsConnectionReady(Tp::PendingOperation *)));
+        if (acc->connection()->isReady() && acc->connection()->interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS))
+        {
+            qDebug() << "TpSessionAccount::onReady: connecting to Connection.Interface.NewChannels";
+            connect(acc->connection()->requestsInterface(),
+                    SIGNAL(NewChannels(const Tp::ChannelDetailsList&)),
+                    SLOT(onNewChannels(const Tp::ChannelDetailsList&)));
+        }
+    }
+    else
+    { // If there is no connection, we are ready now, else we are ready when contacts connection is ready
+        qDebug() << "If there is no connection, we are ready now, else we are ready when contacts connection is ready";
+        ready=true;
+        Q_EMIT accountReady(this);
+    }
+}
+
+void TpSessionAccount::onContactsConnectionReady(Tp::PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "Connection cannot become ready" << acc->cmName();
+        return;
+    }
+
+    if (acc->connection()->interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS)) {
+        qDebug() << "TpSessionAccount::onContactsConectionReady: connecting to Connection.Interface.NewChannels";
+        connect(acc->connection()->requestsInterface(),
+                SIGNAL(NewChannels(const Tp::ChannelDetailsList&)),
+                SLOT(onNewChannels(const Tp::ChannelDetailsList&)));
+    } else qDebug() << "TpSessionAccount::onectionReady: does NO have CONNECTION_INTERFACE_REQUESTS";
+    Tp::PendingReady *pr = qobject_cast<Tp::PendingReady *>(op);
+    contactsConn = Tp::ConnectionPtr(qobject_cast<Tp::Connection *>(pr->object()));
+#if 0
+    connect(contactsConn->contactManager(),
+            SIGNAL(presencePublicationRequested(const Tp::Contacts &)),
+            SLOT(onPresencePublicationRequested(const Tp::Contacts &)));
+#endif
+    qDebug() << "TpSessionAccount::onContactsConnectionReady "<< acc->cmName() ;
+    //    RosterItem *item;
+    bool exists;
+    myContacts=contactsConn->contactManager()->allKnownContacts();
+    Q_FOREACH (const Tp::ContactPtr &contact, myContacts) {
+        qDebug() << "id=" <<contact->id() << " alias=" << contact->alias() << " presence=" << contact->presenceStatus()  ;
+        if(contact->id()==reqContact) {
+            addOutgoingChannel(contact);
+            reqContact="";
+        }
+    };
+    if(!reqContact.isEmpty() ) makeContactFromAddress(reqContact);
+    ready=true;
+    Q_EMIT accountReady(this);
+}
+
+
+/**
+ * Fetch Tp::ContactPtr for contact with given address. Contact is searched among contacts returned by contact manager for ths account.
+ * All connecions managers does not return contacts, as example Ring telephony contact manager does not. Gabble for Googletalk or Spirit for Skype does
+ * return contacts-
+ *
+ * \param id            Contact address/id, as example email address, telephone number etc. Only exact matches
+ * \return      TpContactPtr, if nontact is not returned TpContactPtr.isNull() is true
+ */
+
+Tp::ContactPtr TpSessionAccount::getContactFromAddress(QString id)
+{
+    Tp::ContactPtr p;
+    Q_FOREACH (const Tp::ContactPtr &contact, myContacts) {
+        if(contact->id()==reqContact) return p=contact;
+    }
+    return p;
+}
+/**
+ * Fetch TpSessionChannel for with given address. Contact is searched among active channels for this account.
+ *
+ *
+ * \param id            Contact address/id, as example email address, telephone number etc. Only exact matches
+ * \return          Pointer to TpSessionChannel or NULL if nit found
+ */
+
+TpSessionChannel* TpSessionAccount::getChannelFromPeerAddress(QString id)
+{
+    TpSessionChannel* p=NULL;
+    Q_FOREACH (TpSessionChannel* channel, myChannels) {
+        if(channel->peerId()==id) return p=channel;
+    }
+    return p;
+}
+/**
+ * Creates new contact with given address. This function is Acynchronous, it sends request to contact manager for contact creation,
+ *
+ * \param address           Contact address/id, as example email address, telephone number etc.
+ */
+
+void TpSessionAccount::makeContactFromAddress(QString address)
+{
+    qDebug() << "TpSessionAccount::makeContactFromAddress(QString address)";
+    reqContact=address;  // When we get retrieved signal, we check if it is this one
+    Tp::PendingContacts *pc = contactsConn->contactManager()->contactsForIdentifiers( QStringList(address) );
+    qDebug() << "111111111111111111";
+    connect(pc,SIGNAL(finished(Tp::PendingOperation *)),SLOT(onNewContactRetrieved(Tp::PendingOperation *)));
+}
+
+void TpSessionAccount::onNewContactRetrieved(Tp::PendingOperation *op)
+{
+    Tp::PendingContacts *pcontacts = qobject_cast<Tp::PendingContacts *>(op);
+    QList<Tp::ContactPtr> contacts = pcontacts->contacts();
+    QString username = pcontacts->identifiers().first();
+    if (contacts.size() != 1 || !contacts.first()) {
+        qDebug() << "Unable to add contact " <<reqContact;
+        return;
+    }
+
+    Tp::ContactPtr contact = contacts.first();
+    //    qDebug() << "TpSessionAccount::onContactRetrieved" << reqContact;
+    if(!reqContact.isEmpty()) addOutgoingChannel(contacts.first());
+}
+/**
+ * Send message to given address. This function is compled Acynchronous function that may produce multiple state transitions beforecomletion.
+ * If there is already existing TpSessionChannel for this contact, it simply queues message for sending and no forther transitions are needed
+ * If there are no hannel, it first check is there contact for this address, if is, it requests new channel to be created for ths channel and message
+ * is left waiting untill channel is created. If there is no contact, it sends request fr contact creation and when contact is created state machine
+ * proceeds to channel creation.
+ *
+ * MessageSent() signal is emitted when completed
+ *
+ * \param address           Contact address/id, as example email address, telephone number etc.
+ * \param message           Message string
+ */
+
+void TpSessionAccount::sendMessageToAddress(QString address,QString message)
+{
+    qDebug() << "TpSessionAccount::sendMessageToAddress(QString address,QString message)";
+
+    Tp::ContactPtr p;
+    TpSessionChannel* channel = getChannelFromPeerAddress(address);
+    if(channel) {
+        channel->sendMessage(message); // We have already channel
+        Q_EMIT messageQueued(this);
+    }
+    else {        
+        reqMessage=message;
+        p=getContactFromAddress(address); // Do we have contact ready ?
+        if(p.isNull())  // If not, create it
+        {
+            makeContactFromAddress(address); // Create and after created, send
+        }else
+        {
+            addOutgoingChannel(p); // Create channel and when ready, send
+        }
+    };
+}
+
+void TpSessionAccount::addOutgoingChannel(const Tp::ContactPtr &contact)
+{
+
+
+    //    qDebug() << "TpSessionAccount::addOutgoingChannel";
+
+    TpSessionChannel* newChannel=new TpSessionChannel(contact->manager()->connection(),contact);
+    connect(newChannel,SIGNAL(messageReceived(const Tp::ReceivedMessage &,TpSessionChannel *)),
+            SLOT(onMessageReceived(const Tp::ReceivedMessage &,TpSessionChannel *)));
+    connect(newChannel,SIGNAL(channelReady(TpSessionChannel *)),
+            SLOT(onOutgoingChannelReady(TpSessionChannel*)));
+    myChannels+=newChannel;
+
+}
+
+void TpSessionAccount::onOutgoingChannelReady(TpSessionChannel *ch)
+{
+    // qDebug() << "TpSessionAccoiunt::onOutgoingChannelReady";
+    Q_EMIT channelReady(this);
+    if(!reqMessage.isEmpty()) {
+        ch->sendMessage(reqMessage);
+        Q_EMIT messageQueued(this);
+    };
+    reqMessage.clear();
+}
+
+
+void TpSessionAccount::onMessageSent(const Tp::Message &msg,Tp::MessageSendingFlags, const QString &flags)
+{
+    //    qDebug() << "TpSessionAccount::onMessageSent";
+    Q_EMIT messageSent(msg,this);
+};
+
+void TpSessionAccount::onMessageReceived(const Tp::ReceivedMessage &msg,TpSessionChannel *ch)
+{
+    //    qDebug() << "TpSessionAccount::onMessageReceived " << msg.text();
+    Q_EMIT messageReceived(msg,this);
+};
+
+void TpSessionAccount::onNewChannels(const Tp::ChannelDetailsList &channels)
+{
+
+    Tp::TextChannelPtr myIngoingTextChannel;
+    //    qDebug() << "TpSessionAccount::onNewChannels";
+    Q_FOREACH (const Tp::ChannelDetails &details, channels) {
+        QString channelType = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+        QString targetId = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID")).toString();
+        bool requested = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested")).toBool();
+       //        qDebug() << " channelType:" << channelType <<" requested  :" << requested << " targetId" << targetId;
+
+        Q_EMIT newChannel(this,channelType,targetId,details);
+        if (channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT && !requested) {
+
+            myIngoingTextChannel = Tp::TextChannel::create(acc->connection(),details.channel.path(),details.properties);
+           //            qDebug() << "TpSessionAccount::onNewChannels path=" <<"path " << myIngoingTextChannel->objectPath();
+
+            TpSessionChannel* newChannel=new TpSessionChannel( myIngoingTextChannel);
+            connect(newChannel,SIGNAL(messageReceived(const Tp::ReceivedMessage &,TpSessionChannel *)),
+                    SLOT(onMessageReceived(const Tp::ReceivedMessage &,TpSessionChannel *)));
+            myChannels+=newChannel;
+        }
+        if (channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA && !requested) {
+            //            qDebug() << "Incoming call" ;
+        }
+    }
+}