...
 
Commits (56)
......@@ -23,7 +23,7 @@ if (SAILFISHOS)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
else()
set(KF5_MIN_VERSION "5.42.0")
set(QT_MIN_VERSION "5.7.0")
set(QT_MIN_VERSION "5.10.0")
set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service)
set(KF5_OPTIONAL_COMPONENTS DocTools)
if(UNIX)
......@@ -31,7 +31,9 @@ else()
endif()
set(QCA_MIN_VERSION "2.1.0")
find_package(Qca-qt5 ${QCA_MIN_VERSION} REQUIRED)
find_package(KF5PulseAudioQt)
if(NOT WIN32 AND NOT APPLE)
find_package(KF5PulseAudioQt REQUIRED)
endif()
add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS)
endif()
......
......@@ -36,6 +36,7 @@ set(kdeconnectcore_SRCS
daemon.cpp
device.cpp
core_debug.cpp
notificationserverinfo.cpp
)
add_library(kdeconnectcore ${kdeconnectcore_SRCS})
......
......@@ -43,11 +43,11 @@ public:
BluetoothPairingHandler(DeviceLink* deviceLink);
virtual ~BluetoothPairingHandler() { }
virtual void packetReceived(const NetworkPacket& np) Q_DECL_OVERRIDE;
virtual bool requestPairing() Q_DECL_OVERRIDE;
virtual bool acceptPairing() Q_DECL_OVERRIDE;
virtual void rejectPairing() Q_DECL_OVERRIDE;
virtual void unpair() Q_DECL_OVERRIDE;
void packetReceived(const NetworkPacket& np) override;
bool requestPairing() override;
bool acceptPairing() override;
void rejectPairing() override;
void unpair() override;
bool isPairRequested() const { return m_status == Requested; }
bool isPaired() const { return m_status == Paired; }
......
......@@ -88,7 +88,6 @@ void LanLinkProvider::onStart()
bool success = m_udpSocket.bind(bindAddress, UDP_PORT, QUdpSocket::ShareAddress);
Q_ASSERT(success);
qCDebug(KDECONNECT_CORE) << "onStart";
m_tcpPort = MIN_TCP_PORT;
while (!m_server->listen(bindAddress, m_tcpPort)) {
......@@ -101,13 +100,14 @@ void LanLinkProvider::onStart()
}
onNetworkChange();
qCDebug(KDECONNECT_CORE) << "LanLinkProvider started";
}
void LanLinkProvider::onStop()
{
qCDebug(KDECONNECT_CORE) << "onStop";
m_udpSocket.close();
m_server->close();
qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped";
}
void LanLinkProvider::onNetworkChange()
......@@ -217,11 +217,12 @@ void LanLinkProvider::udpBroadcastReceived()
}
}
void LanLinkProvider::connectError()
void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError)
{
QSslSocket* socket = qobject_cast<QSslSocket*>(sender());
if (!socket) return;
qCDebug(KDECONNECT_CORE) << "Socket error" << socketError;
qCDebug(KDECONNECT_CORE) << "Fallback (1), try reverse connection (send udp packet)" << socket->errorString();
NetworkPacket np(QLatin1String(""));
NetworkPacket::createIdentityPacket(&np);
......
......@@ -63,7 +63,7 @@ public Q_SLOTS:
void onStop() override;
void tcpSocketConnected();
void encrypted();
void connectError();
void connectError(QAbstractSocket::SocketError socketError);
private Q_SLOTS:
void udpBroadcastReceived();
......
......@@ -29,6 +29,7 @@
#include "core_debug.h"
#include "kdeconnectconfig.h"
#include "networkpacket.h"
#include "notificationserverinfo.h"
#ifdef KDECONNECT_BLUETOOTH
#include "backends/bluetooth/bluetoothlinkprovider.h"
......@@ -54,6 +55,7 @@ struct DaemonPrivate
QMap<QString, Device*> m_devices;
QSet<QString> m_discoveryModeAcquisitions;
bool m_testMode;
};
Daemon* Daemon::instance()
......@@ -68,10 +70,18 @@ Daemon::Daemon(QObject* parent, bool testMode)
{
Q_ASSERT(!s_instance);
s_instance = this;
qCDebug(KDECONNECT_CORE) << "KdeConnect daemon starting";
d->m_testMode = testMode;
// HACK init may call pure virtual functions from this class so it can't be called directly from the ctor
QTimer::singleShot(0, this, &Daemon::init);
}
void Daemon::init()
{
qCDebug(KDECONNECT_CORE) << "Daemon starting";
//Load backends
if (testMode)
if (d->m_testMode)
d->m_linkProviders.insert(new LoopbackLinkProvider());
else {
d->m_linkProviders.insert(new LanLinkProvider());
......@@ -101,7 +111,9 @@ Daemon::Daemon(QObject* parent, bool testMode)
QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kdeconnect"));
QDBusConnection::sessionBus().registerObject(QStringLiteral("/modules/kdeconnect"), this, QDBusConnection::ExportScriptableContents);
qCDebug(KDECONNECT_CORE) << "KdeConnect daemon started";
NotificationServerInfo::instance().init();
qCDebug(KDECONNECT_CORE) << "Daemon started";
}
void Daemon::acquireDiscoveryMode(const QString& key)
......@@ -151,7 +163,7 @@ void Daemon::cleanDevices()
void Daemon::forceOnNetworkChange()
{
qCDebug(KDECONNECT_CORE) << "Sending onNetworkChange to " << d->m_linkProviders.size() << " LinkProviders";
qCDebug(KDECONNECT_CORE) << "Sending onNetworkChange to" << d->m_linkProviders.size() << "LinkProviders";
for (LinkProvider* a : qAsConst(d->m_linkProviders)) {
a->onNetworkChange();
}
......
......@@ -51,6 +51,7 @@ public:
virtual void askPairingConfirmation(Device* device) = 0;
virtual void reportError(const QString& title, const QString& description) = 0;
virtual void quit() = 0;
virtual QNetworkAccessManager* networkAccessManager();
Device* getDevice(const QString& deviceId);
......@@ -89,6 +90,9 @@ private Q_SLOTS:
void onNewDeviceLink(const NetworkPacket& identityPacket, DeviceLink* dl);
void onDeviceStatusChanged();
private:
void init();
protected:
void addDevice(Device* device);
bool isDiscoveringDevices() const;
......
......@@ -67,6 +67,7 @@ public:
QMultiMap<QString, KdeConnectPlugin *> m_pluginsByIncomingCapability;
QSet<QString> m_supportedPlugins;
QSet<QString> m_allPlugins;
QSet<PairingHandler *> m_pairRequests;
};
......@@ -89,7 +90,8 @@ Device::Device(QObject* parent, const QString& id)
QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors);
//Assume every plugin is supported until addLink is called and we can get the actual list
d->m_supportedPlugins = PluginLoader::instance()->getPluginList().toSet();
d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet();
d->m_supportedPlugins = d->m_allPlugins;
connect(this, &Device::pairingError, this, &warn);
}
......@@ -99,6 +101,8 @@ Device::Device(QObject* parent, const NetworkPacket& identityPacket, DeviceLink*
, d(new Device::DevicePrivate(identityPacket.get<QString>(QStringLiteral("deviceId"))))
{
d->m_deviceName = identityPacket.get<QString>(QStringLiteral("deviceName"));
d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet();
addLink(identityPacket, dl);
//Register in bus
......@@ -509,6 +513,10 @@ KdeConnectPlugin* Device::plugin(const QString& pluginName) const
void Device::setPluginEnabled(const QString& pluginName, bool enabled)
{
if (!d->m_allPlugins.contains(pluginName)) {
return;
}
KConfigGroup pluginStates = KSharedConfig::openConfig(pluginsConfigFile())->group("Plugins");
const QString enabledKey = pluginName + QStringLiteral("Enabled");
......
......@@ -82,6 +82,7 @@ void FileTransferJob::startTransfer()
return;
setProcessedAmount(Bytes, 0);
setTotalAmount(Files, 1);
Q_EMIT description(this, i18n("Receiving file over KDE Connect"),
{ i18nc("File transfer origin", "From"), m_from },
{ i18nc("File transfer destination", "To"), m_destination.toLocalFile() });
......@@ -125,7 +126,7 @@ void FileTransferJob::transferFinished()
//TODO: MD5-check the file
if (m_size == m_written) {
qCDebug(KDECONNECT_CORE) << "Finished transfer" << m_destination;
setProcessedAmount(Files, 1);
emitResult();
} else {
qCDebug(KDECONNECT_CORE) << "Received incomplete file ("<< m_written << "/" << m_size << "bytes ), deleting";
......
......@@ -38,6 +38,8 @@
#include "dbushelper.h"
#include "daemon.h"
const QFile::Permissions strictPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser;
struct KdeConnectConfigPrivate {
// The Initializer object sets things up, and also does cleanup when it goes out of scope
......@@ -69,7 +71,6 @@ KdeConnectConfig::KdeConnectConfig()
i18n("Could not find support for RSA in your QCA installation. If your "
"distribution provides separate packets for QCA-ossl and QCA-gnupg, "
"make sure you have them installed and try again."));
return;
}
//Make sure base directory exists
......@@ -207,24 +208,25 @@ void KdeConnectConfig::loadPrivateKey()
{
QString keyPath = privateKeyPath();
QFile privKey(keyPath);
if (privKey.exists() && privKey.open(QIODevice::ReadOnly)) {
d->m_privateKey = QCA::PrivateKey::fromPEM(privKey.readAll());
bool needsToGenerateKey = false;
if (privKey.exists() && privKey.open(QIODevice::ReadOnly)) {
QCA::ConvertResult result;
d->m_privateKey = QCA::PrivateKey::fromPEM(privKey.readAll(), QCA::SecureArray(), &result);
if (result != QCA::ConvertResult::ConvertGood) {
qCWarning(KDECONNECT_CORE) << "Private key from" << keyPath << "is not valid";
needsToGenerateKey = true;
}
} else {
needsToGenerateKey = true;
}
d->m_privateKey = QCA::KeyGenerator().createRSA(2048);
if (!privKey.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store private key file: %1", keyPath));
} else {
privKey.setPermissions(strict);
privKey.write(d->m_privateKey.toPEM().toLatin1());
}
if (needsToGenerateKey) {
generatePrivateKey(keyPath);
}
//Extra security check
if (QFile::permissions(keyPath) != strict) {
if (QFile::permissions(keyPath) != strictPermissions) {
qCWarning(KDECONNECT_CORE) << "Warning: KDE Connect private key file has too open permissions " << keyPath;
}
}
......@@ -233,41 +235,95 @@ void KdeConnectConfig::loadCertificate()
{
QString certPath = certificatePath();
QFile cert(certPath);
bool needsToGenerateCert = false;
if (cert.exists() && cert.open(QIODevice::ReadOnly)) {
auto loadedCerts = QSslCertificate::fromPath(certPath);
if (loadedCerts.empty()) {
qCWarning(KDECONNECT_CORE) << "Certificate from" << certPath << "is not valid";
needsToGenerateCert = true;
} else {
d->m_certificate = loadedCerts.at(0);
}
} else {
needsToGenerateCert = true;
}
d->m_certificate = QSslCertificate::fromPath(certPath).at(0);
if (needsToGenerateCert) {
generateCertificate(certPath);
}
//Extra security check
if (QFile::permissions(certPath) != strictPermissions) {
qCWarning(KDECONNECT_CORE) << "Warning: KDE Connect certificate file has too open permissions " << certPath;
}
}
void KdeConnectConfig::generatePrivateKey(const QString& keyPath)
{
qCDebug(KDECONNECT_CORE) << "Generating private key";
bool error = false;
d->m_privateKey = QCA::KeyGenerator().createRSA(2048);
QFile privKey(keyPath);
if (!privKey.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
error = true;
} else {
privKey.setPermissions(strictPermissions);
int written = privKey.write(d->m_privateKey.toPEM().toLatin1());
if (written <= 0) {
error = true;
}
}
if (error) {
Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store private key file: %1", keyPath));
}
// No certificate yet. Probably first run. Let's generate one!
}
QString uuid = QUuid::createUuid().toString();
DbusHelper::filterNonExportableCharacters(uuid);
qCDebug(KDECONNECT_CORE) << "My id:" << uuid;
void KdeConnectConfig::generateCertificate(const QString& certPath)
{
qCDebug(KDECONNECT_CORE) << "Generating certificate";
// FIXME: We only use QCA here to generate the cert and key, would be nice to get rid of it completely.
// The same thing we are doing with QCA could be done invoking openssl (although it's potentially less portable):
// openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes -keyout privateKey.pem -days 3650 -out certificate.pem -subj "/O=KDE/OU=KDE Connect/CN=_e6e29ad4_2b31_4b6d_8f7a_9872dbaa9095_"
bool error = false;
QCA::CertificateOptions certificateOptions = QCA::CertificateOptions();
QDateTime startTime = QDateTime::currentDateTime().addYears(-1);
QDateTime endTime = startTime.addYears(10);
QCA::CertificateInfo certificateInfo;
certificateInfo.insert(QCA::CommonName, uuid);
certificateInfo.insert(QCA::Organization,QStringLiteral("KDE"));
certificateInfo.insert(QCA::OrganizationalUnit,QStringLiteral("Kde connect"));
certificateOptions.setInfo(certificateInfo);
certificateOptions.setFormat(QCA::PKCS10);
certificateOptions.setSerialNumber(QCA::BigInteger(10));
certificateOptions.setValidityPeriod(startTime, endTime);
QString uuid = QUuid::createUuid().toString();
DbusHelper::filterNonExportableCharacters(uuid);
qCDebug(KDECONNECT_CORE) << "My id:" << uuid;
d->m_certificate = QSslCertificate(QCA::Certificate(certificateOptions, d->m_privateKey).toPEM().toLatin1());
// FIXME: We only use QCA here to generate the cert and key, would be nice to get rid of it completely.
// The same thing we are doing with QCA could be done invoking openssl (although it's potentially less portable):
// openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes -keyout privateKey.pem -days 3650 -out certificate.pem -subj "/O=KDE/OU=KDE Connect/CN=_e6e29ad4_2b31_4b6d_8f7a_9872dbaa9095_"
if (!cert.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store certificate file: %1", certPath));
} else {
cert.setPermissions(strict);
cert.write(d->m_certificate.toPem());
QCA::CertificateOptions certificateOptions = QCA::CertificateOptions();
QDateTime startTime = QDateTime::currentDateTime().addYears(-1);
QDateTime endTime = startTime.addYears(10);
QCA::CertificateInfo certificateInfo;
certificateInfo.insert(QCA::CommonName, uuid);
certificateInfo.insert(QCA::Organization,QStringLiteral("KDE"));
certificateInfo.insert(QCA::OrganizationalUnit,QStringLiteral("Kde connect"));
certificateOptions.setInfo(certificateInfo);
certificateOptions.setFormat(QCA::PKCS10);
certificateOptions.setSerialNumber(QCA::BigInteger(10));
certificateOptions.setValidityPeriod(startTime, endTime);
d->m_certificate = QSslCertificate(QCA::Certificate(certificateOptions, d->m_privateKey).toPEM().toLatin1());
QFile cert(certPath);
if (!cert.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
error = true;
} else {
cert.setPermissions(strictPermissions);
int written = cert.write(d->m_certificate.toPem());
if (written <= 0) {
error = true;
}
}
if (error) {
Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store certificate file: %1", certPath));
}
}
......@@ -74,12 +74,11 @@ private:
KdeConnectConfig();
void loadPrivateKey();
void generatePrivateKey(const QString& path);
void loadCertificate();
void generateCertificate(const QString& path);
struct KdeConnectConfigPrivate* d;
const QFile::Permissions strict = QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser;
};
#endif
/**
* Copyright 2019 Nicolas Fella <nicolas.fella@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "notificationserverinfo.h"
#include <QDBusMessage>
#include <QDBusPendingReply>
#include <QDBusPendingCallWatcher>
#include <QDBusConnection>
#include "core_debug.h"
NotificationServerInfo& NotificationServerInfo::instance()
{
static NotificationServerInfo instance;
return instance;
}
void NotificationServerInfo::init()
{
QDBusMessage query = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("GetCapabilities"));
QDBusPendingReply<QStringList> reply = QDBusConnection::sessionBus().asyncCall(query);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, reply, watcher] {
watcher->deleteLater();
if (reply.isError()) {
qCWarning(KDECONNECT_CORE) << "Could not query capabilities from notifications server";
return;
}
if (reply.value().contains(QLatin1String("x-kde-display-appname"))) {
m_supportedHints |= X_KDE_DISPLAY_APPNAME;
}
if (reply.value().contains(QLatin1String("x-kde-origin-name"))) {
m_supportedHints |= X_KDE_ORIGIN_NAME;
}
});
}
NotificationServerInfo::Hints NotificationServerInfo::supportedHints()
{
return m_supportedHints;
}
/**
* Copyright 2019 Nicolas Fella <nicolas.fella@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "kdeconnectcore_export.h"
#include <QObject>
class KDECONNECTCORE_EXPORT NotificationServerInfo
: public QObject
{
Q_OBJECT
public:
enum Hint {
X_KDE_DISPLAY_APPNAME = 1,
X_KDE_ORIGIN_NAME = 2
};
Q_DECLARE_FLAGS(Hints, Hint)
static NotificationServerInfo& instance();
void init();
Hints supportedHints();
private:
Hints m_supportedHints;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(NotificationServerInfo::Hints)
......@@ -8,10 +8,11 @@ target_link_libraries(kdeconnectd kdeconnectcore KF5::KIOWidgets KF5::DBusAddons
ecm_mark_nongui_executable(kdeconnectd)
configure_file(kdeconnectd.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdeconnectd.desktop)
configure_file(org.kde.kdeconnect.daemon.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kdeconnect.daemon.desktop)
configure_file(org.kde.kdeconnect.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kdeconnect.service)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kdeconnectd.desktop DESTINATION ${AUTOSTART_INSTALL_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kdeconnect.daemon.desktop DESTINATION ${AUTOSTART_INSTALL_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kdeconnect.daemon.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kdeconnect.service DESTINATION ${DBUS_SERVICES_INSTALL_DIR})
install(TARGETS kdeconnectd DESTINATION ${LIBEXEC_INSTALL_DIR})
[Desktop Entry]
Type=Service
Icon=preferences-system-power-management
X-KDE-ServiceTypes=KDEDModule
X-KDE-Library=kdeconnect
X-DBUS-ServiceName=org.kde.kdeconnect
X-KDE-DBus-ModuleName=kdeconnect
X-KDE-Kded-autoload=true
X-KDE-Kded-load-on-demand=false
X-KDE-Kded-phase=1
Name=KDE Connect
Name[ar]=كدي المتّصل
Name[bg]=KDE Connect
Name[bs]=Konekcija KDE
Name[ca]=KDE Connect
Name[ca@valencia]=KDE Connect
Name[cs]=KDE Connect
Name[da]=KDE Connect
Name[de]=KDE-Connect
Name[el]=KDE Connect
Name[en_GB]=KDE Connect
Name[es]=KDE Connect
Name[et]=KDE Connect
Name[eu]=KDE Connect
Name[fi]=KDE Connect
Name[fr]=KDE Connect
Name[gl]=KDE Connect
Name[he]=KDE Connect
Name[hu]=KDE csatlakozás
Name[id]=KDE Connect
Name[it]=KDE Connect
Name[ko]=KDE Connect
Name[nl]=KDE Connect
Name[nn]=KDE Connect
Name[pl]=KDE Connect
Name[pt]=KDE Connect
Name[pt_BR]=KDE Connect
Name[ro]=KDE Connect
Name[ru]=KDE Connect
Name[sk]=KDE Connect
Name[sr]=КДЕ‑конекција
Name[sr@ijekavian]=КДЕ‑конекција
Name[sr@ijekavianlatin]=KDE‑konekcija
Name[sr@latin]=KDE‑konekcija
Name[sv]=KDE-anslut
Name[tr]=KDE Connect
Name[uk]=З’єднання KDE
Name[x-test]=xxKDE Connectxx
Name[zh_CN]=KDE Connect
Name[zh_TW]=KDE 連線
Comment=Connect KDE with your smartphone
Comment[ar]=أوصل كدي بهاتفك الذّكيّ
Comment[bg]=Свържете КДЕ с вашия смартфон
Comment[bs]=Konektujte se na KDE sa Vašim mobitelom
Comment[ca]=Connecta el KDE amb el vostre telèfon intel·ligent
Comment[ca@valencia]=Connecta el KDE amb el vostre telèfon intel·ligent
Comment[cs]=Propojte KDE s vaším telefonem
Comment[da]=Forbind KDE med din smartphone
Comment[de]=KDE mit Ihren Smartphone verbinden
Comment[el]=Σύνδεση του KDE με το έξυπνο τηλέφωνό σας
Comment[en_GB]=Connect KDE with your smartphone
Comment[es]=Conecte KDE con su teléfono móvil
Comment[et]=KDE ühendamine oma nutitelefoniga
Comment[eu]=Konektatu KDE zure mugikorrarekin
Comment[fi]=Yhdistä KDE älypuhelimeesi
Comment[fr]=Connectez KDE avec votre smartphone
Comment[gl]=Conectar KDE co seu teléfono móbil.
Comment[he]=חבר את KDE לפלאפון החכם שלך
Comment[hu]=A KDE csatlakoztatása az okostelefonnal
Comment[id]=Koneksikan KDE dengan smartphone-mu
Comment[it]=Connette KDE al tuo smartphone
Comment[ko]=KDE와 스마트폰 연결
Comment[nl]=KDE met uw smartphone verbinden
Comment[nn]=Kopla KDE til smarttelefonen din
Comment[pl]=Podłącz swój smartfon do KDE
Comment[pt]=Ligue o KDE ao seu telemóvel
Comment[pt_BR]=Conecte o KDE ao seu celular
Comment[ru]=Подключайте смартфон к KDE
Comment[sk]=Prepojiť KDE s vašim smartfónom
Comment[sr]=Повежите КДЕ са својим паметним телефоном
Comment[sr@ijekavian]=Повежите КДЕ са својим паметним телефоном
Comment[sr@ijekavianlatin]=Povežite KDE sa svojim pametnim telefonom
Comment[sr@latin]=Povežite KDE sa svojim pametnim telefonom
Comment[sv]=Anslut KDE till din smartphone
Comment[tr]=KDE'yi akıllı telefonunuz ile bağlayın
Comment[uk]=З’єднання KDE з вашим смартфоном
Comment[x-test]=xxConnect KDE with your smartphonexx
Comment[zh_CN]=用您的智能手机连接到 KDE
Comment[zh_TW]=將您的智慧型手機與KDE連線
......@@ -21,6 +21,11 @@
#include <QApplication>
#include <QNetworkAccessManager>
#include <QTimer>
#include <QLoggingCategory>
#include <QCommandLineOption>
#include <QCommandLineParser>
#include <QDBusMessage>
#include <QDBusConnection>
#include <KAboutData>
#include <KDBusService>
......@@ -33,6 +38,9 @@
#include "core/backends/pairinghandler.h"
#include "kdeconnect-version.h"
Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_DAEMON)
Q_LOGGING_CATEGORY(KDECONNECT_DAEMON, "kdeconnect.daemon")
class DesktopDaemon : public Daemon
{
Q_OBJECT
......@@ -58,6 +66,7 @@ public:
void reportError(const QString & title, const QString & description) override
{
qCWarning(KDECONNECT_DAEMON) << title << ":" << description;
KNotification::event(KNotification::Error, title, description);
}
......@@ -79,6 +88,9 @@ public:
notification->sendEvent();
}
void quit() override {
QApplication::quit();
}
private:
QNetworkAccessManager* m_nam;
......@@ -88,7 +100,7 @@ int main(int argc, char* argv[])
{
QApplication app(argc, argv);
KAboutData aboutData(
QStringLiteral("kdeconnectd"),
QStringLiteral("org.kde.kdeconnect.daemon"),
i18n("KDE Connect Daemon"),
QStringLiteral(KDECONNECT_VERSION_STRING),
i18n("KDE Connect Daemon"),
......@@ -97,6 +109,21 @@ int main(int argc, char* argv[])
KAboutData::setApplicationData(aboutData);
app.setQuitOnLastWindowClosed(false);
QCommandLineParser parser;
QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance"));
parser.addOption(replaceOption);
aboutData.setupCommandLine(&parser);
parser.process(app);
aboutData.processCommandLine(&parser);
if (parser.isSet(replaceOption)) {
auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnectd"),
QStringLiteral("/MainApplication"),
QStringLiteral("org.qtproject.Qt.QCoreApplication"),
QStringLiteral("quit"));
QDBusConnection::sessionBus().call(message); //deliberately block until it's done, so we register the name after the app quits
}
KDBusService dbusService(KDBusService::Unique);
DesktopDaemon daemon;
......
[Desktop Entry]
Type=Application
Exec=${KDE_INSTALL_FULL_LIBEXECDIR}/kdeconnectd
X-KDE-StartupNotify=false
X-KDE-autostart-phase=1
X-GNOME-Autostart-enabled=true
OnlyShowIn=KDE;GNOME;Unity;XFCE;
NoDisplay=true
Name=KDEConnect daemon
Name[ar]=عفريت KDEConnect
Name[bg]=Услуга KDE Connect
Name[ca]=Dimoni del KDEConnect
Name[ca@valencia]=Dimoni del KDEConnect
Name[cs]=Démon KDE Connect
Name[da]=KDEConnect-dæmon
Name[de]=KDE-Connect-Dienst
Name[el]=Δαίμονας του KDEConnect
Name[en_GB]=KDEConnect daemon
Name[es]=Demonio de KDE Connect
Name[et]=KDEConnecti deemon
Name[eu]=KDEConnect daimona
Name[fi]=KDEConnect-taustapalvelu
Name[fr]=Démon KDE Connect
Name[gl]=Servizo de KDE Connect
Name[he]=דמון KDEConnect
Name[hu]=KDEConnect szolgáltatás
Name[id]=Daemon KDEConnect
Name[it]=Demone KDE Connect
Name[ko]=KDE Connect 데몬
Name[nl]=KDEConnect-daemon
Name[nn]=KDEConnect-teneste
Name[pl]=Usługa KDEConnect
Name[pt]=Serviço do KDE Connect
Name[pt_BR]=Serviço do KDE Connect
Name[ru]=Служба KDE Connect
Name[sk]=KDEConnect démon
Name[sr]=КДЕ‑конекцијин демон
Name[sr@ijekavian]=КДЕ‑конекцијин демон
Name[sr@ijekavianlatin]=KDE‑konekcijin demon
Name[sr@latin]=KDE‑konekcijin demon
Name[sv]=Demon för KDE-anslut
Name[tr]=KDEConnect süreci
Name[uk]=Фонова служба KDEConnect
Name[x-test]=xxKDEConnect daemonxx
Name[zh_CN]=KDEConnect 守护进程
Name[zh_TW]=KDE連線作業
[Desktop Entry]
Type=Application
Exec=${KDE_INSTALL_FULL_LIBEXECDIR}/kdeconnectd
X-KDE-StartupNotify=false
X-KDE-autostart-phase=1
X-GNOME-Autostart-enabled=true
OnlyShowIn=KDE;GNOME;Unity;XFCE;
NoDisplay=true
Icon=kdeconnect
Name=KDE Connect
Name[ar]=كدي المتّصل
Name[bg]=KDE Connect
Name[bs]=Konekcija KDE
Name[ca]=KDE Connect
Name[ca@valencia]=KDE Connect
Name[cs]=KDE Connect
Name[da]=KDE Connect
Name[de]=KDE-Connect
Name[el]=KDE Connect
Name[en_GB]=KDE Connect
Name[es]=KDE Connect
Name[et]=KDE Connect
Name[eu]=KDE Connect
Name[fi]=KDE Connect
Name[fr]=KDE Connect
Name[gl]=KDE Connect
Name[he]=KDE Connect
Name[hu]=KDE csatlakozás
Name[id]=KDE Connect
Name[it]=KDE Connect
Name[ko]=KDE Connect
Name[nl]=KDE Connect
Name[nn]=KDE Connect
Name[pl]=KDE Connect
Name[pt]=KDE Connect
Name[pt_BR]=KDE Connect
Name[ro]=KDE Connect
Name[ru]=KDE Connect
Name[sk]=KDE Connect
Name[sr]=КДЕ‑конекција
Name[sr@ijekavian]=КДЕ‑конекција
Name[sr@ijekavianlatin]=KDE‑konekcija
Name[sr@latin]=KDE‑konekcija
Name[sv]=KDE-anslut
Name[tr]=KDE Connect
Name[uk]=З’єднання KDE
Name[x-test]=xxKDE Connectxx
Name[zh_CN]=KDE Connect
Name[zh_TW]=KDE 連線
......@@ -16,6 +16,7 @@ Name[fi]=Lähetä tiedosto KDE Connectilla
Name[fr]=Envoyer via KDE Connect
Name[gl]=Enviar mediante KDE Connect
Name[it]=Invia tramite KDE Connect
Name[ko]=KDE Connect로 통해 보내기
Name[nl]=Via KDE Connect versturen
Name[nn]=Send via KDE Connect
Name[pl]=Wyślij przez KDE Connect
......
......@@ -4,12 +4,14 @@ Name=Send file via KDE Connect service
Name[ca]=Envia el fitxer a través del servei KDE Connect
Name[ca@valencia]=Envia el fitxer a través del servei KDE Connect
Name[cs]=Poslat soubor přes službu KDE Connect
Name[de]=Datei mit KDE-Connect -Dienst versenden
Name[en_GB]=Send file via KDE Connect service
Name[es]=Enviar archivo a través del servicio KDE Connect
Name[fi]=Lähetä tiedosto KDE Connect -palvelulla
Name[fr]=Envoyer un fichier via le service KDE Connect
Name[gl]=Enviar o ficheiro mediante o servizo de KDE Connect
Name[it]=Invia il file tramite il servizio KDE Connect
Name[ko]=KDE Connect 서비스로 파일 보내기
Name[nl]=Bestand via KDE Connect-service versturen
Name[nn]=Send fil med KDE Connect-tenesta
Name[pt]=Enviar o ficheiro pelo serviço do KDE Connect
......@@ -17,6 +19,7 @@ Name[pt_BR]=Enviar arquivo via serviço do KDE Connect
Name[sv]=Skicka fil via KDE:s anslutningstjänst
Name[uk]=Надіслати файл за допомогою служби KDE Connect
Name[x-test]=xxSend file via KDE Connect servicexx
Name[zh_CN]=通过 KDE Connect 服务发送文件
Name[zh_TW]=使用 KDE Connect 服務傳送檔案
X-KDE-Library=kdeconnectfileitemaction
X-KDE-Submenu=Connect
......@@ -53,6 +56,7 @@ X-KDE-Submenu[sv]=Anslut
X-KDE-Submenu[tr]=Bağlan
X-KDE-Submenu[uk]=З’єднання
X-KDE-Submenu[x-test]=xxConnectxx
X-KDE-Submenu[zh_CN]=连接
X-KDE-Submenu[zh_TW]=連線
Icon=preferences-system-network
......
......@@ -27,20 +27,7 @@ DevicesSortProxyModel::DevicesSortProxyModel(DevicesModel* devicesModel)
: QSortFilterProxyModel(devicesModel)
{
setSourceModel(devicesModel);
}
void DevicesSortProxyModel::setSourceModel(QAbstractItemModel* devicesModel)
{
QSortFilterProxyModel::setSourceModel(devicesModel);
if (devicesModel) {
setSortRole(DevicesModel::StatusModelRole);
connect(devicesModel, &QAbstractItemModel::dataChanged, this, &DevicesSortProxyModel::sourceDataChanged);
}
sort(0);
}
void DevicesSortProxyModel::sourceDataChanged()
{
setSortRole(DevicesModel::StatusModelRole);
sort(0);
}
......
......@@ -33,10 +33,6 @@ public:
explicit DevicesSortProxyModel(DevicesModel* devicesModel = nullptr);
bool lessThan(const QModelIndex& left, const QModelIndex& right) const override;
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
void setSourceModel(QAbstractItemModel* sourceModel) override;
public Q_SLOTS:
void sourceDataChanged();
};
#endif // DEVICESSORTPROXYMODEL_H
......@@ -195,7 +195,7 @@ QVariant NotificationsModel::data(const QModelIndex& index, int role) const
case AppNameModelRole:
return notification->appName();
case DbusInterfaceRole:
return qVariantFromValue<QObject*>(notification);
return QVariant::fromValue<QObject*>(notification);
case DismissableModelRole:
return notification->dismissable();
case RepliableModelRole:
......
......@@ -107,6 +107,7 @@ void KioKdeconnect::listAllDevices()
const QString icon = QStringLiteral("kdeconnect");
KIO::UDSEntry entry;
entry.reserve(6);
entry.insert(KIO::UDSEntry::UDS_NAME, name);
entry.insert(KIO::UDSEntry::UDS_ICON_NAME, icon);
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
......@@ -118,6 +119,7 @@ void KioKdeconnect::listAllDevices()
// We also need a non-null and writable UDSentry for "."
KIO::UDSEntry entry;
entry.reserve(4);
entry.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.insert(KIO::UDSEntry::UDS_SIZE, 0);
......@@ -192,6 +194,7 @@ void KioKdeconnect::listDevice(const QString& device)
const QString icon = QStringLiteral("folder");
KIO::UDSEntry entry;
entry.reserve(6);
entry.insert(KIO::UDSEntry::UDS_NAME, name);
entry.insert(KIO::UDSEntry::UDS_ICON_NAME, icon);
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
......@@ -203,6 +206,7 @@ void KioKdeconnect::listDevice(const QString& device)
// We also need a non-null and writable UDSentry for "."
KIO::UDSEntry entry;
entry.reserve(4);
entry.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.insert(KIO::UDSEntry::UDS_SIZE, 0);
......@@ -246,7 +250,14 @@ void KioKdeconnect::stat(const QUrl& url)
QString currentDevice = url.host();
if (!currentDevice.isEmpty()) {
SftpDbusInterface interface(currentDevice);
entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, interface.mountPoint());
if (interface.isValid()) {
entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, interface.mountPoint());
if (!interface.isMounted()) {
interface.mount();
}
}
}
statEntry(entry);
......
<?xml version="1.0" encoding="utf-8"?>
<component type="desktop-application">
<id>org.kde.kdeconnect</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0+</project_license>
<name>KDE Connect</name>
<name xml:lang="ca">KDE Connect</name>
<name xml:lang="ca-valencia">KDE Connect</name>
<name xml:lang="en-GB">KDE Connect</name>
<name xml:lang="nl">KDE Connect</name>
<name xml:lang="pt">KDE Connect</name>
<name xml:lang="pt-BR">KDE Connect</name>
<name xml:lang="sk">KDE Connect</name>
<name xml:lang="sv">KDE-anslut</name>
<name xml:lang="uk">KDE Connect</name>
<name xml:lang="x-test">xxKDE Connectxx</name>
<summary>Seamless connection of your devices</summary>
<summary xml:lang="ca">Connexió transparent amb els vostres dispositius</summary>
<summary xml:lang="ca-valencia">Connexió transparent amb els vostres dispositius</summary>
<summary xml:lang="en-GB">Seamless connection of your devices</summary>
<summary xml:lang="fr">Connecter vos périphériques avec facilité</summary>
<summary xml:lang="nl">Naadloze verbinding met uw apparaten</summary>
<summary xml:lang="pt">Ligação transparente aos seus dispositivos</summary>
<summary xml:lang="pt-BR">Conexão transparente com seus dispositivos</summary>
<summary xml:lang="sk">Jednoduché prepojenie vašich zariadení</summary>
<summary xml:lang="sv">Sömlös anslutning av dina apparater</summary>
<summary xml:lang="uk">Зручне з'єднання із вашими пристроями</summary>
<summary xml:lang="x-test">xxSeamless connection of your devicesxx</summary>
<description>
<p>KDE Connect provides several features to integrate your phone and your computer:
</p>
<p xml:lang="ca">El KDE Connect proporciona diverses característiques per integrar el telèfon i l'ordinador:</p>
<p xml:lang="ca-valencia">El KDE Connect proporciona diverses característiques per integrar el telèfon i l'ordinador:</p>
<p xml:lang="en-GB">KDE Connect provides several features to integrate your phone and your computer:</p>
<p xml:lang="nl">KDE Connect biedt verschillende functis om uw telefoon en uw computer te integreren:</p>
<p xml:lang="pt">O KDE Connect oferece diversas funcionalidades para integrar o seu telefone com o seu computador:</p>
<p xml:lang="pt-BR">O KDE Connect oferece diversos recursos para integrar seu telefone com seu computador:</p>
<p xml:lang="sk">KDE Connect poskytuje niekoľko funkcií na integráciu vášho telefónu s vašim počítačom.</p>
<p xml:lang="sv">KDE-anslut tillhandahåller integrering mellan en Android-telefon och skrivbordet:</p>
<p xml:lang="uk">У KDE Connect реалізовано декілька можливостей з інтеграції вашого телефону і комп'ютера:</p>
<p xml:lang="x-test">xxKDE Connect provides several features to integrate your phone and your computer:xx</p>
<p>Please note you will need to install KDE Connect on your
computer for this app to work, and keep the desktop version
up-to-date with the Android version for the latest features to
work.
</p>
<p xml:lang="ca">Recordeu que cal instal·lar el KDE Connect a l'ordinador perquè aquesta aplicació funcioni, i mantenir actualitzada la versió de l'escriptori amb la versió d'Android perquè funcionin les últimes característiques.</p>
<p xml:lang="ca-valencia">Recordeu que cal instal·lar el KDE Connect a l'ordinador perquè aquesta aplicació funcione, i mantindre actualitzada la versió de l'escriptori amb la versió d'Android perquè funcionen les últimes característiques.</p>
<p xml:lang="en-GB">Please note you will need to install KDE Connect on your computer for this app to work, and keep the desktop version up-to-date with the Android version for the latest features to work.</p>
<p xml:lang="nl">Merk op dat het nodig is om KDE Connect op uw computer te installeren om deze app te laten werken en de bureaubladversie up-to-date te houden met de Android-versie om de laatste functies te laten werken.</p>
<p xml:lang="pt">Lembre-se que terá de instalar o KDE Connect no seu computador para esta aplicação funcionar, e deve manter a versão do mesmo actualizada com a versão para Android, para que as últimas funcionalidades funcionem bem.</p>
<p xml:lang="pt-BR">Lembre-se de que é necessário instalar o KDE Connect no seu computador para este aplicativo funcionar e também manter a versão atualizada com a versão para Android, de forma que os recursos mais recentes funcionem bem.</p>
<p xml:lang="sk">Pamätajte, že si musíte nainštalovať KDE Connect, aby fungovala táto aplikácia a udržiavať verziu pracovného prostredia aktuálnu s verziou Androidu, aby fungovali najnovšie funkcie.</p>
<p xml:lang="sv">Observera att KDE-anslut måste installeras på datorn för att appen ska fungera, och att skrivbordsdatorns version måste hållas uppdaterad tillsammans med Androidversionen för att de senaste funktionerna ska fungera.</p>
<p xml:lang="uk">Будь ласка, зауважте, що для того, щоб ця програма працювала, вам слід встановити KDE Connect на ваш комп'ютер і підтримувати синхронізацію версій із Android, щоб можна було скористатися найновішими можливостями.</p>
<p xml:lang="x-test">xxPlease note you will need to install KDE Connect on your computer for this app to work, and keep the desktop version up-to-date with the Android version for the latest features to work.xx</p>
</description>
<url type="homepage">https://community.kde.org/KDEConnect</url>
<url type="bugtracker">https://bugs.kde.org/enter_bug.cgi?format=guided&amp;product=kdeconnect</url>
<url type="help">https://docs.kde.org/index.php?application=kdeconnect-kde&amp;language=en</url>
<screenshots>
<screenshot type="default">
<image default="true">https://cdn.kde.org/screenshots/kdeconnect/plasmoid.png</image>
<image>https://cdn.kde.org/screenshots/kdeconnect/kcm.png</image>
<image>https://cdn.kde.org/screenshots/kdeconnect/gnome3.png</image>
</screenshot>
</screenshots>
<project_group>KDE</project_group>
</component>
[Desktop Entry]
Type=Application
Icon=kdeconnect
Terminal=false
Name=KDE Connect
Name[ar]=كدي المتّصل
Name[bg]=KDE Connect
Name[bs]=Konekcija KDE
Name[ca]=KDE Connect
Name[ca@valencia]=KDE Connect
Name[cs]=KDE Connect
Name[da]=KDE Connect
Name[de]=KDE-Connect
Name[el]=KDE Connect
Name[en_GB]=KDE Connect
Name[es]=KDE Connect
Name[et]=KDE Connect
Name[eu]=KDE Connect
Name[fi]=KDE Connect
Name[fr]=KDE Connect
Name[gl]=KDE Connect
Name[he]=KDE Connect
Name[hu]=KDE csatlakozás
Name[id]=KDE Connect
Name[it]=KDE Connect
Name[ko]=KDE Connect
Name[nl]=KDE Connect
Name[nn]=KDE Connect
Name[pl]=KDE Connect
Name[pt]=KDE Connect
Name[pt_BR]=KDE Connect
Name[ro]=KDE Connect
Name[ru]=KDE Connect
Name[sk]=KDE Connect
Name[sr]=КДЕ‑конекција
Name[sr@ijekavian]=КДЕ‑конекција
Name[sr@ijekavianlatin]=KDE‑konekcija
Name[sr@latin]=KDE‑konekcija
Name[sv]=KDE-anslut
Name[tr]=KDE Connect
Name[uk]=З’єднання KDE
Name[x-test]=xxKDE Connectxx
Name[zh_CN]=KDE Connect
Name[zh_TW]=KDE 連線
GenericName=Connect and sync your devices
GenericName[ar]=اتّصل وزامن أجهزتك
GenericName[ca]=Connecta i sincronitza els vostres dispositius
GenericName[ca@valencia]=Connecta i sincronitza els vostres dispositius
GenericName[cs]=Připojte a synchronizujte svá zařízení
GenericName[da]=Forbind og synkronisér dine enheder
GenericName[de]=Verbinden und Abgleichen Ihrer Geräte
GenericName[el]=Σύνδεση και συγχρονισμός των συσκευών σας
GenericName[en_GB]=Connect and sync your devices
GenericName[es]=Conecte y sincronice sus dispositivos
GenericName[et]=Oma seadmete ühendamine ja sünkroonimine
GenericName[eu]=Konektatu eta sinkronizatu zure gailuak
GenericName[fi]=Yhdistä ja synkronoi laitteitasi
GenericName[fr]=Connectez et synchronisez vos périphériques
GenericName[gl]=Conecte e sincronice os seus dispositivos
GenericName[he]=Connect and sync your devices
GenericName[hu]=Csatlakoztassa és szinkronizálja eszközeit
GenericName[id]=Koneksikan dan sinkronkan perangkatmu
GenericName[it]=Connetti e sincronizza i tuoi dispositivi
GenericName[ko]=내 장치에 연결하고 동기화
GenericName[nl]=Uw apparaten verbinden en synchroniseren
GenericName[nn]=Kopla til og synkroniser einingar
GenericName[pl]=Podłącz i zsynchronizuj swoje urządzenia
GenericName[pt]=Ligue e sincronize os seus dispositivos
GenericName[pt_BR]=Conecta e sincroniza seus dispositivos
GenericName[ru]=Подключение и синхронизация с мобильными устройствами
GenericName[sk]=Pripojiť a synchronizovať vaše zariadenia
GenericName[sr]=Повежите и синхронизујте своје уређаје
GenericName[sr@ijekavian]=Повежите и синхронизујте своје уређаје
GenericName[sr@ijekavianlatin]=Povežite i sinhronizujte svoje uređaje
GenericName[sr@latin]=Povežite i sinhronizujte svoje uređaje
GenericName[sv]=Anslut och synkronisera dina apparater
GenericName[tr]=Aygıtlarınıza bağlanın ve eşitleyin
GenericName[uk]=З’єднання і синхронізація ваших пристроїв
GenericName[x-test]=xxConnect and sync your devicesxx
GenericName[zh_CN]=连接并同步您的设备
GenericName[zh_TW]=連線並且同步您的裝置
Categories=Qt;KDE;System;
......@@ -7,6 +7,9 @@ add_subdirectory(battery)
add_subdirectory(sendnotifications)
add_subdirectory(mpriscontrol)
add_subdirectory(photo)
if(NOT APPLE)
add_subdirectory(systemvolume)
endif()
if(NOT SAILFISHOS)
add_subdirectory(clipboard)
......@@ -19,12 +22,13 @@ if(NOT SAILFISHOS)
add_subdirectory(mousepad)
add_subdirectory(sms)
add_subdirectory(runcommand)
if(NOT WIN32)
if(NOT WIN32 AND NOT APPLE)
add_subdirectory(screensaver-inhibit)
add_subdirectory(sftp)
add_subdirectory(pausemusic)
endif()
if(Qt5Multimedia_FOUND)
if(Qt5Multimedia_FOUND AND KF5PulseAudioQt_FOUND)
add_subdirectory(findthisdevice)
endif()
endif()
......@@ -37,12 +41,6 @@ if(SAILFISHOS OR EXPERIMENTALAPP_ENABLED)
add_subdirectory(remotesystemvolume)
endif()
if(KF5PulseAudioQt_FOUND AND NOT WIN32)
add_subdirectory(pausemusic)
endif()
if(KF5PulseAudioQt_FOUND OR WIN32)
add_subdirectory(systemvolume)
endif()
# If we split notifications per plugin, in several notifyrc files, they won't
# appear in the same group in the Notifications KCM
install(FILES kdeconnect.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
......@@ -50,15 +50,19 @@ ContactsPlugin::ContactsPlugin(QObject* parent, const QVariantList& args) :
// Create the storage directory if it doesn't exist
if (!QDir().mkpath(vcardsPath)) {
qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" << "Unable to create VCard directory";
qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "Unable to create VCard directory";
}
synchronizeRemoteWithLocal();
qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Contacts constructor for device " << device()->name();
}
bool ContactsPlugin::receivePacket(const NetworkPacket& np) {
void ContactsPlugin::connected()
{
synchronizeRemoteWithLocal();
}
bool ContactsPlugin::receivePacket(const NetworkPacket& np)
{
//qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Packet Received for device " << device()->name();
//qCDebug(KDECONNECT_PLUGIN_CONTACTS) << np.body();
......@@ -68,17 +72,19 @@ bool ContactsPlugin::receivePacket(const NetworkPacket& np) {
return handleResponseVCards(np);
} else {
// Is this check necessary?
qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Unknown package type received from device: "
qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Unknown packet type received from device: "
<< device()->name() << ". Maybe you need to upgrade KDE Connect?";
return false;
}
}
void ContactsPlugin::synchronizeRemoteWithLocal() {
void ContactsPlugin::synchronizeRemoteWithLocal()
{
sendRequest(PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMP);
}
bool ContactsPlugin::handleResponseUIDsTimestamps(const NetworkPacket& np) {
bool ContactsPlugin::handleResponseUIDsTimestamps(const NetworkPacket& np)
{
if (!np.has("uids")) {
qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseUIDsTimestamps:"
<< "Malformed packet does not have uids key";
......@@ -150,7 +156,8 @@ bool ContactsPlugin::handleResponseUIDsTimestamps(const NetworkPacket& np) {
return true;
}
bool ContactsPlugin::handleResponseVCards(const NetworkPacket& np) {
bool ContactsPlugin::handleResponseVCards(const NetworkPacket& np)
{
if (!np.has("uids")) {
qCDebug(KDECONNECT_PLUGIN_CONTACTS)
<< "handleResponseVCards:" << "Malformed packet does not have uids key";
......@@ -180,7 +187,8 @@ bool ContactsPlugin::handleResponseVCards(const NetworkPacket& np) {
return true;
}
bool ContactsPlugin::sendRequest(const QString& packetType) {
bool ContactsPlugin::sendRequest(const QString& packetType)
{
NetworkPacket np(packetType);
bool success = sendPacket(np);
qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "sendRequest: Sending " << packetType << success;
......@@ -188,7 +196,8 @@ bool ContactsPlugin::sendRequest(const QString& packetType) {
return success;
}
bool ContactsPlugin::sendRequestWithIDs(const QString& packetType, const uIDList_t& uIDs) {
bool ContactsPlugin::sendRequestWithIDs(const QString& packetType, const uIDList_t& uIDs)
{
NetworkPacket np(packetType);
np.set<uIDList_t>("uids", uIDs);
......@@ -196,7 +205,8 @@ bool ContactsPlugin::sendRequestWithIDs(const QString& packetType, const uIDList
return success;
}
QString ContactsPlugin::dbusPath() const {
QString ContactsPlugin::dbusPath() const
{
return "/modules/kdeconnect/devices/" + device()->id() + "/contacts";
}
......
......@@ -85,7 +85,9 @@ Q_DECLARE_METATYPE(uID)
typedef QStringList uIDList_t;
Q_DECLARE_METATYPE(uIDList_t)
class Q_DECL_EXPORT ContactsPlugin : public KdeConnectPlugin {
class Q_DECL_EXPORT ContactsPlugin
: public KdeConnectPlugin
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.contacts")
......@@ -93,8 +95,7 @@ public:
explicit ContactsPlugin(QObject* parent, const QVariantList& args);
bool receivePacket(const NetworkPacket& np) override;
void connected() override {
}
void connected() override;
QString dbusPath () const override;
......
......@@ -13,6 +13,11 @@ target_link_libraries(kdeconnect_findthisdevice
Qt5::DBus
)
if (NOT WIN32)
target_link_libraries(kdeconnect_findthisdevice
KF5::PulseAudioQt
)
endif()
set(kdeconnect_findthisdevice_config_SRCS findthisdevice_config.cpp)
ki18n_wrap_ui(kdeconnect_findthisdevice_config_SRCS findthisdevice_config.ui)
......
......@@ -22,6 +22,11 @@
// KF
#include <KPluginFactory>
#ifndef Q_OS_WIN
#include <PulseAudioQt/Context>
#include <PulseAudioQt/Sink>
#endif
// Qt
#include <QDBusConnection>
#include <QStandardPaths>
......@@ -91,8 +96,26 @@ bool FindThisDevicePlugin::receivePacket(const NetworkPacket& np)
player->setMedia(soundURL);
player->setVolume(100);
player->play();
connect(player, &QMediaPlayer::stateChanged, player, &QObject::deleteLater);
// TODO: by-pass volume settings in case it is muted
#ifndef Q_OS_WIN
const auto sinks = PulseAudioQt::Context::instance()->sinks();
QVector<PulseAudioQt::Sink*> mutedSinks;
for (auto sink : sinks) {
if (sink->isMuted()) {
sink->setMuted(false);
mutedSinks.append(sink);
}
}
connect(player, &QMediaPlayer::stateChanged, this, [player, mutedSinks]{
player->deleteLater();
for (auto sink : qAsConst(mutedSinks)) {
sink->setMuted(true);
}
});
#endif
// TODO: ensure to use built-in loudspeakers
return true;
......
......@@ -61,7 +61,7 @@ Comment[he]=התראות מההתקן שלך
Comment[hu]=Az eszközökről származó értesítések
Comment[id]=Notifikasi dari perangkatmu
Comment[it]=Notifiche dai tuoi dispositivi
Comment[ko]=장치에 표시된 알림
Comment[ko]=장치에 알림
Comment[nl]=Meldingen van uw apparaten
Comment[nn]=Varslingar frå einingane dine
Comment[pl]=Powiadomienia z urządzeń
......@@ -103,7 +103,7 @@ Name[he]=בקשת התאמה
Name[hu]=Párosítási kérés
Name[id]=Minta Sandingkan
Name[it]=Richiesta di associazione
Name[ko]=연결 요청
Name[ko]=페어링 요청
Name[nl]=Verzoek om een paar te maken
Name[nn]=Paringsførespurnad
Name[pl]=Żądanie parowania
......@@ -139,7 +139,7 @@ Comment[gl]=Recibiuse unha solicitude de emparellamento dun dispositivo.
Comment[he]=התקבלה בקשת התאמה מהתקן
Comment[id]=Diterima permintaan sandingan dari sebuah perangkat
Comment[it]=Richiesta di associazione ricevuta da un dispositivo
Comment[ko]=장치에서 연결 요청을 받음
Comment[ko]=장치에서 수신된 페어링 요청
Comment[nl]=Verzoek om een paar te maken ontvangen van apparaten
Comment[nn]=Fekk paringsførespurnad frå eining
Comment[pl]=Otrzymano żądanie parowania z urządzenia
......@@ -220,7 +220,7 @@ Comment[he]=מישהו מתקשר אילך
Comment[hu]=Valaki hívja önt
Comment[id]=Seseorang memanggilmu
Comment[it]=Chiamata in arrivo
Comment[ko]=누군가가 전화를 걸었음
Comment[ko]=누군가 전화를 걸었습니다
Comment[nl]=Iemand belt u op
Comment[nn]=Nokon ringjer deg
Comment[pl]=Ktoś do ciebie dzwoni
......@@ -262,7 +262,7 @@ Name[he]=שיחה שלא נענתה
Name[hu]=Nem fogadott hívás
Name[id]=Panggilan Tidak Terjawab
Name[it]=Chiamata persa
Name[ko]=부재 중 전화
Name[ko]=부재중 전화
Name[nl]=Gemiste oproep
Name[nn]=Tapt oppringing
Name[pl]=Połączenie nieodebrane
......@@ -301,7 +301,7 @@ Comment[he]=יש לך שיחה שלא נענטה
Comment[hu]=Nem fogadott hívása van
Comment[id]=Kamu memiliki panggilan tak terjawab
Comment[it]=Hai una chiamata persa
Comment[ko]=부재 중 전화가 있음
Comment[ko]=부재중 전화가 있습니다
Comment[nl]=U hebt een gemiste oproep
Comment[nn]=Du har ei tapt oppringing
Comment[pl]=Nie odebrałeś połączenia
......@@ -343,7 +343,7 @@ Name[he]=התקבלה הודעת SMS
Name[hu]=SMS érkezett
Name[id]=Diterima SMS
Name[it]=SMS ricevuto
Name[ko]=SMS 받음
Name[ko]=SMS 수신
Name[nl]=SMS ontvangen
Name[nn]=SMS motteken
Name[pl]=Otrzymano SMSa
......@@ -382,7 +382,7 @@ Comment[he]=מישהו שלח לך SMS
Comment[hu]=Valaki SMS-t küldött önnek
Comment[id]=Seseorang memangirimmu sebuah SMS
Comment[it]=Hai ricevuto un SMS
Comment[ko]=누군가가 문자 메시지를 보냄
Comment[ko]=누군가 SMS를 보냈습니다
Comment[nl]=Iemand heeft u een SMS gestuurd
Comment[nn]=Nokon sende deg ein SMS
Comment[pl]=Ktoś do ciebie wysłał SMSa
......@@ -463,7 +463,7 @@ Comment[he]=הבטריה שלך הולכת להגמר
Comment[hu]=Az akkumulátora feszültsége alacsony
Comment[id]=Bateraimu dalam keadaan lemah
Comment[it]=La batteria è al livello basso
Comment[ko]=배터리가 부족
Comment[ko]=배터리가 부족한 상태입니다
Comment[nl]=Uw batterij is bijna leeg
Comment[nn]=Det er lite batteri att
Comment[pl]=Twoja bateria ma niski poziom naładowania
......@@ -505,7 +505,7 @@ Name[he]=התקבל פינג
Name[hu]=Ping érkezett
Name[id]=Diterima Ping
Name[it]=Ping ricevuto
Name[ko]=핑 받음
Name[ko]=핑 수신
Name[nl]=Ping ontvangen
Name[nn]=Pingsignal motteke
Name[pl]=Otrzymano ping
......@@ -544,7 +544,7 @@ Comment[he]=התקבל פינג
Comment[hu]=Ping érkezett
Comment[id]=Diterima ping
Comment[it]=Hai ricevuto un ping
Comment[ko]=핑 받음
Comment[ko]=핑 수신
Comment[nl]=Ping ontvangen
Comment[nn]=Pingsignal motteke
Comment[pl]=Otrzymano ping
......@@ -625,7 +625,7 @@ Comment[he]=התראה התקבלה
Comment[hu]=Értesítés érkezett
Comment[id]=Diterima notifikasi
Comment[it]=Hai ricevuto una notifica