Commit 8d403351 authored by Albert Vaca Cintora's avatar Albert Vaca Cintora
Browse files

First working implementation of notifications plasmoid

Some fixes to libkdeconnect models
Added slot connected() to plugins
parent 60b3c3c1
......@@ -106,6 +106,10 @@ void Device::reloadPlugins()
m_plugins = newPluginMap;
Q_FOREACH(KdeConnectPlugin* plugin, m_plugins) {
plugin->connected();
}
Q_EMIT pluginsChanged();
}
......@@ -150,10 +154,13 @@ void Device::addLink(DeviceLink* link)
qSort(m_deviceLinks.begin(),m_deviceLinks.end(),lessThan);
if (m_deviceLinks.size() == 1) {
reloadPlugins();
reloadPlugins(); //Will load the plugins
Q_EMIT reachableStatusChanged();
} else {
Q_FOREACH(KdeConnectPlugin* plugin, m_plugins) {
plugin->connected();
}
}
}
void Device::linkDestroyed(QObject* o)
......
......@@ -88,6 +88,11 @@ Name=Ping
Comment=Ping received
Action=Popup
[Event/notification]
Name=Notification
Comment=Notification received
Action=Popup
[Event/unknownEvent]
Name=Unknown
Comment=Something unknown happened
......
......@@ -34,10 +34,13 @@ BatteryPlugin::BatteryPlugin(QObject *parent, const QVariantList &args)
, batteryDbusInterface(new BatteryDbusInterface(parent))
{
}
void BatteryPlugin::connected()
{
NetworkPackage np(PACKAGE_TYPE_BATTERY);
np.set("request",true);
device()->sendPackage(np);
}
BatteryPlugin::~BatteryPlugin()
......@@ -71,6 +74,7 @@ bool BatteryPlugin::receivePackage(const NetworkPackage& np)
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle(device()->name() + ": low battery");
notification->setText("Battery at 14%");
notification->sendEvent();
}
}
......
......@@ -40,6 +40,7 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected();
private:
BatteryDbusInterface* batteryDbusInterface;
......
......@@ -38,7 +38,8 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected() { }
private Q_SLOTS:
void clipboardChanged(QClipboard::Mode mode);
......
......@@ -35,6 +35,7 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected() { }
};
......
......@@ -44,6 +44,7 @@ public Q_SLOTS:
//Returns true if it has handled the package in some way
//device.sendPackage can be used to send an answer back to the device
virtual bool receivePackage(const NetworkPackage& np) = 0;
virtual void connected() = 0;
private:
Device* mDevice;
......
......@@ -39,6 +39,7 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected() { }
private Q_SLOTS:
void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
......
......@@ -25,14 +25,12 @@ install(TARGETS kdeconnect_notifications DESTINATION ${PLUGIN_INSTALL_DIR} )
install(FILES kdeconnect_notifications.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
include(../../../cmakemacros.txt)
generate_and_install_dbus_interface(
kdeconnect_notifications
notificationsdbusinterface.h
org.kde.kdeconnect.device.notifications.xml
OPTIONS -a
)
generate_and_install_dbus_interface(
kdeconnect_notifications
notification.h
......
......@@ -34,7 +34,7 @@ class Notification
Q_PROPERTY(QString internalId READ internalId)
Q_PROPERTY(QString appName READ appName)
Q_PROPERTY(QString ticker READ ticker)
Q_PROPERTY(QString dismissable READ dismissable)
Q_PROPERTY(bool dismissable READ dismissable)
public:
Notification(const NetworkPackage& np, QObject* parent);
......
......@@ -23,6 +23,9 @@
#include <QDebug>
#include <QDBusConnection>
#include <KNotification>
#include <KIcon>
NotificationsDbusInterface::NotificationsDbusInterface(Device* device, QObject *parent)
: QDBusAbstractAdaptor(parent)
, mDevice(device)
......@@ -68,6 +71,13 @@ void NotificationsDbusInterface::addNotification(Notification* noti)
QDBusConnection::sessionBus().registerObject(mDevice->dbusPath()+"/notifications/"+publicId, noti, QDBusConnection::ExportScriptableContents);
Q_EMIT notificationPosted(publicId);
KNotification* notification = new KNotification("notification");
notification->setPixmap(KIcon("preferences-desktop-notification").pixmap(48, 48));
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle(mDevice->name());
notification->setText(noti->appName() + ": " + noti->ticker());
notification->sendEvent();
}
void NotificationsDbusInterface::removeNotification(const QString& internalId)
......
......@@ -33,7 +33,10 @@ NotificationsPlugin::NotificationsPlugin(QObject* parent, const QVariantList& ar
: KdeConnectPlugin(parent, args)
{
notificationsDbusInterface = new NotificationsDbusInterface(device(), parent);
}
void NotificationsPlugin::connected()
{
NetworkPackage np(PACKAGE_TYPE_NOTIFICATION);
np.set("request",true);
device()->sendPackage(np);
......
......@@ -43,6 +43,7 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected();
private:
NotificationsDbusInterface* notificationsDbusInterface;
......
......@@ -37,7 +37,8 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected() { };
private:
enum PauseCondtions { PauseWhenTalking, PauseWhenRinging, NeverPause };
PauseCondtions pauseWhen;
......
......@@ -36,6 +36,7 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected() { };
};
......
......@@ -37,6 +37,7 @@ public:
public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np);
virtual void connected() { }
private:
KNotification* createNotification(const NetworkPackage& np);
......
......@@ -12,6 +12,7 @@ set(libkdeconnect_SRC
dbusinterfaces.cpp
devicesmodel.cpp
notificationsmodel.cpp
modeltest.cpp
)
set_source_files_properties(
......
......@@ -19,6 +19,7 @@
*/
#include "devicesmodel.h"
#include "modeltest.h"
#include <ksharedconfig.h>
#include <QDebug>
......@@ -34,6 +35,13 @@ DevicesModel::DevicesModel(QObject *parent)
, m_dbusInterface(new DaemonDbusInterface(this))
{
//new ModelTest(this, this);
connect(this, SIGNAL(rowsRemoved(QModelIndex, int, int)),
this, SIGNAL(rowsChanged()));
connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)),
this, SIGNAL(rowsChanged()));
connect(m_dbusInterface, SIGNAL(deviceAdded(QString)),
this, SLOT(deviceAdded(QString)));
connect(m_dbusInterface, SIGNAL(deviceVisibilityChanged(QString,bool)),
......@@ -43,6 +51,11 @@ DevicesModel::DevicesModel(QObject *parent)
refreshDeviceList();
//Role names for QML
QHash<int, QByteArray> names = roleNames();
names.insert(IdModelRole, "deviceId");
setRoleNames(names);
}
DevicesModel::~DevicesModel()
......@@ -70,6 +83,21 @@ void DevicesModel::deviceStatusChanged(const QString& id)
//Q_EMIT dataChanged(index(0),index(rowCount()));
refreshDeviceList();
}
DevicesModel::StatusFlags DevicesModel::displayFilter() const
{
return m_displayFilter;
}
void DevicesModel::setDisplayFilter(int flags)
{
setDisplayFilter((StatusFlags)flags);
}
void DevicesModel::setDisplayFilter(DevicesModel::StatusFlags flags)
{
m_displayFilter = flags;
refreshDeviceList();
}
void DevicesModel::refreshDeviceList()
{
......@@ -80,13 +108,29 @@ void DevicesModel::refreshDeviceList()
endRemoveRows();
}
QList<QString> deviceIds = m_dbusInterface->devices();
beginInsertRows(QModelIndex(), 0, deviceIds.size()-1);
Q_FOREACH(const QString& id, deviceIds) {
if (!m_dbusInterface->isValid()) {
return;
}
QDBusPendingReply<QStringList> deviceIds = m_dbusInterface->devices();
deviceIds.waitForFinished();
if (deviceIds.isError()) return;
Q_FOREACH(const QString& id, deviceIds.value()) {
DeviceDbusInterface* deviceDbusInterface = new DeviceDbusInterface(id,this);
bool onlyPaired = (m_displayFilter & StatusPaired);
if (onlyPaired && !deviceDbusInterface->paired()) continue;
bool onlyReachable = (m_displayFilter & StatusReachable);
if (onlyReachable && !deviceDbusInterface->reachable()) continue;
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_deviceList.append(deviceDbusInterface);
endInsertRows();
}
endInsertRows();
Q_EMIT dataChanged(index(0), index(deviceIds.count()));
......@@ -95,18 +139,8 @@ void DevicesModel::refreshDeviceList()
QVariant DevicesModel::data(const QModelIndex &index, int role) const
{
if (!m_dbusInterface->isValid()) {
switch (role) {
case IconModelRole:
return KIcon("dialog-close").pixmap(32, 32);
case NameModelRole:
return QString("KDED not running");
default:
return QVariant();
}
}
if (!index.isValid()
if (!m_dbusInterface->isValid()
|| !index.isValid()
|| index.row() < 0
|| index.row() >= m_deviceList.count()
|| !m_deviceList[index.row()]->isValid())
......@@ -159,7 +193,10 @@ DeviceDbusInterface* DevicesModel::getDevice(const QModelIndex& index)
int DevicesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
if(parent.isValid()) {
//Return size 0 if we are a child because this is not a tree
return 0;
}
return m_deviceList.count();
}
......
......@@ -33,6 +33,9 @@ class KDECONNECT_EXPORT DevicesModel
: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int displayFilter READ displayFilter WRITE setDisplayFilter)
Q_PROPERTY(int count READ rowCount NOTIFY rowsChanged)
public:
enum ModelRoles {
NameModelRole = Qt::DisplayRole,
......@@ -49,6 +52,10 @@ public:
DevicesModel(QObject *parent = 0);
virtual ~DevicesModel();
void setDisplayFilter(StatusFlags flags);
void setDisplayFilter(int flags);
StatusFlags displayFilter() const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
......@@ -62,9 +69,14 @@ private Q_SLOTS:
void deviceRemoved(const QString& id);
void refreshDeviceList();
Q_SIGNALS:
void rowsChanged();
private:
DaemonDbusInterface* m_dbusInterface;
QList<DeviceDbusInterface*> m_deviceList;
StatusFlags m_displayFilter;
};
#endif // DEVICESMODEL_H
/****************************************************************************
**
** Copyright (C) 2007 Trolltech ASA. All rights reserved.
**
** This file is part of the Qt Concurrent project on Trolltech Labs.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
#include <QtGui/QtGui>
#include "modeltest.h"
Q_DECLARE_METATYPE(QModelIndex)
/*!
Connect to all of the models signals. Whenever anything happens recheck everything.
*/
ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false)
{
Q_ASSERT(model);
connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests()));
connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests()));
connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
this, SLOT(runAllTests()));
// Special checks for inserting/removing
connect(model, SIGNAL(layoutAboutToBeChanged()),
this, SLOT(layoutAboutToBeChanged()));
connect(model, SIGNAL(layoutChanged()),
this, SLOT(layoutChanged()));
connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int)));
connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int)));
connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
this, SLOT(rowsInserted(const QModelIndex &, int, int)));
connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
this, SLOT(rowsRemoved(const QModelIndex &, int, int)));
runAllTests();
}
void ModelTest::runAllTests()
{
if (fetchingMore)
return;
nonDestructiveBasicTest();
rowCount();
columnCount();
hasIndex();
index();
parent();
data();
}
/*!
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
to make sure the model doesn't outright segfault, testing the functions that makes sense.
*/
void ModelTest::nonDestructiveBasicTest()
{
Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex());
model->canFetchMore(QModelIndex());
Q_ASSERT(model->columnCount(QModelIndex()) >= 0);
Q_ASSERT(model->data(QModelIndex()) == QVariant());
fetchingMore = true;
model->fetchMore(QModelIndex());
fetchingMore = false;
Qt::ItemFlags flags = model->flags(QModelIndex());
Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0);
model->hasChildren(QModelIndex());
model->hasIndex(0, 0);
model->headerData(0, Qt::Horizontal);
model->index(0, 0);
Q_ASSERT(model->index(-1, -1) == QModelIndex());
model->itemData(QModelIndex());
QVariant cache;
model->match(QModelIndex(), -1, cache);
model->mimeTypes();
Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
Q_ASSERT(model->rowCount() >= 0);
QVariant variant;
model->setData(QModelIndex(), variant, -1);
model->setHeaderData(-1, Qt::Horizontal, QVariant());
model->setHeaderData(0, Qt::Horizontal, QVariant());
model->setHeaderData(999999, Qt::Horizontal, QVariant());
QMap<int, QVariant> roles;
model->sibling(0, 0, QModelIndex());
model->span(QModelIndex());
model->supportedDropActions();
}
/*!
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
Models that are dynamically populated are not as fully tested here.
*/
void ModelTest::rowCount()
{
// check top row
QModelIndex topIndex = model->index(0, 0, QModelIndex());
int rows = model->rowCount(topIndex);
Q_ASSERT(rows >= 0);
if (rows > 0)
Q_ASSERT(model->hasChildren(topIndex) == true);
QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
if (secondLevelIndex.isValid()) { // not the top level
// check a row count where parent is valid
rows = model->rowCount(secondLevelIndex);
Q_ASSERT(rows >= 0);
if (rows > 0)
Q_ASSERT(model->hasChildren(secondLevelIndex) == true);
}
// The models rowCount() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
*/
void ModelTest::columnCount()
{
// check top row
QModelIndex topIndex = model->index(0, 0, QModelIndex());
Q_ASSERT(model->columnCount(topIndex) >= 0);
// check a column count where parent is valid
QModelIndex childIndex = model->index(0, 0, topIndex);
if (childIndex.isValid())
Q_ASSERT(model->columnCount(childIndex) >= 0);
// columnCount() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::hasIndex()
*/
void ModelTest::hasIndex()
{
// Make sure that invalid values returns an invalid index
Q_ASSERT(model->hasIndex(-2, -2) == false);
Q_ASSERT(model->hasIndex(-2, 0) == false);
Q_ASSERT(model->hasIndex(0, -2) == false);
int rows = model->rowCount();
int columns = model->columnCount();
// check out of bounds
Q_ASSERT(model->hasIndex(rows, columns) == false);
Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false);
if (rows > 0)
Q_ASSERT(model->hasIndex(0, 0) == true);
// hasIndex() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::index()
*/
void ModelTest::index()
{
// Make sure that invalid values returns an invalid index
Q_ASSERT(model->index(-2, -2) == QModelIndex());
Q_ASSERT(model->index(-2, 0) == QModelIndex());
Q_ASSERT(model->index(0, -2) == QModelIndex());
int rows = model->rowCount();
int columns = model->columnCount();
if (rows == 0)
return;
// Catch off by one errors
Q_ASSERT(model->index(rows, columns) == QModelIndex());
Q_ASSERT(model->index(0, 0).isValid() == true);
// Make sure that the same index is *always* returned
QModelIndex a = model->index(0, 0);
QModelIndex b = model->index(0, 0);
Q_ASSERT(a == b);
// index() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::parent()
*/
void ModelTest::parent()
{
// Make sure the model wont crash and will return an invalid QModelIndex
// when asked for the parent of an invalid index.
Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
if (model->rowCount() == 0)
return;