[Telepathy-commits] [telepathy-qt4/master] call example: Added to repository.

Andre Moreira Magalhaes (andrunko) andre.magalhaes at collabora.co.uk
Fri Mar 13 14:14:34 PDT 2009


This is a simple example to make voice calls, video not supported.

P.s.: Maybe we should move FarsightChannel to the public API.
---
 examples/call/Makefile.am            |   18 +++-
 examples/call/call-handler.cpp       |   69 ++++++++++
 examples/call/call-handler.h         |   55 ++++++++
 examples/call/call-roster-widget.cpp |   90 +++++++++++++
 examples/call/call-roster-widget.h   |   60 +++++++++
 examples/call/call-widget.cpp        |  234 +++++++++++++++++++++++++++++++++
 examples/call/call-widget.h          |   81 ++++++++++++
 examples/call/call-window.cpp        |  170 ++++++++++++++++++++++++
 examples/call/call-window.h          |   69 ++++++++++
 examples/call/farsight-glue.cpp      |  238 ++++++++++++++++++++++++++++++++--
 examples/call/farsight-glue.h        |   45 ++++++-
 examples/call/main.cpp               |   32 +++++-
 12 files changed, 1139 insertions(+), 22 deletions(-)
 create mode 100644 examples/call/call-handler.cpp
 create mode 100644 examples/call/call-handler.h
 create mode 100644 examples/call/call-roster-widget.cpp
 create mode 100644 examples/call/call-roster-widget.h
 create mode 100644 examples/call/call-widget.cpp
 create mode 100644 examples/call/call-widget.h
 create mode 100644 examples/call/call-window.cpp
 create mode 100644 examples/call/call-window.h

diff --git a/examples/call/Makefile.am b/examples/call/Makefile.am
index 4593521..b042690 100644
--- a/examples/call/Makefile.am
+++ b/examples/call/Makefile.am
@@ -12,14 +12,28 @@ call_LDADD = \
 	$(QTGUI_LIBS) \
 	$(QTDBUS_LIBS) \
 	$(TP_FARSIGHT_LIBS) \
-	$(top_builddir)/TelepathyQt4/libtelepathy-qt4.la
+	$(top_builddir)/TelepathyQt4/libtelepathy-qt4.la \
+	$(top_builddir)/examples/roster/libtelepathy-qt4-examples-roster.la
 
 call_SOURCES = \
 	main.cpp \
+	call-handler.cpp \
+	call-handler.h \
+	call-roster-widget.cpp \
+	call-roster-widget.h \
+	call-widget.cpp \
+	call-widget.h \
+	call-window.cpp \
+	call-window.h \
 	farsight-glue.cpp \
 	farsight-glue.h
 
-nodist_call_SOURCES =
+nodist_call_SOURCES = \
+	_gen/call-handler.moc.hpp \
+	_gen/call-roster-widget.moc.hpp \
+	_gen/call-widget.moc.hpp \
+	_gen/call-window.moc.hpp \
+	_gen/farsight-glue.moc.hpp
 
 BUILT_SOURCES = \
 	$(nodist_call_SOURCES)
diff --git a/examples/call/call-handler.cpp b/examples/call/call-handler.cpp
new file mode 100644
index 0000000..5a821cd
--- /dev/null
+++ b/examples/call/call-handler.cpp
@@ -0,0 +1,69 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (call) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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 "call-handler.h"
+#include "_gen/call-handler.moc.hpp"
+
+#include "call-widget.h"
+
+#include <TelepathyQt4/Client/Channel>
+#include <TelepathyQt4/Client/Contact>
+
+#include <QDebug>
+
+using namespace Telepathy::Client;
+
+CallHandler::CallHandler(QObject *parent)
+    : QObject(parent)
+{
+}
+
+CallHandler::~CallHandler()
+{
+    foreach (CallWidget *call, mCalls) {
+        call->close();
+    }
+}
+
+void CallHandler::addOutgoingCall(const QSharedPointer<Contact> &contact)
+{
+    CallWidget *call = new CallWidget(contact);
+    mCalls.append(call);
+    connect(call,
+            SIGNAL(destroyed(QObject *)),
+            SLOT(onCallTerminated(QObject *)));
+    call->show();
+}
+
+void CallHandler::addIncomingCall(const ChannelPtr &chan)
+{
+    CallWidget *call = new CallWidget(chan);
+    mCalls.append(call);
+    connect(call,
+            SIGNAL(destroyed(QObject *)),
+            SLOT(onCallTerminated(QObject *)));
+    call->show();
+}
+
+void CallHandler::onCallTerminated(QObject *obj)
+{
+    // Why does obj comes 0x0?
+    mCalls.removeOne((CallWidget *) sender());
+}
diff --git a/examples/call/call-handler.h b/examples/call/call-handler.h
new file mode 100644
index 0000000..f50c81e
--- /dev/null
+++ b/examples/call/call-handler.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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
+ */
+
+#ifndef _TelepathyQt4_examples_call_call_handler_h_HEADER_GUARD_
+#define _TelepathyQt4_examples_call_call_handler_h_HEADER_GUARD_
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include <TelepathyQt4/Client/Channel>
+
+namespace Telepathy {
+namespace Client {
+class Contact;
+}
+}
+
+class CallWidget;
+
+class CallHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    CallHandler(QObject *parent = 0);
+    virtual ~CallHandler();
+
+    void addOutgoingCall(const QSharedPointer<Telepathy::Client::Contact> &contact);
+    void addIncomingCall(const Telepathy::Client::ChannelPtr &chan);
+
+private Q_SLOTS:
+    void onCallTerminated(QObject *);
+
+private:
+    QList<CallWidget *> mCalls;
+};
+
+#endif
diff --git a/examples/call/call-roster-widget.cpp b/examples/call/call-roster-widget.cpp
new file mode 100644
index 0000000..1844855
--- /dev/null
+++ b/examples/call/call-roster-widget.cpp
@@ -0,0 +1,90 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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 "call-roster-widget.h"
+#include "_gen/call-roster-widget.moc.hpp"
+
+#include "call-handler.h"
+#include "call-widget.h"
+
+#include <examples/roster/roster-item.h>
+
+#include <TelepathyQt4/Client/Connection>
+#include <TelepathyQt4/Client/Contact>
+#include <TelepathyQt4/Client/ContactManager>
+#include <TelepathyQt4/Client/PendingChannel>
+#include <TelepathyQt4/Client/PendingReady>
+#include <TelepathyQt4/Client/StreamedMediaChannel>
+
+#include <QAction>
+#include <QDebug>
+
+using namespace Telepathy::Client;
+
+CallRosterWidget::CallRosterWidget(CallHandler *callHandler, QWidget *parent)
+    : RosterWidget(parent),
+      mCallHandler(callHandler)
+{
+    createActions();
+    setupGui();
+}
+
+CallRosterWidget::~CallRosterWidget()
+{
+}
+
+RosterItem *CallRosterWidget::createItemForContact(const QSharedPointer<Contact> &contact,
+        bool &exists)
+{
+    return RosterWidget::createItemForContact(contact, exists);
+}
+
+void CallRosterWidget::createActions()
+{
+    mCallAction = new QAction(QLatin1String("Call Contact"), this);
+    mCallAction->setEnabled(false);
+    connect(mCallAction,
+            SIGNAL(triggered(bool)),
+            SLOT(onCallActionTriggered(bool)));
+}
+
+void CallRosterWidget::setupGui()
+{
+    QListWidget *list = listWidget();
+    list->insertAction(list->actions().first(), mCallAction);
+}
+
+void CallRosterWidget::updateActions(RosterItem *item)
+{
+    mCallAction->setEnabled(item);
+}
+
+void CallRosterWidget::onCallActionTriggered(bool checked)
+{
+    QList<QListWidgetItem *> selectedItems = listWidget()->selectedItems();
+    if (selectedItems.isEmpty()) {
+        return;
+    }
+
+    Q_ASSERT(selectedItems.size() == 1);
+    RosterItem *item = dynamic_cast<RosterItem*>(selectedItems.first());
+    QSharedPointer<Contact> contact = item->contact();
+    mCallHandler->addOutgoingCall(contact);
+}
diff --git a/examples/call/call-roster-widget.h b/examples/call/call-roster-widget.h
new file mode 100644
index 0000000..45e7fd1
--- /dev/null
+++ b/examples/call/call-roster-widget.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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
+ */
+
+#ifndef _TelepathyQt4_examples_call_call_roster_widget_h_HEADER_GUARD_
+#define _TelepathyQt4_examples_call_call_roster_widget_h_HEADER_GUARD_
+
+#include <examples/roster/roster-widget.h>
+
+namespace Telepathy {
+namespace Client {
+class Channel;
+}
+}
+
+class CallHandler;
+class CallWidget;
+
+class CallRosterWidget : public RosterWidget
+{
+    Q_OBJECT
+
+public:
+    CallRosterWidget(CallHandler *callHandler, QWidget *parent = 0);
+    virtual ~CallRosterWidget();
+
+protected:
+    virtual RosterItem *createItemForContact(
+            const QSharedPointer<Telepathy::Client::Contact> &contact,
+            bool &exists);
+    virtual void updateActions(RosterItem *item);
+
+private Q_SLOTS:
+    void onCallActionTriggered(bool);
+
+private:
+    void createActions();
+    void setupGui();
+
+    CallHandler *mCallHandler;
+    QAction *mCallAction;
+};
+
+#endif
diff --git a/examples/call/call-widget.cpp b/examples/call/call-widget.cpp
new file mode 100644
index 0000000..4a43540
--- /dev/null
+++ b/examples/call/call-widget.cpp
@@ -0,0 +1,234 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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 "call-widget.h"
+#include "_gen/call-widget.moc.hpp"
+
+#include <TelepathyQt4/Client/Channel>
+#include <TelepathyQt4/Client/Connection>
+#include <TelepathyQt4/Client/Contact>
+#include <TelepathyQt4/Client/ContactManager>
+#include <TelepathyQt4/Client/PendingChannel>
+#include <TelepathyQt4/Client/PendingReady>
+#include <TelepathyQt4/Client/StreamedMediaChannel>
+
+#include <QAction>
+#include <QDebug>
+#include <QLabel>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QStackedWidget>
+#include <QVariantMap>
+
+using namespace Telepathy::Client;
+
+CallWidget::CallWidget(const QSharedPointer<Contact> &contact,
+        QWidget *parent)
+    : QWidget(parent),
+      mContact(contact)
+{
+    setWindowTitle(QString("Call (%1)").arg(mContact->id()));
+
+    setAttribute(Qt::WA_DeleteOnClose);
+
+    setupGui();
+
+    QVariantMap request;
+    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+                   TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+                   Telepathy::HandleTypeContact);
+    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+                   contact->handle()[0]);
+
+    Connection *conn = contact->manager()->connection();
+    connect(conn->ensureChannel(request),
+            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+            SLOT(onChannelCreated(Telepathy::Client::PendingOperation*)));
+}
+
+CallWidget::CallWidget(const ChannelPtr &chan,
+        QWidget *parent)
+    : QWidget(parent),
+      mChan(chan)
+{
+    setWindowTitle(QString("Call"));
+
+    setAttribute(Qt::WA_DeleteOnClose);
+
+    setupGui();
+
+    connect(mChan->becomeReady(StreamedMediaChannel::FeatureStreams),
+            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+            SLOT(onChannelReady(Telepathy::Client::PendingOperation*)));
+    connect(mChan.data(),
+            SIGNAL(invalidated(DBusProxy *, const QString &, const QString &)),
+            SLOT(onChannelInvalidated(DBusProxy *, const QString &, const QString &)));
+}
+
+CallWidget::~CallWidget()
+{
+    if (mChan) {
+        mChan->requestClose();
+    }
+}
+
+void CallWidget::setupGui()
+{
+    QHBoxLayout *mainBox = new QHBoxLayout;
+
+    mStack = new QStackedWidget();
+
+    mLblStatus = new QLabel("Initiating...");
+    mLblStatus->setAlignment(Qt::AlignCenter);
+    mStack->addWidget(mLblStatus);
+
+    QFrame *frame = new QFrame;
+
+    QHBoxLayout *hbox = new QHBoxLayout;
+
+    mBtnAccept = new QPushButton("Call");
+    mBtnAccept->setEnabled(false);
+    connect(mBtnAccept,
+            SIGNAL(clicked(bool)),
+            SLOT(onBtnAcceptClicked()));
+    hbox->addWidget(mBtnAccept);
+    mBtnReject = new QPushButton("Terminate");
+    mBtnReject->setEnabled(false);
+    connect(mBtnReject,
+            SIGNAL(clicked(bool)),
+            SLOT(onBtnRejectClicked()));
+    hbox->addWidget(mBtnReject);
+
+    frame->setLayout(hbox);
+
+    mStack->addWidget(frame);
+
+    mStack->setCurrentIndex(0);
+
+    mainBox->addWidget(mStack);
+
+    setLayout(mainBox);
+}
+
+void CallWidget::onChannelCreated(PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "CallWidget::onChannelCreated: channel cannot be created:" <<
+            op->errorName() << "-" << op->errorMessage();
+        mLblStatus->setText("Unable to establish call");
+        mStack->setCurrentIndex(0);
+        return;
+    }
+
+    PendingChannel *pc = qobject_cast<PendingChannel *>(op);
+
+    mChan = pc->channel();
+
+    connect(mChan->becomeReady(StreamedMediaChannel::FeatureStreams),
+            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+            SLOT(onChannelReady(Telepathy::Client::PendingOperation*)));
+}
+
+void CallWidget::onChannelReady(PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "CallWidget::onChannelReady: channel cannot become ready:" <<
+            op->errorName() << "-" << op->errorMessage();
+        mLblStatus->setText("Unable to establish call");
+        mStack->setCurrentIndex(0);
+        return;
+    }
+
+    mStack->setCurrentIndex(1);
+
+    StreamedMediaChannel *streamChan =
+        qobject_cast<StreamedMediaChannel *>(mChan.data());
+    if (streamChan->awaitingLocalAnswer()) {
+        mBtnAccept->setText("Accept");
+        mBtnAccept->setEnabled(true);
+        mBtnReject->setText("Reject");
+        mBtnReject->setEnabled(true);
+    } else {
+        mBtnAccept->setText("Call");
+        mBtnAccept->setEnabled(false);
+        mBtnReject->setText("Terminate");
+        mBtnReject->setEnabled(true);
+
+        PendingMediaStreams *pms =
+            streamChan->requestStream(mContact, Telepathy::MediaStreamTypeAudio);
+        connect(pms,
+                SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+                SLOT(onStreamCreated(Telepathy::Client::PendingOperation*)));
+    }
+
+    mTfChan = new FarsightChannel(streamChan, this);
+    connect(mTfChan,
+            SIGNAL(statusChanged(Telepathy::Client::FarsightChannel::Status)),
+            SLOT(onTfChannelStatusChanged(Telepathy::Client::FarsightChannel::Status)));
+}
+
+void CallWidget::onChannelInvalidated(DBusProxy *proxy,
+        const QString &errorName, const QString &errorMessage)
+{
+    qDebug() << "CallWindow::onChannelInvalidated: channel became invalid:" <<
+        errorName << "-" << errorMessage;
+    mLblStatus->setText("Call terminated");
+    mStack->setCurrentIndex(0);
+}
+
+void CallWidget::onStreamCreated(PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "CallWidget::onStreamCreated: unable to create audio stream:" <<
+            op->errorName() << "-" << op->errorMessage();
+        mLblStatus->setText("Unable to establish call");
+        mStack->setCurrentIndex(0);
+        return;
+    }
+}
+
+void CallWidget::onTfChannelStatusChanged(FarsightChannel::Status status)
+{
+    if (status == FarsightChannel::StatusDisconnected) {
+        mLblStatus->setText("Call terminated");
+        mStack->setCurrentIndex(0);
+    }
+}
+
+void CallWidget::onBtnAcceptClicked()
+{
+    StreamedMediaChannel *streamChan =
+        qobject_cast<StreamedMediaChannel *>(mChan.data());
+    streamChan->acceptCall();
+
+    mBtnAccept->setText("Call");
+    mBtnAccept->setEnabled(false);
+    mBtnReject->setText("Terminate");
+    mBtnReject->setEnabled(true);
+}
+
+void CallWidget::onBtnRejectClicked()
+{
+    mChan->requestClose();
+
+    mLblStatus->setText("Call terminated");
+    mStack->setCurrentIndex(0);
+}
diff --git a/examples/call/call-widget.h b/examples/call/call-widget.h
new file mode 100644
index 0000000..f3ae945
--- /dev/null
+++ b/examples/call/call-widget.h
@@ -0,0 +1,81 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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
+ */
+
+#ifndef _TelepathyQt4_examples_call_call_widget_h_HEADER_GUARD_
+#define _TelepathyQt4_examples_call_call_widget_h_HEADER_GUARD_
+
+#include <QSharedPointer>
+#include <QWidget>
+
+#include <TelepathyQt4/Client/Channel>
+
+#include "farsight-glue.h"
+
+namespace Telepathy {
+namespace Client {
+class Contact;
+class DBusProxy;
+class PendingOperation;
+}
+}
+
+class QLabel;
+class QPushButton;
+class QStackedWidget;
+
+class CallWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    CallWidget(const QSharedPointer<Telepathy::Client::Contact> &contact,
+               QWidget *parent = 0);
+    CallWidget(const Telepathy::Client::ChannelPtr &chan,
+               QWidget *parent = 0);
+    virtual ~CallWidget();
+
+    QSharedPointer<Telepathy::Client::Contact> contact() const { return mContact; }
+    Telepathy::Client::ChannelPtr channel() const { return mChan; }
+
+private Q_SLOTS:
+    void onChannelCreated(Telepathy::Client::PendingOperation *);
+    void onChannelReady(Telepathy::Client::PendingOperation *);
+    void onChannelInvalidated(Telepathy::Client::DBusProxy *,
+            const QString &, const QString &);
+    void onStreamCreated(Telepathy::Client::PendingOperation *);
+    void onTfChannelStatusChanged(Telepathy::Client::FarsightChannel::Status);
+
+    void onBtnAcceptClicked();
+    void onBtnRejectClicked();
+
+private:
+    void createActions();
+    void setupGui();
+
+    Telepathy::Client::ChannelPtr mChan;
+    QSharedPointer<Telepathy::Client::Contact> mContact;
+    Telepathy::Client::FarsightChannel *mTfChan;
+    QPushButton *mBtnAccept;
+    QPushButton *mBtnReject;
+    QLabel *mLblStatus;
+    QStackedWidget *mStack;
+};
+
+#endif
diff --git a/examples/call/call-window.cpp b/examples/call/call-window.cpp
new file mode 100644
index 0000000..a5c6058
--- /dev/null
+++ b/examples/call/call-window.cpp
@@ -0,0 +1,170 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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 "call-window.h"
+#include "_gen/call-window.moc.hpp"
+
+#include "call-handler.h"
+#include "call-roster-widget.h"
+
+#include <TelepathyQt4/Types>
+#include <TelepathyQt4/Client/Connection>
+#include <TelepathyQt4/Client/ConnectionManager>
+#include <TelepathyQt4/Client/PendingConnection>
+#include <TelepathyQt4/Client/PendingOperation>
+#include <TelepathyQt4/Client/PendingReady>
+#include <TelepathyQt4/Client/StreamedMediaChannel>
+
+#include <QDebug>
+
+using namespace Telepathy::Client;
+
+CallWindow::CallWindow(const QString &username, const QString &password,
+        QWidget *parent)
+    : QMainWindow(parent),
+      mUsername(username),
+      mPassword(password)
+{
+    setWindowTitle("Roster");
+
+    mCM = new ConnectionManager("gabble", this);
+    connect(mCM->becomeReady(),
+            SIGNAL(finished(Telepathy::Client::PendingOperation *)),
+            SLOT(onCMReady(Telepathy::Client::PendingOperation *)));
+
+    mCallHandler = new CallHandler(this);
+
+    setupGui();
+
+    resize(240, 320);
+}
+
+CallWindow::~CallWindow()
+{
+    if (mConn) {
+        mConn->requestDisconnect();
+    }
+}
+
+void CallWindow::setupGui()
+{
+    mRoster = new CallRosterWidget(mCallHandler);
+    setCentralWidget(mRoster);
+}
+
+void CallWindow::onCMReady(Telepathy::Client::PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "CM cannot become ready";
+        return;
+    }
+
+    qDebug() << "CM ready";
+    QVariantMap params;
+    params.insert("account", QVariant(mUsername));
+    params.insert("password", QVariant(mPassword));
+    PendingConnection *pconn = mCM->requestConnection("jabber", params);
+    connect(pconn,
+            SIGNAL(finished(Telepathy::Client::PendingOperation *)),
+            SLOT(onConnectionCreated(Telepathy::Client::PendingOperation *)));
+}
+
+void CallWindow::onConnectionCreated(Telepathy::Client::PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "Unable to create connection";
+        return;
+    }
+
+    qDebug() << "Connection created";
+    PendingConnection *pconn =
+        qobject_cast<PendingConnection *>(op);
+    ConnectionPtr conn = pconn->connection();
+    mConn = conn;
+    connect(conn->requestConnect(Connection::FeatureSelfContact),
+            SIGNAL(finished(Telepathy::Client::PendingOperation *)),
+            SLOT(onConnectionConnected(Telepathy::Client::PendingOperation *)));
+    connect(conn.data(),
+            SIGNAL(invalidated(Telepathy::Client::DBusProxy *, const QString &, const QString &)),
+            SLOT(onConnectionInvalidated(Telepathy::Client::DBusProxy *, const QString &, const QString &)));
+}
+
+void CallWindow::onConnectionConnected(Telepathy::Client::PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "Connection cannot become connected";
+        return;
+    }
+
+    PendingReady *pr = qobject_cast<PendingReady *>(op);
+    Connection *conn = qobject_cast<Connection *>(pr->object());
+
+    if (conn->interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CAPABILITIES)) {
+        Telepathy::CapabilityPair capability = {
+            TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA,
+            Telepathy::ChannelMediaCapabilityAudio |
+                Telepathy::ChannelMediaCapabilityNATTraversalSTUN |
+                Telepathy::ChannelMediaCapabilityNATTraversalGTalkP2P
+        };
+        qDebug() << "CallWindow::onConnectionConnected: advertising capabilities";
+        conn->capabilitiesInterface()->AdvertiseCapabilities(
+                Telepathy::CapabilityPairList() << capability,
+                QStringList());
+    }
+
+    if (conn->interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS)) {
+        qDebug() << "CallWindow::onConnectionConnected: connecting to Connection.Interface.NewChannels";
+        connect(conn->requestsInterface(),
+                SIGNAL(NewChannels(const Telepathy::ChannelDetailsList&)),
+                SLOT(onNewChannels(const Telepathy::ChannelDetailsList&)));
+    }
+
+    mRoster->addConnection(conn);
+}
+
+void CallWindow::onConnectionInvalidated(DBusProxy *proxy,
+        const QString &errorName, const QString &errorMessage)
+{
+    qDebug() << "CallWindow::onConnectionInvalidated: connection became invalid:" <<
+        errorName << "-" << errorMessage;
+    mRoster->removeConnection(mConn.data());
+    mConn.reset();
+}
+
+void CallWindow::onNewChannels(const Telepathy::ChannelDetailsList &channels)
+{
+    qDebug() << "CallWindow::onNewChannels";
+    foreach (const Telepathy::ChannelDetails &details, channels) {
+        QString channelType = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
+        bool requested = details.properties.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".Requested")).toBool();
+        qDebug() << "CallWindow::onNewChannels: channelType:" << channelType;
+        qDebug() << "CallWindow::onNewChannels: requested:" << requested;
+
+        if (channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA &&
+            !requested) {
+            ChannelPtr channel = ChannelPtr(
+                    new StreamedMediaChannel(mConn.data(),
+                        details.channel.path(),
+                        details.properties));
+            mCallHandler->addIncomingCall(channel);
+            qDebug() << "CallWindow::onNewChannels: new call received";
+        }
+    }
+}
diff --git a/examples/call/call-window.h b/examples/call/call-window.h
new file mode 100644
index 0000000..bc1e436
--- /dev/null
+++ b/examples/call/call-window.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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
+ */
+
+#ifndef _TelepathyQt4_examples_call_call_window_h_HEADER_GUARD_
+#define _TelepathyQt4_examples_call_call_window_h_HEADER_GUARD_
+
+#include <QMainWindow>
+#include <QSharedPointer>
+
+#include <TelepathyQt4/Client/Connection>
+#include <TelepathyQt4/Types>
+
+namespace Telepathy {
+namespace Client {
+class ConnectionManager;
+class DBusProxy;
+class PendingOperation;
+}
+}
+
+class CallHandler;
+class CallRosterWidget;
+
+class CallWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    CallWindow(const QString &username, const QString &password,
+            QWidget *parent = 0);
+    virtual ~CallWindow();
+
+private Q_SLOTS:
+    void onCMReady(Telepathy::Client::PendingOperation *);
+    void onConnectionCreated(Telepathy::Client::PendingOperation *);
+    void onConnectionConnected(Telepathy::Client::PendingOperation *);
+    void onConnectionInvalidated(Telepathy::Client::DBusProxy *,
+            const QString &, const QString &);
+    void onNewChannels(const Telepathy::ChannelDetailsList &);
+
+private:
+    void setupGui();
+
+    Telepathy::Client::ConnectionManager *mCM;
+    Telepathy::Client::ConnectionPtr mConn;
+    QString mUsername;
+    QString mPassword;
+    CallHandler *mCallHandler;
+    CallRosterWidget *mRoster;
+};
+
+#endif
diff --git a/examples/call/farsight-glue.cpp b/examples/call/farsight-glue.cpp
index 00266e5..26cd724 100644
--- a/examples/call/farsight-glue.cpp
+++ b/examples/call/farsight-glue.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "farsight-glue.h"
+#include "_gen/farsight-glue.moc.hpp"
 
 #include <QDebug>
 
@@ -30,15 +31,57 @@
 #include <TelepathyQt4/Client/Connection>
 #include <TelepathyQt4/Client/StreamedMediaChannel>
 
-TfChannel *tfChannelFromQt(const Telepathy::Client::Connection *connection,
-        const Telepathy::Client::StreamedMediaChannel *channel)
+namespace Telepathy {
+namespace Client {
+
+struct FarsightChannel::Private
+{
+    Private(FarsightChannel *parent, StreamedMediaChannel *channel);
+    ~Private();
+
+    static gboolean busWatch(GstBus *bus,
+            GstMessage *message, FarsightChannel::Private *self);
+    static void onClosed(TfChannel *tfChannel,
+            FarsightChannel::Private *self);
+    static void onSessionCreated(TfChannel *tfChannel,
+            FsConference *conference, FsParticipant *participant,
+            FarsightChannel::Private *self);
+    static void onStreamCreated(TfChannel *tfChannel,
+            TfStream *stream, FarsightChannel::Private *self);
+    static void onSrcPadAdded(TfStream *stream,
+            GstPad *pad, FsCodec *codec, FarsightChannel::Private *self);
+    static gboolean onRequestResource(TfStream *stream,
+            guint direction, gpointer data);
+
+    FarsightChannel *parent;
+    StreamedMediaChannel *channel;
+    Status status;
+    TfChannel *tfChannel;
+    GstBus *bus;
+    GstElement *pipeline;
+    GstElement *audio_input;
+    GstElement *audio_output;
+};
+
+FarsightChannel::Private::Private(FarsightChannel *parent,
+        StreamedMediaChannel *channel)
+    : parent(parent),
+      channel(channel),
+      status(StatusDisconnected),
+      tfChannel(0),
+      bus(0),
+      pipeline(0),
+      audio_input(0),
+      audio_output(0)
 {
     TpDBusDaemon *dbus = tp_dbus_daemon_dup(0);
     if (!dbus) {
         qWarning() << "Unable to connect to D-Bus";
-        return 0;
+        return;
     }
 
+    Connection *connection = channel->connection();
+
     TpConnection *gconnection = tp_connection_new(dbus,
             connection->busName().toAscii(),
             connection->objectPath().toAscii(),
@@ -48,28 +91,203 @@ TfChannel *tfChannelFromQt(const Telepathy::Client::Connection *connection,
 
     if (!gconnection) {
         qWarning() << "Unable to construct TpConnection";
-        return 0;
+        return;
     }
 
     TpChannel *gchannel = tp_channel_new(gconnection,
             channel->objectPath().toAscii(),
             TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA,
-            TP_UNKNOWN_HANDLE_TYPE, 0, 0);
+            (TpHandleType) channel->targetHandleType(),
+            channel->targetHandle(),
+            0);
     g_object_unref(gconnection);
     gconnection = 0;
 
     if (!gchannel) {
         qWarning() << "Unable to construct TpChannel";
-        return 0;
+        return;
     }
 
-    TfChannel *ret = tf_channel_new(gchannel);
+    tfChannel = tf_channel_new(gchannel);
     g_object_unref(gchannel);
     gchannel = 0;
 
-    if (!ret) {
+    if (!tfChannel) {
         qWarning() << "Unable to construct TfChannel";
-        return 0;
+        return;
+    }
+
+    /* Set up the telepathy farsight channel */
+    g_signal_connect(tfChannel, "closed",
+        G_CALLBACK(&FarsightChannel::Private::onClosed), this);
+    g_signal_connect(tfChannel, "session-created",
+        G_CALLBACK(&FarsightChannel::Private::onSessionCreated), this);
+    g_signal_connect(tfChannel, "stream-created",
+        G_CALLBACK(&FarsightChannel::Private::onStreamCreated), this);
+
+    pipeline = gst_pipeline_new (NULL);
+    bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
+
+    audio_input = gst_element_factory_make("gconfaudiosrc", NULL);
+    gst_object_ref(audio_input);
+    gst_object_sink(audio_input);
+
+    audio_output = gst_bin_new("bin");
+    GstElement *resample = gst_element_factory_make("audioresample", NULL);
+    GstElement *audio_sink = gst_element_factory_make("gconfaudiosink", NULL);
+    gst_bin_add_many(GST_BIN(audio_output), resample, audio_sink, NULL);
+    gst_element_link_many(resample, audio_sink, NULL);
+    GstPad *sink = gst_element_get_static_pad(resample, "sink");
+    GstPad *ghost = gst_ghost_pad_new("sink", sink);
+    gst_element_add_pad(GST_ELEMENT(audio_output), ghost);
+    gst_object_unref(G_OBJECT(sink));
+    gst_object_ref(audio_output);
+    gst_object_sink(audio_output);
+
+    gst_element_set_state(pipeline, GST_STATE_PLAYING);
+
+    status = StatusConnecting;
+    emit parent->statusChanged(status);
+}
+
+FarsightChannel::Private::~Private()
+{
+    if (tfChannel) {
+        g_object_unref(tfChannel);
+        tfChannel = 0;
+    }
+
+    if (bus) {
+        g_object_unref(bus);
+        bus = 0;
+    }
+
+    gst_element_set_state(pipeline, GST_STATE_NULL);
+
+    if (pipeline) {
+        g_object_unref(pipeline);
+        pipeline = 0;
+    }
+
+    if (audio_input) {
+        g_object_unref(audio_input);
+        audio_input = 0;
+    }
+
+    if (audio_output) {
+        g_object_unref(audio_output);
+        audio_output = 0;
+    }
+}
+
+gboolean FarsightChannel::Private::busWatch(GstBus *bus,
+        GstMessage *message, FarsightChannel::Private *self)
+{
+    tf_channel_bus_message(self->tfChannel, message);
+    return TRUE;
+}
+
+void FarsightChannel::Private::onClosed(TfChannel *tfChannel,
+        FarsightChannel::Private *self)
+{
+    self->status = StatusDisconnected;
+    emit self->parent->statusChanged(self->status);
+}
+
+void FarsightChannel::Private::onSessionCreated(TfChannel *tfChannel,
+        FsConference *conference, FsParticipant *participant,
+        FarsightChannel::Private *self)
+{
+    gst_bus_add_watch(self->bus,
+            (GstBusFunc) &FarsightChannel::Private::busWatch, self);
+
+    gst_bin_add(GST_BIN(self->pipeline), GST_ELEMENT(conference));
+    gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING);
+}
+
+void FarsightChannel::Private::onStreamCreated(TfChannel *tfChannel,
+        TfStream *stream, FarsightChannel::Private *self)
+{
+    guint media_type;
+    GstPad *sink;
+
+    g_signal_connect(stream, "src-pad-added",
+        G_CALLBACK(&FarsightChannel::Private::onSrcPadAdded), self);
+    g_signal_connect(stream, "request-resource",
+        G_CALLBACK(&FarsightChannel::Private::onRequestResource), NULL);
+
+    g_object_get(stream, "media-type", &media_type,
+            "sink-pad", &sink, NULL);
+
+    GstPad *pad;
+    switch (media_type) {
+    case TP_MEDIA_STREAM_TYPE_AUDIO:
+        gst_bin_add(GST_BIN(self->pipeline), self->audio_input);
+        gst_element_set_state(self->audio_input, GST_STATE_PLAYING);
+
+        pad = gst_element_get_static_pad(self->audio_input, "src");
+        gst_pad_link(pad, sink);
+        break;
+    case TP_MEDIA_STREAM_TYPE_VIDEO:
+        // TODO
+        break;
+    default:
+        Q_ASSERT(false);
+    }
+
+    gst_object_unref(sink);
+}
+
+void FarsightChannel::Private::onSrcPadAdded(TfStream *stream,
+        GstPad *src, FsCodec *codec, FarsightChannel::Private *self)
+{
+    guint media_type;
+
+    g_object_get(stream, "media-type", &media_type, NULL);
+
+    GstPad *pad;
+    GstElement *element = 0;
+
+    switch (media_type) {
+    case TP_MEDIA_STREAM_TYPE_AUDIO:
+        element = self->audio_output;
+        g_object_ref(element);
+        break;
+    case TP_MEDIA_STREAM_TYPE_VIDEO:
+        // TODO
+        return;
+        break;
+    default:
+        Q_ASSERT(false);
     }
-    return ret;
+
+    gst_bin_add(GST_BIN(self->pipeline), element);
+
+    pad = gst_element_get_static_pad(element, "sink");
+    gst_element_set_state(element, GST_STATE_PLAYING);
+
+    gst_pad_link(src, pad);
+
+    self->status = StatusConnected;
+    emit self->parent->statusChanged(self->status);
+}
+
+gboolean FarsightChannel::Private::onRequestResource(TfStream *stream,
+        guint direction, gpointer data)
+{
+    return TRUE;
+}
+
+FarsightChannel::FarsightChannel(StreamedMediaChannel *channel, QObject *parent)
+    : QObject(parent),
+      mPriv(new Private(this, channel))
+{
+}
+
+FarsightChannel::~FarsightChannel()
+{
+    delete mPriv;
+}
+
+}
 }
diff --git a/examples/call/farsight-glue.h b/examples/call/farsight-glue.h
index 83dc9c6..d802bf3 100644
--- a/examples/call/farsight-glue.h
+++ b/examples/call/farsight-glue.h
@@ -19,19 +19,48 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#ifndef _TelepathyQt4_examples_call_farsight_h_HEADER_GUARD_
-#define _TelepathyQt4_examples_call_farsight_h_HEADER_GUARD_
+#ifndef _TelepathyQt4_examples_call_farsight_glue_h_HEADER_GUARD_
+#define _TelepathyQt4_examples_call_farsight_glue_h_HEADER_GUARD_
+
+#include <QObject>
 
 #include <telepathy-farsight/channel.h>
 
 namespace Telepathy {
 namespace Client {
-    class Connection;
-    class StreamedMediaChannel;
-} // Client
-} // Telepathy
 
-TfChannel *tfChannelFromQt(const Telepathy::Client::Connection *connection,
-        const Telepathy::Client::StreamedMediaChannel *channel);
+class Connection;
+class StreamedMediaChannel;
+
+class FarsightChannel : public QObject
+{
+    Q_OBJECT
+
+public:
+    enum Status {
+        StatusDisconnected = 0,
+        StatusConnecting = 1,
+        StatusConnected = 2
+    };
+
+    FarsightChannel(StreamedMediaChannel *channel, QObject *parent = 0);
+    virtual ~FarsightChannel();
+
+    Status status() const;
+
+    // TODO add a way to change input and output devices
+    //      add video support
+
+Q_SIGNALS:
+    void statusChanged(Telepathy::Client::FarsightChannel::Status status);
+
+private:
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+}
+}
 
 #endif
diff --git a/examples/call/main.cpp b/examples/call/main.cpp
index 9877b12..b78a140 100644
--- a/examples/call/main.cpp
+++ b/examples/call/main.cpp
@@ -1,5 +1,33 @@
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include <TelepathyQt4/Debug>
+#include <TelepathyQt4/Constants>
+#include <TelepathyQt4/Types>
+
+#include <QDebug>
+#include <QtGui>
+
+#include "call-window.h"
+
 int main(int argc, char **argv)
 {
-    // nothing here yet
-    return 0;
+    g_type_init();
+    gst_init(&argc, &argv);
+
+    QApplication app(argc, argv);
+
+    if (argc < 3) {
+        qDebug() << "usage: call username password";
+        return 1;
+    }
+
+    Telepathy::registerTypes();
+    Telepathy::enableDebug(true);
+    Telepathy::enableWarnings(true);
+
+    CallWindow w(argv[1], argv[2]);
+    w.show();
+
+    return app.exec();
 }
-- 
1.5.6.5




More information about the telepathy-commits mailing list