diff options
| author | Tomasz Sterna <tomek@xiaoka.com> | 2014-06-24 03:36:17 +0200 |
|---|---|---|
| committer | Tomasz Sterna <tomek@xiaoka.com> | 2014-06-24 03:36:17 +0200 |
| commit | 79162515fc2ddb492fc24da80ca2000550971d4f (patch) | |
| tree | f91e013d0ef13931e00245c58baabc9aabd8be47 /daemon | |
| parent | 1f0cde7cfd31c180eaceeab4ee0ad24613eaf34c (diff) | |
Ported VoiceCallManager from Nemo/voicecall and wired to WatchConnector
Diffstat (limited to 'daemon')
| -rw-r--r-- | daemon/daemon.cpp | 12 | ||||
| -rw-r--r-- | daemon/daemon.pro | 16 | ||||
| -rw-r--r-- | daemon/manager.cpp | 85 | ||||
| -rw-r--r-- | daemon/manager.h | 36 | ||||
| -rw-r--r-- | daemon/voicecallhandler.cpp | 418 | ||||
| -rw-r--r-- | daemon/voicecallhandler.h | 90 | ||||
| -rw-r--r-- | daemon/voicecallmanager.cpp | 308 | ||||
| -rw-r--r-- | daemon/voicecallmanager.h | 106 |
8 files changed, 1068 insertions, 3 deletions
diff --git a/daemon/daemon.cpp b/daemon/daemon.cpp index c50af39..b49ea6e 100644 --- a/daemon/daemon.cpp +++ b/daemon/daemon.cpp @@ -26,8 +26,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "watchconnector.h" +#include "manager.h" + +#include <QCoreApplication> int main(int argc, char *argv[]) { + QCoreApplication app(argc, argv); + + watch::WatchConnector watch; + VoiceCallManager voice; + + Manager manager(&watch, &voice); + + return app.exec(); } diff --git a/daemon/daemon.pro b/daemon/daemon.pro index d1fb5ca..24bbfbd 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -2,12 +2,24 @@ TARGET = pebbled CONFIG += console CONFIG -= app_bundle +QT -= gui INCLUDEPATH += ../lib LIBS += -L$$OUT_PWD/../lib -lpebble -QT += bluetooth +QT += bluetooth dbus QMAKE_CXXFLAGS += -std=c++0x SOURCES += \ - daemon.cpp + daemon.cpp \ + voicecallmanager.cpp \ + voicecallhandler.cpp \ + manager.cpp + +HEADERS += \ + voicecallmanager.h \ + voicecallhandler.h \ + manager.h + +INSTALLS += target +target.path = /usr/sbin diff --git a/daemon/manager.cpp b/daemon/manager.cpp new file mode 100644 index 0000000..1bb71c0 --- /dev/null +++ b/daemon/manager.cpp @@ -0,0 +1,85 @@ +#include "manager.h" + +#include <QDebug> + +Manager::Manager(watch::WatchConnector *watch, VoiceCallManager *voice) : + QObject(0), watch(watch), voice(voice) +{ + connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged())); + connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &))); + + // Watch instantiated hangup, follow the orders + connect(watch, SIGNAL(hangup()), SLOT(hangupAll())); + + if (btDevice.isValid()) { + qDebug() << "BT local name:" << btDevice.name(); + } +} + +void Manager::onActiveVoiceCallChanged() +{ + qDebug() << "Manager::onActiveVoiceCallChanged()"; + + VoiceCallHandler* handler = voice->activeVoiceCall(); + if (handler) { + connect(handler, SIGNAL(statusChanged()), SLOT(onActiveVoiceCallStatusChanged())); + return; + } +} + +void Manager::onActiveVoiceCallStatusChanged() +{ + VoiceCallHandler* handler = voice->activeVoiceCall(); + if (!handler) { + qWarning() << "ActiveVoiceCallStatusChanged but no activeVoiceCall??"; + return; + } + + qDebug() << "handlerId:" << handler->handlerId() + << "providerId:" << handler->providerId() + << "status:" << handler->status() + << "statusText:" << handler->statusText() + << "lineId:" << handler->lineId() + << "incoming:" << handler->isIncoming(); + + if (!watch->isConnected()) { + qDebug() << "Watch is not connected"; + return; + } + + switch ((VoiceCallHandler::VoiceCallStatus)handler->status()) { + case VoiceCallHandler::STATUS_ALERTING: + case VoiceCallHandler::STATUS_DIALING: + qDebug() << "Tell outgoing:" << handler->lineId(); + watch->ring("+48123123123", "person", false); + break; + case VoiceCallHandler::STATUS_INCOMING: + case VoiceCallHandler::STATUS_WAITING: + qDebug() << "Tell incoming:" << handler->lineId(); + watch->ring("+48123123123", "person"); + break; + case VoiceCallHandler::STATUS_NULL: + case VoiceCallHandler::STATUS_DISCONNECTED: + qDebug() << "Endphone"; + watch->endPhoneCall(); + break; + case VoiceCallHandler::STATUS_ACTIVE: + qDebug() << "Startphone"; + watch->startPhoneCall(); + break; + case VoiceCallHandler::STATUS_HELD: + break; + } +} + +void Manager::onVoiceError(const QString &message) +{ + qWarning() << "Error: " << message; +} + +void Manager::hangupAll() +{ + foreach (VoiceCallHandler* handler, voice->voiceCalls()) { + handler->hangup(); + } +} diff --git a/daemon/manager.h b/daemon/manager.h new file mode 100644 index 0000000..fd39639 --- /dev/null +++ b/daemon/manager.h @@ -0,0 +1,36 @@ +#ifndef MANAGER_H +#define MANAGER_H + +#include "watchconnector.h" +#include "voicecallmanager.h" + +#include <QObject> +#include <QBluetoothLocalDevice> + +class Manager : public QObject +{ + Q_OBJECT + + QBluetoothLocalDevice btDevice; + + watch::WatchConnector *watch; + VoiceCallManager *voice; + +public: + explicit Manager(watch::WatchConnector *watch, VoiceCallManager *voice); + +signals: + +public slots: + void hangupAll(); + +protected slots: + void onBTDeviceDiscovered(const QBluetoothDeviceInfo & device); + + void onActiveVoiceCallChanged(); + void onVoiceError(const QString &message); + void onActiveVoiceCallStatusChanged(); + +}; + +#endif // MANAGER_H diff --git a/daemon/voicecallhandler.cpp b/daemon/voicecallhandler.cpp new file mode 100644 index 0000000..8e43fb3 --- /dev/null +++ b/daemon/voicecallhandler.cpp @@ -0,0 +1,418 @@ +#include "voicecallhandler.h" + +#include <QDebug> +#include <QTimer> +#include <QDBusInterface> +#include <QDBusPendingReply> +#include <QDBusReply> +#include <QVariantMap> + +/*! + \class VoiceCallHandler + \brief This is the D-Bus proxy for communicating with the voice call manager + from a declarative context, this interface specifically interfaces with + the managers' voice call handler instances. +*/ +class VoiceCallHandlerPrivate +{ + Q_DECLARE_PUBLIC(VoiceCallHandler) + +public: + VoiceCallHandlerPrivate(VoiceCallHandler *q, const QString &pHandlerId) + : q_ptr(q), handlerId(pHandlerId), interface(NULL), connected(false) + , duration(0), status(0), emergency(false), multiparty(false), forwarded(false) + { /* ... */ } + + VoiceCallHandler *q_ptr; + + QString handlerId; + + QDBusInterface *interface; + + bool connected; + int duration; + int status; + QString statusText; + QString lineId; + QString providerId; + QDateTime startedAt; + bool emergency; + bool multiparty; + bool forwarded; +}; + +/*! + Constructs a new proxy interface for the provided voice call handlerId. +*/ +VoiceCallHandler::VoiceCallHandler(const QString &handlerId, QObject *parent) + : QObject(parent), d_ptr(new VoiceCallHandlerPrivate(this, handlerId)) +{ + Q_D(VoiceCallHandler); + qDebug() << QString("Creating D-Bus interface to: ") + handlerId; + d->interface = new QDBusInterface("org.nemomobile.voicecall", + "/calls/" + handlerId, + "org.nemomobile.voicecall.VoiceCall", + QDBusConnection::sessionBus(), + this); + this->initialize(true); +} + +VoiceCallHandler::~VoiceCallHandler() +{ + Q_D(VoiceCallHandler); + delete d; +} + +void VoiceCallHandler::initialize(bool notifyError) +{ + Q_D(VoiceCallHandler); + bool success = false; + +/* +method return sender=:1.13 -> dest=:1.150 reply_serial=2 + string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.nemomobile.voicecall.VoiceCall"> + <property name="handlerId" type="s" access="read"/> + <property name="providerId" type="s" access="read"/> + <property name="status" type="i" access="read"/> + <property name="statusText" type="s" access="read"/> + <property name="lineId" type="s" access="read"/> + <property name="startedAt" type="((iii)(iiii)i)" access="read"> + <annotation name="org.qtproject.QtDBus.QtTypeName" value="QDateTime"/> + </property> + <property name="duration" type="i" access="read"/> + <property name="isIncoming" type="b" access="read"/> + <property name="isEmergency" type="b" access="read"/> + <property name="isMultiparty" type="b" access="read"/> + <property name="isForwarded" type="b" access="read"/> + <signal name="error"> + <arg name="message" type="s" direction="out"/> + </signal> + <signal name="statusChanged"> + </signal> + <signal name="lineIdChanged"> + </signal> + <signal name="startedAtChanged"> + </signal> + <signal name="durationChanged"> + </signal> + <signal name="emergencyChanged"> + </signal> + <signal name="multipartyChanged"> + </signal> + <signal name="forwardedChanged"> + </signal> + <method name="answer"> + <arg type="b" direction="out"/> + </method> + <method name="hangup"> + <arg type="b" direction="out"/> + </method> + <method name="hold"> + <arg type="b" direction="out"/> + <arg name="on" type="b" direction="in"/> + </method> + <method name="deflect"> + <arg type="b" direction="out"/> + <arg name="target" type="s" direction="in"/> + </method> + <method name="sendDtmf"> + <arg name="tones" type="s" direction="in"/> + </method> + </interface> + <interface name="org.freedesktop.DBus.Properties"> + <method name="Get"> + <arg name="interface_name" type="s" direction="in"/> + <arg name="property_name" type="s" direction="in"/> + <arg name="value" type="v" direction="out"/> + </method> + <method name="Set"> + <arg name="interface_name" type="s" direction="in"/> + <arg name="property_name" type="s" direction="in"/> + <arg name="value" type="v" direction="in"/> + </method> + <method name="GetAll"> + <arg name="interface_name" type="s" direction="in"/> + <arg name="values" type="a{sv}" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/> + </method> + </interface> + <interface name="org.freedesktop.DBus.Introspectable"> + <method name="Introspect"> + <arg name="xml_data" type="s" direction="out"/> + </method> + </interface> + <interface name="org.freedesktop.DBus.Peer"> + <method name="Ping"/> + <method name="GetMachineId"> + <arg name="machine_uuid" type="s" direction="out"/> + </method> + </interface> +</node> +" +*/ + + if(d->interface->isValid()) + { + success = true; + success &= (bool)QObject::connect(d->interface, SIGNAL(error(QString)), SIGNAL(error(QString))); + success &= (bool)QObject::connect(d->interface, SIGNAL(statusChanged()), SLOT(onStatusChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(lineIdChanged()), SLOT(onLineIdChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(durationChanged()), SLOT(onDurationChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(startedAtChanged()), SLOT(onStartedAtChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(emergencyChanged()), SLOT(onEmergencyChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(multipartyChanged()), SLOT(onMultipartyChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(forwardedChanged()), SLOT(onForwardedChanged())); + } + + if(!(d->connected = success)) + { + QTimer::singleShot(2000, this, SLOT(initialize())); + if(notifyError) emit this->error("Failed to connect to VCM D-Bus service."); + } else { + QDBusReply<QVariantMap> reply = d->interface->call("getProperties"); + if (reply.isValid()) { + QVariantMap props = reply.value(); + qDebug() << "VoiceCallHandler::initialize:" << props; + d->providerId = props["providerId"].toString(); + d->duration = props["duration"].toInt(); + d->status = props["status"].toInt(); + d->statusText = props["statusText"].toString(); + d->lineId = props["lineId"].toString(); + d->startedAt = QDateTime::fromMSecsSinceEpoch(props["startedAt"].toULongLong()); + d->multiparty = props["isMultiparty"].toBool(); + d->emergency = props["isEmergency"].toBool(); + d->forwarded = props["isForwarded"].toBool(); + emit durationChanged(); + emit statusChanged(); + emit lineIdChanged(); + emit startedAtChanged(); + emit multipartyChanged(); + emit emergencyChanged(); + emit forwardedChanged(); + } else if (notifyError) { + emit this->error("Failed to getProperties() from VCM D-Bus service."); + } + } +} + +void VoiceCallHandler::onDurationChanged() +{ + Q_D(VoiceCallHandler); + d->duration = d->interface->property("duration").toInt(); + emit durationChanged(); +} + +void VoiceCallHandler::onStatusChanged() +{ + Q_D(VoiceCallHandler); + d->status = d->interface->property("status").toInt(); + d->statusText = d->interface->property("statusText").toString(); + emit statusChanged(); +} + +void VoiceCallHandler::onLineIdChanged() +{ + Q_D(VoiceCallHandler); + d->lineId = d->interface->property("lineId").toString(); + emit lineIdChanged(); +} + +void VoiceCallHandler::onStartedAtChanged() +{ + Q_D(VoiceCallHandler); + d->startedAt = d->interface->property("startedAt").toDateTime(); + emit startedAtChanged(); +} + +void VoiceCallHandler::onEmergencyChanged() +{ + Q_D(VoiceCallHandler); + d->emergency = d->interface->property("isEmergency").toBool(); + emit emergencyChanged(); +} + +void VoiceCallHandler::onMultipartyChanged() +{ + Q_D(VoiceCallHandler); + d->multiparty = d->interface->property("isMultiparty").toBool(); + emit multipartyChanged(); +} + +void VoiceCallHandler::onForwardedChanged() +{ + Q_D(VoiceCallHandler); + d->forwarded = d->interface->property("isForwarded").toBool(); + emit forwardedChanged(); +} + +/*! + Returns this voice calls' handler id. + */ +QString VoiceCallHandler::handlerId() const +{ + Q_D(const VoiceCallHandler); + return d->handlerId; +} + +/*! + Returns this voice calls' provider id. + */ +QString VoiceCallHandler::providerId() const +{ + Q_D(const VoiceCallHandler); + return d->providerId; +} + +/*! + Returns this voice calls' call status. + */ +int VoiceCallHandler::status() const +{ + Q_D(const VoiceCallHandler); + return d->status; +} + +/*! + Returns this voice calls' call status as a symbolic string. + */ +QString VoiceCallHandler::statusText() const +{ + Q_D(const VoiceCallHandler); + return d->statusText; +} + +/*! + Returns this voice calls' remote end-point line id. + */ +QString VoiceCallHandler::lineId() const +{ + Q_D(const VoiceCallHandler); + return d->lineId; +} + +/*! + Returns this voice calls' started at property. + */ +QDateTime VoiceCallHandler::startedAt() const +{ + Q_D(const VoiceCallHandler); + return d->startedAt; +} + +/*! + Returns this voice calls' duration property. + */ +int VoiceCallHandler::duration() const +{ + Q_D(const VoiceCallHandler); + return d->duration; +} + +/*! + Returns this voice calls' incoming call flag property. + */ +bool VoiceCallHandler::isIncoming() const +{ + Q_D(const VoiceCallHandler); + return d->interface->property("isIncoming").toBool(); +} + +/*! + Returns this voice calls' multiparty flag property. + */ +bool VoiceCallHandler::isMultiparty() const +{ + Q_D(const VoiceCallHandler); + return d->multiparty; +} + +/*! + Returns this voice calls' forwarded flag property. + */ +bool VoiceCallHandler::isForwarded() const +{ + Q_D(const VoiceCallHandler); + return d->forwarded; +} + +/*! + Returns this voice calls' emergency flag property. + */ +bool VoiceCallHandler::isEmergency() const +{ + Q_D(const VoiceCallHandler); + return d->emergency; +} + +/*! + Initiates answering this call, if the call is an incoming call. + */ +void VoiceCallHandler::answer() +{ + Q_D(VoiceCallHandler); + QDBusPendingCall call = d->interface->asyncCall("answer"); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); +} + +/*! + Initiates droping the call, unless the call is disconnected. + */ +void VoiceCallHandler::hangup() +{ + Q_D(VoiceCallHandler); + QDBusPendingCall call = d->interface->asyncCall("hangup"); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); +} + +/*! + Initiates holding the call, unless the call is disconnected. + */ +void VoiceCallHandler::hold(bool on) +{ + Q_D(VoiceCallHandler); + QDBusPendingCall call = d->interface->asyncCall("hold", on); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); +} + +/*! + Initiates deflecting the call to the provided target phone number. + */ +void VoiceCallHandler::deflect(const QString &target) +{ + Q_D(VoiceCallHandler); + QDBusPendingCall call = d->interface->asyncCall("deflect", target); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); +} + +void VoiceCallHandler::sendDtmf(const QString &tones) +{ + Q_D(VoiceCallHandler); + QDBusPendingCall call = d->interface->asyncCall("sendDtmf", tones); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); +} + +void VoiceCallHandler::onPendingCallFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply<bool> reply = *watcher; + + if (reply.isError()) { + qWarning() << QString::fromLatin1("Received error reply for member: %1 (%2)").arg(reply.reply().member()).arg(reply.error().message()); + emit this->error(reply.error().message()); + watcher->deleteLater(); + } else { + qDebug() << QString::fromLatin1("Received successful reply for member: %1").arg(reply.reply().member()); + } +} diff --git a/daemon/voicecallhandler.h b/daemon/voicecallhandler.h new file mode 100644 index 0000000..678d8f8 --- /dev/null +++ b/daemon/voicecallhandler.h @@ -0,0 +1,90 @@ +#ifndef VOICECALLHANDLER_H +#define VOICECALLHANDLER_H + +#include <QObject> +#include <QDateTime> + +#include <QDBusPendingCallWatcher> + +class VoiceCallHandler : public QObject +{ + Q_OBJECT + + Q_ENUMS(VoiceCallStatus) + + Q_PROPERTY(QString handlerId READ handlerId CONSTANT) + Q_PROPERTY(QString providerId READ providerId CONSTANT) + Q_PROPERTY(int status READ status NOTIFY statusChanged) + Q_PROPERTY(QString statusText READ statusText NOTIFY statusChanged) + Q_PROPERTY(QString lineId READ lineId NOTIFY lineIdChanged) + Q_PROPERTY(QDateTime startedAt READ startedAt NOTIFY startedAtChanged) + Q_PROPERTY(int duration READ duration NOTIFY durationChanged) + Q_PROPERTY(bool isIncoming READ isIncoming CONSTANT) + Q_PROPERTY(bool isEmergency READ isEmergency NOTIFY emergencyChanged) + Q_PROPERTY(bool isMultiparty READ isMultiparty NOTIFY multipartyChanged) + Q_PROPERTY(bool isForwarded READ isForwarded NOTIFY forwardedChanged) + +public: + enum VoiceCallStatus { + STATUS_NULL, + STATUS_ACTIVE, + STATUS_HELD, + STATUS_DIALING, + STATUS_ALERTING, + STATUS_INCOMING, + STATUS_WAITING, + STATUS_DISCONNECTED + }; + + explicit VoiceCallHandler(const QString &handlerId, QObject *parent = 0); + ~VoiceCallHandler(); + + QString handlerId() const; + QString providerId() const; + int status() const; + QString statusText() const; + QString lineId() const; + QDateTime startedAt() const; + int duration() const; + bool isIncoming() const; + bool isMultiparty() const; + bool isEmergency() const; + bool isForwarded() const; + +Q_SIGNALS: + void error(const QString &error); + void statusChanged(); + void lineIdChanged(); + void durationChanged(); + void startedAtChanged(); + void emergencyChanged(); + void multipartyChanged(); + void forwardedChanged(); + +public Q_SLOTS: + void answer(); + void hangup(); + void hold(bool on); + void deflect(const QString &target); + void sendDtmf(const QString &tones); + +protected Q_SLOTS: + void initialize(bool notifyError = false); + + void onPendingCallFinished(QDBusPendingCallWatcher *watcher); + void onDurationChanged(); + void onStatusChanged(); + void onLineIdChanged(); + void onStartedAtChanged(); + void onEmergencyChanged(); + void onMultipartyChanged(); + void onForwardedChanged(); + +private: + class VoiceCallHandlerPrivate *d_ptr; + + Q_DISABLE_COPY(VoiceCallHandler) + Q_DECLARE_PRIVATE(VoiceCallHandler) +}; + +#endif // VOICECALLHANDLER_H diff --git a/daemon/voicecallmanager.cpp b/daemon/voicecallmanager.cpp new file mode 100644 index 0000000..79ab797 --- /dev/null +++ b/daemon/voicecallmanager.cpp @@ -0,0 +1,308 @@ +#include "voicecallmanager.h" + +#include <QDebug> +#include <QTimer> +#include <QDBusInterface> +#include <QDBusPendingReply> + +class VoiceCallManagerPrivate +{ + Q_DECLARE_PUBLIC(VoiceCallManager) + +public: + VoiceCallManagerPrivate(VoiceCallManager *q) + : q_ptr(q), + interface(NULL), + activeVoiceCall(NULL), + connected(false) + { /*...*/ } + + VoiceCallManager *q_ptr; + + QDBusInterface *interface; + + QList<VoiceCallHandler*> voicecalls; + QHash<QString,VoiceCallProviderData> providers; + + VoiceCallHandler* activeVoiceCall; + + bool connected; +}; + +VoiceCallManager::VoiceCallManager(QObject *parent) + : QObject(parent), d_ptr(new VoiceCallManagerPrivate(this)) +{ + Q_D(VoiceCallManager); + d->interface = new QDBusInterface("org.nemomobile.voicecall", + "/", + "org.nemomobile.voicecall.VoiceCallManager", + QDBusConnection::sessionBus(), + this); + + this->initialize(); +} + +VoiceCallManager::~VoiceCallManager() +{ + Q_D(VoiceCallManager); + delete d; +} + +void VoiceCallManager::initialize(bool notifyError) +{ + Q_D(VoiceCallManager); + bool success = false; + + if(d->interface->isValid()) + { + success = true; + success &= (bool)QObject::connect(d->interface, SIGNAL(error(QString)), SIGNAL(error(QString))); + success &= (bool)QObject::connect(d->interface, SIGNAL(voiceCallsChanged()), SLOT(onVoiceCallsChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(providersChanged()), SLOT(onProvidersChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(audioModeChanged()), SIGNAL(audioModeChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(audioRoutedChanged()), SIGNAL(audioRoutedChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(microphoneMutedChanged()), SIGNAL(microphoneMutedChanged())); + success &= (bool)QObject::connect(d->interface, SIGNAL(speakerMutedChanged()), SIGNAL(speakerMutedChanged())); + + onActiveVoiceCallChanged(); + onVoiceCallsChanged(); + } + + if(!(d->connected = success)) + { + QTimer::singleShot(2000, this, SLOT(initialize())); + if(notifyError) emit this->error("Failed to connect to VCM D-Bus service."); + } +} + +QDBusInterface* VoiceCallManager::interface() const +{ + Q_D(const VoiceCallManager); + return d->interface; +} + +VoiceCallHandlerList VoiceCallManager::voiceCalls() const +{ + Q_D(const VoiceCallManager); + return d->voicecalls; +} + +VoiceCallProviderHash VoiceCallManager::providers() const +{ + Q_D(const VoiceCallManager); + return d->providers; +} + +QString VoiceCallManager::defaultProviderId() const +{ + Q_D(const VoiceCallManager); + if(d->providers.count() == 0) { + qWarning() << Q_FUNC_INFO << "No provider added"; + return QString::null; + } + + QStringList keys = d->providers.keys(); + qSort(keys); + + VoiceCallProviderData provider = d->providers.value(keys.value(0)); + return provider.id; +} + +VoiceCallHandler* VoiceCallManager::activeVoiceCall() const +{ + Q_D(const VoiceCallManager); + return d->activeVoiceCall; +} + +QString VoiceCallManager::audioMode() const +{ + Q_D(const VoiceCallManager); + return d->interface->property("audioMode").toString(); +} + +bool VoiceCallManager::isAudioRouted() const +{ + Q_D(const VoiceCallManager); + return d->interface->property("isAudioRouted").toBool(); +} + +bool VoiceCallManager::isMicrophoneMuted() const +{ + Q_D(const VoiceCallManager); + return d->interface->property("isMicrophoneMuted").toBool(); +} + +bool VoiceCallManager::isSpeakerMuted() const +{ + Q_D(const VoiceCallManager); + return d->interface->property("isSpeakerMuted").toBool(); +} + +void VoiceCallManager::dial(const QString &provider, const QString &msisdn) +{ + Q_D(VoiceCallManager); + QDBusPendingCall call = d->interface->asyncCall("dial", provider, msisdn); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingCallFinished(QDBusPendingCallWatcher*))); +} + +void VoiceCallManager::silenceRingtone() +{ + Q_D(const VoiceCallManager); + QDBusPendingCall call = d->interface->asyncCall("silenceRingtone"); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onPendingSilenceFinished(QDBusPendingCallWatcher*))); +} + +/* + - Use of method calls instead of property setters to allow status checking. + */ +bool VoiceCallManager::setAudioMode(const QString &mode) +{ + Q_D(const VoiceCallManager); + QDBusPendingReply<bool> reply = d->interface->call("setAudioMode", mode); + return reply.isError() ? false : reply.value(); +} + +bool VoiceCallManager::setAudioRouted(bool on) +{ + Q_D(const VoiceCallManager); + QDBusPendingReply<bool> reply = d->interface->call("setAudioRouted", on); + return reply.isError() ? false : reply.value(); +} + +bool VoiceCallManager::setMuteMicrophone(bool on) +{ + Q_D(VoiceCallManager); + QDBusPendingReply<bool> reply = d->interface->call("setMuteMicrophone", on); + return reply.isError() ? false : reply.value(); +} + +bool VoiceCallManager::setMuteSpeaker(bool on) +{ + Q_D(VoiceCallManager); + QDBusPendingReply<bool> reply = d->interface->call("setMuteSpeaker", on); + return reply.isError() ? false : reply.value(); +} + +void VoiceCallManager::onVoiceCallsChanged() +{ + Q_D(VoiceCallManager); + QStringList nIds = d->interface->property("voiceCalls").toStringList(); + QStringList oIds; + + QStringList added; + QStringList removed; + + // Map current call handlers to handler ids for easy indexing. + foreach(VoiceCallHandler *handler, d->voicecalls) + { + oIds.append(handler->handlerId()); + } + + // Index new handlers to be added. + foreach(QString nId, nIds) + { + if(!oIds.contains(nId)) added.append(nId); + } + + // Index old handlers to be removed. + foreach(QString oId, oIds) + { + if(!nIds.contains(oId)) removed.append(oId); + } + + // Remove handlers that need to be removed. + foreach(QString removeId, removed) + { + for (int i = 0; i < d->voicecalls.count(); ++i) { + VoiceCallHandler *handler = d->voicecalls.at(i); + if(handler->handlerId() == removeId) + { + handler->disconnect(this); + d->voicecalls.removeAt(i); + handler->deleteLater(); + break; + } + } + } + + // Add handlers that need to be added. + foreach(QString addId, added) + { + VoiceCallHandler *handler = new VoiceCallHandler(addId, this); + d->voicecalls.append(handler); + } + + emit this->voiceCallsChanged(); +} + +void VoiceCallManager::onProvidersChanged() +{ + Q_D(VoiceCallManager); + d->providers.clear(); + foreach(QString provider, d->interface->property("providers").toStringList()) + { + QStringList parts = provider.split(':'); + d->providers.insert(parts.first(), VoiceCallProviderData(parts.first(), + parts.last(), + parts.first())); + } + + emit this->providersChanged(); +} + +void VoiceCallManager::onActiveVoiceCallChanged() +{ + Q_D(VoiceCallManager); + QString voiceCallId = d->interface->property("activeVoiceCall").toString(); + + if(d->voicecalls.count() == 0 || voiceCallId.isNull() || voiceCallId.isEmpty()) + { + d->activeVoiceCall = NULL; + } + else + { + bool found = false; + d->activeVoiceCall = NULL; + foreach(VoiceCallHandler* handler, d->voicecalls) + { + if(handler->handlerId() == voiceCallId) + { + d->activeVoiceCall = handler; + found = true; + } + if(!found) d->activeVoiceCall = NULL; + } + } + + emit this->activeVoiceCallChanged(); +} + +void VoiceCallManager::onPendingCallFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply<bool> reply = *watcher; + + if (reply.isError()) { + emit this->error(reply.error().message()); + } else { + qDebug() << QString("Received successful reply for member: ") + reply.reply().member(); + } + + watcher->deleteLater(); +} + +void VoiceCallManager::onPendingSilenceFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply<> reply = *watcher; + + if (reply.isError()) { + emit this->error(reply.error().message()); + } else { + qDebug() << QString("Received successful reply for member: ") + reply.reply().member(); + } + + watcher->deleteLater(); +} diff --git a/daemon/voicecallmanager.h b/daemon/voicecallmanager.h new file mode 100644 index 0000000..de18781 --- /dev/null +++ b/daemon/voicecallmanager.h @@ -0,0 +1,106 @@ +#ifndef VOICECALLMANAGER_H +#define VOICECALLMANAGER_H + +#include "voicecallhandler.h" + +#include <QObject> + +#include <QDBusInterface> +#include <QDBusPendingCallWatcher> + +class VoiceCallProviderData +{ +public: + VoiceCallProviderData() {/*..*/} + VoiceCallProviderData(const QString &pId, const QString &pType, const QString &pLabel) + : id(pId), type(pType), label(pLabel) {/*...*/} + + QString id; + QString type; + QString label; +}; + +typedef QHash<QString,VoiceCallProviderData> VoiceCallProviderHash; + +typedef QList<VoiceCallHandler*> VoiceCallHandlerList; + +class VoiceCallManager : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDBusInterface* interface READ interface) + + Q_PROPERTY(VoiceCallHandlerList voiceCalls READ voiceCalls NOTIFY voiceCallsChanged) + Q_PROPERTY(VoiceCallProviderHash providers READ providers NOTIFY providersChanged) + + Q_PROPERTY(QString defaultProviderId READ defaultProviderId NOTIFY defaultProviderChanged) + + Q_PROPERTY(VoiceCallHandler* activeVoiceCall READ activeVoiceCall NOTIFY activeVoiceCallChanged) + + Q_PROPERTY(QString audioMode READ audioMode WRITE setAudioMode NOTIFY audioModeChanged) + Q_PROPERTY(bool isAudioRouted READ isAudioRouted WRITE setAudioRouted NOTIFY audioRoutedChanged) + Q_PROPERTY(bool isMicrophoneMuted READ isMicrophoneMuted WRITE setMuteMicrophone NOTIFY microphoneMutedChanged) + Q_PROPERTY(bool isSpeakerMuted READ isSpeakerMuted WRITE setMuteSpeaker NOTIFY speakerMutedChanged) + +public: + explicit VoiceCallManager(QObject *parent = 0); + ~VoiceCallManager(); + + QDBusInterface* interface() const; + + VoiceCallHandlerList voiceCalls() const; + VoiceCallProviderHash providers() const; + + QString defaultProviderId() const; + + VoiceCallHandler* activeVoiceCall() const; + + QString audioMode() const; + bool isAudioRouted() const; + + bool isMicrophoneMuted() const; + bool isSpeakerMuted() const; + +Q_SIGNALS: + void error(const QString &message); + + void providersChanged(); + void voiceCallsChanged(); + + void defaultProviderChanged(); + + void activeVoiceCallChanged(); + + void audioModeChanged(); + void audioRoutedChanged(); + void microphoneMutedChanged(); + void speakerMutedChanged(); + +public Q_SLOTS: + void dial(const QString &providerId, const QString &msisdn); + + void silenceRingtone(); + + bool setAudioMode(const QString &mode); + bool setAudioRouted(bool on); + bool setMuteMicrophone(bool on = true); + bool setMuteSpeaker(bool on = true); + +protected Q_SLOTS: + void initialize(bool notifyError = false); + + void onProvidersChanged(); + void onVoiceCallsChanged(); + void onActiveVoiceCallChanged(); + + void onPendingCallFinished(QDBusPendingCallWatcher *watcher); + void onPendingSilenceFinished(QDBusPendingCallWatcher *watcher); + +private: + class VoiceCallManagerPrivate *d_ptr; + + Q_DISABLE_COPY(VoiceCallManager) + Q_DECLARE_PRIVATE(VoiceCallManager) +}; + +#endif // VOICECALLMANAGER_H |
