diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,5 @@ +include: + - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-before.yml + - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-extragear-linux.yml + - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-extragear-freebsd.yml + - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-extragear-windows.yml diff --git a/.gitlab/merge_request_templates/Bugfix.md b/.gitlab/merge_request_templates/Bugfix.md new file mode 100644 --- /dev/null +++ b/.gitlab/merge_request_templates/Bugfix.md @@ -0,0 +1,32 @@ +## Summary + +Add a description of your merge request here. What does your new feature do? + +Describe in detail what your patch does, why it does that, etc. Merge requests +without an adequate description are difficult to review, and probably we will +ask for more information! + +Please also keep this description up-to-date with any discussion that takes +place so that reviewers can understand your intent. This is especially +important if they didn't participate in the discussion. + +Make sure to remove this comment when you are done. + +Fill in the following lines as appropriate to automatically close GitLab issue or Bugzilla bugs +Fixes +BUG: + +## Test Plan + +### Before: +Add a quick discription of the (buggy) behavior of the app before this fix +This section does not need to be too detailed because it should mostly be +covered by the bug report and the summary. Just share the steps for how to +reproduce the bug. + +### After: +Add a more detailed description of how to exercise the new behavior, showing +that the bug has been fixed. If any other behavior has been changed, share +the steps to verify that the new behavior doesn't have any regressions. + +/label ~bugfix diff --git a/.gitlab/merge_request_templates/Feature.md b/.gitlab/merge_request_templates/Feature.md new file mode 100644 --- /dev/null +++ b/.gitlab/merge_request_templates/Feature.md @@ -0,0 +1,27 @@ +## Summary + +Add a description of your merge request here. What does your new feature do? + +Describe in detail what your patch does, why it does that, etc. Merge requests +without an adequate description are difficult to review, and probably we will +ask for more information! + +Please also keep this description up-to-date with any discussion that takes +place so that reviewers can understand your intent. This is especially +important if they didn't participate in the discussion. + +Make sure to remove this comment when you are done. + +Implements + +## Test Plan + +Add a description of how to test your patch here. Tell us how to use the new +feature and what we should be seeing. If applicable, it is great to include +screenshots, either here or in the Summary section. + +It can be difficult to understand a new feature from the text description in +the summary, so put enough detail here that so that we can understand how to run +the new feature and we can play with it ourselves to understand it. + +/label ~feature diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,10 +24,10 @@ else() set(KF5_MIN_VERSION "5.42.0") set(QT_MIN_VERSION "5.7.0") - set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils) + set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service) set(KF5_OPTIONAL_COMPONENTS DocTools) if(UNIX) - set(KF5_OPTIONAL_COMPONENTS ${KF5_OPTIONAL_COMPONENTS} PulseAudioQt) + set(KF5_OPTIONAL_COMPONENTS ${KF5_OPTIONAL_COMPONENTS} PulseAudioQt Runner) endif() set(QCA_MIN_VERSION "2.1.0") find_package(Qca-qt5 ${QCA_MIN_VERSION} REQUIRED) @@ -50,6 +50,13 @@ PURPOSE "Required for Find My Device plugin" ) +set_package_properties(KF5Kirigami2 PROPERTIES + DESCRIPTION "QtQuick plugins to build user interfaces based on KDE UX guidelines" + PURPOSE "Required for KDE Connect's QML-based GUI applications" + URL "https://www.kde.org/products/kirigami/" + TYPE RUNTIME +) + include_directories(${CMAKE_SOURCE_DIR}) configure_file(kdeconnect-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/kdeconnect-version.h) @@ -86,18 +93,16 @@ option(EXPERIMENTALAPP_ENABLED OFF) if(EXPERIMENTALAPP_ENABLED) find_package(KF5Kirigami2) - set_package_properties(KF5Kirigami2 PROPERTIES - DESCRIPTION "QtQuick plugins to build user interfaces based on KDE UX guidelines" - PURPOSE "Required for kcapp" - URL "https://www.kde.org/products/kirigami/" - TYPE RUNTIME -) add_subdirectory(app) endif() add_subdirectory(plugins) add_subdirectory(cli) add_subdirectory(declarativeplugin) +if(KF5Runner_FOUND) + add_subdirectory(runners) +endif() + if (NOT SAILFISHOS) add_subdirectory(indicator) add_subdirectory(urlhandler) @@ -108,6 +113,7 @@ option(SMSAPP_ENABLED OFF) if(SMSAPP_ENABLED) + find_package(KF5Kirigami2) find_package(KF5People REQUIRED) find_package(KF5PeopleVCard) set_package_properties(KF5PeopleVCard PROPERTIES diff --git a/app/main.cpp b/app/main.cpp --- a/app/main.cpp +++ b/app/main.cpp @@ -21,10 +21,11 @@ #include #include #include +#include + #include #include #include -#include int main(int argc, char* argv[]) { diff --git a/app/org.kde.kdeconnect.app.desktop b/app/org.kde.kdeconnect.app.desktop --- a/app/org.kde.kdeconnect.app.desktop +++ b/app/org.kde.kdeconnect.app.desktop @@ -92,7 +92,7 @@ Comment[he]=הפוך את כך ההתקנים שלך לאחד Comment[hu]=Egyesítse eszközeit Comment[id]=Membuat semua perangkatmu menyatu -Comment[it]=Fai di tutti i tuoi dispositivi un solo dispositivo +Comment[it]=Unisce tutti i tuoi dispositivi Comment[ko]=모든 장치를 하나로 동기화 Comment[nl]=Al uw apparaten een maken Comment[nn]=Slå alle einingane dine saman diff --git a/app/qml/DevicePage.qml b/app/qml/DevicePage.qml --- a/app/qml/DevicePage.qml +++ b/app/qml/DevicePage.qml @@ -40,8 +40,6 @@ Loader { id: deviceLoader anchors.fill: parent - Layout.fillHeight: true - Layout.fillWidth: true sourceComponent: { if (deviceView.currentDevice.hasPairingRequests) { @@ -61,7 +59,7 @@ Component { id: trustedDevice - ColumnLayout { + ListView { property list actions: [ Kirigami.Action { onTriggered: deviceView.currentDevice.unpair() @@ -76,67 +74,74 @@ ] id: trustedView - spacing: 0 - Layout.fillHeight: true Layout.fillWidth: true - PluginItem { - label: i18n("Multimedia control") - interfaceFactory: MprisDbusInterfaceFactory - component: "qrc:/qml/mpris.qml" - pluginName: "mprisremote" - } - PluginItem { - label: i18n("Remote input") - interfaceFactory: RemoteControlDbusInterfaceFactory - component: "qrc:/qml/mousepad.qml" - pluginName: "remotecontrol" - } - PluginItem { - label: i18n("Presentation Remote") - interfaceFactory: RemoteKeyboardDbusInterfaceFactory - component: "qrc:/qml/presentationRemote.qml" - pluginName: "remotecontrol" - } - PluginItem { - readonly property var lockIface: LockDeviceDbusInterfaceFactory.create(deviceView.currentDevice.id()) - pluginName: "lockdevice" - label: lockIface.isLocked ? i18n("Unlock") : i18n("Lock") - onClicked: { - lockIface.isLocked = !lockIface.isLocked; - } - } - PluginItem { - readonly property var findmyphoneIface: FindMyPhoneDbusInterfaceFactory.create(deviceView.currentDevice.id()) - pluginName: "findmyphone" - label: i18n("Find Device") - onClicked: { - findmyphoneIface.ring() - } - } - PluginItem { - label: i18n("Run command") - interfaceFactory: RemoteCommandsDbusInterfaceFactory - component: "qrc:/qml/runcommand.qml" - pluginName: "remotecommands" - } - PluginItem { - readonly property var shareIface: ShareDbusInterfaceFactory.create(deviceView.currentDevice.id()) - pluginName: "share" - label: i18n("Share File") - onClicked: { - fileDialog.open() - shareIface.shareUrl(fileDialog.fileUrl) - } - } - PluginItem { - label: i18n("Volume control") - interfaceFactory: RemoteSystemVolumeDbusInterfaceFactory - component: "qrc:/qml/volume.qml" - pluginName: "remotesystemvolume" + model: plugins + delegate: Kirigami.BasicListItem { + label: name + icon: iconName + visible: loaded + onClicked: onClick() } - Item { Layout.fillHeight: true } + property list plugins : [ + + PluginItem { + name: i18n("Multimedia control") + interfaceFactory: MprisDbusInterfaceFactory + component: "qrc:/qml/mpris.qml" + pluginName: "mprisremote" + }, + PluginItem { + name: i18n("Remote input") + interfaceFactory: RemoteControlDbusInterfaceFactory + component: "qrc:/qml/mousepad.qml" + pluginName: "remotecontrol" + }, + PluginItem { + name: i18n("Presentation Remote") + interfaceFactory: RemoteKeyboardDbusInterfaceFactory + component: "qrc:/qml/presentationRemote.qml" + pluginName: "remotecontrol" + }, + PluginItem { + readonly property var lockIface: LockDeviceDbusInterfaceFactory.create(deviceView.currentDevice.id()) + pluginName: "lockdevice" + name: lockIface.isLocked ? i18n("Unlock") : i18n("Lock") + onClick: function() { + lockIface.isLocked = !lockIface.isLocked; + } + }, + PluginItem { + readonly property var findmyphoneIface: FindMyPhoneDbusInterfaceFactory.create(deviceView.currentDevice.id()) + pluginName: "findmyphone" + name: i18n("Find Device") + onClick: function() { + findmyphoneIface.ring() + } + }, + PluginItem { + name: i18n("Run command") + interfaceFactory: RemoteCommandsDbusInterfaceFactory + component: "qrc:/qml/runcommand.qml" + pluginName: "remotecommands" + }, + PluginItem { + readonly property var shareIface: ShareDbusInterfaceFactory.create(deviceView.currentDevice.id()) + pluginName: "share" + name: i18n("Share File") + onClick: function() { + fileDialog.open() + shareIface.shareUrl(fileDialog.fileUrl) + } + }, + PluginItem { + name: i18n("Volume control") + interfaceFactory: RemoteSystemVolumeDbusInterfaceFactory + component: "qrc:/qml/volume.qml" + pluginName: "remotesystemvolume" + } + ] } } Component { diff --git a/app/qml/FindDevicesPage.qml b/app/qml/FindDevicesPage.qml --- a/app/qml/FindDevicesPage.qml +++ b/app/qml/FindDevicesPage.qml @@ -46,6 +46,7 @@ property: "status" delegate: Kirigami.Heading { level: 4 + leftPadding: Kirigami.Units.smallSpacing text: switch (parseInt(section)) { case DevicesModel.Paired: @@ -65,6 +66,7 @@ delegate: Kirigami.BasicListItem { width: ListView.view.width icon: iconName + iconColor: "transparent" label: display + "\n" + toolTip onClicked: { pageStack.push( diff --git a/app/qml/PluginItem.qml b/app/qml/PluginItem.qml --- a/app/qml/PluginItem.qml +++ b/app/qml/PluginItem.qml @@ -24,19 +24,20 @@ import org.kde.kirigami 2.0 as Kirigami import org.kde.kdeconnect 1.0 -Kirigami.BasicListItem +QtObject { property alias pluginName: checker.pluginName + property alias iconName: checker.iconName + property alias loaded: checker.available property var interfaceFactory property var component + property var name readonly property var checker: PluginChecker { id: checker device: deviceView.currentDevice } - visible: checker.available - icon: checker.iconName - onClicked: { + property var onClick: function() { if (component === "" || !interfaceFactory) return; diff --git a/app/qml/main.qml b/app/qml/main.qml --- a/app/qml/main.qml +++ b/app/qml/main.qml @@ -107,7 +107,8 @@ sourceModel: DevicesModel { displayFilter: DevicesModel.Paired | DevicesModel.Reachable } } delegate: Kirigami.Action { - iconName: model.iconName + icon.name: model.iconName + icon.color: "transparent" text: display + "\n" + toolTip visible: status & DevicesModel.Reachable checked: pageStack.currentItem && pageStack.currentItem.currentDevice == device diff --git a/core/backends/bluetooth/bluetoothdevicelink.h b/core/backends/bluetooth/bluetoothdevicelink.h --- a/core/backends/bluetooth/bluetoothdevicelink.h +++ b/core/backends/bluetooth/bluetoothdevicelink.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include "../devicelink.h" #include "../devicelinereader.h" diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp --- a/core/backends/bluetooth/bluetoothdevicelink.cpp +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -79,7 +79,7 @@ //qCDebug(KDECONNECT_CORE) << "BluetoothDeviceLink dataReceived" << packet; - NetworkPacket packet(QString::null); + NetworkPacket packet((QString())); NetworkPacket::unserialize(serializedPacket, &packet); if (packet.type() == PACKET_TYPE_PAIR) { diff --git a/core/backends/bluetooth/bluetoothlinkprovider.h b/core/backends/bluetooth/bluetoothlinkprovider.h --- a/core/backends/bluetooth/bluetoothlinkprovider.h +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -24,13 +24,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "../linkprovider.h" diff --git a/core/backends/bluetooth/bluetoothpairinghandler.cpp b/core/backends/bluetooth/bluetoothpairinghandler.cpp --- a/core/backends/bluetooth/bluetoothpairinghandler.cpp +++ b/core/backends/bluetooth/bluetoothpairinghandler.cpp @@ -18,12 +18,13 @@ * along with this program. If not, see . */ +#include "bluetoothpairinghandler.h" + #include #include "core_debug.h" #include "daemon.h" #include "kdeconnectconfig.h" -#include "bluetoothpairinghandler.h" #include "networkpackettypes.h" BluetoothPairingHandler::BluetoothPairingHandler(DeviceLink* deviceLink) diff --git a/core/backends/devicelink.h b/core/backends/devicelink.h --- a/core/backends/devicelink.h +++ b/core/backends/devicelink.h @@ -22,8 +22,6 @@ #define DEVICELINK_H #include -#include //Fix build on older QCA -#include #include "core/networkpacket.h" diff --git a/core/backends/lan/compositeuploadjob.h b/core/backends/lan/compositeuploadjob.h --- a/core/backends/lan/compositeuploadjob.h +++ b/core/backends/lan/compositeuploadjob.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Erik Duisters + * Copyright 2018 Erik Duisters * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -66,6 +66,8 @@ quint64 m_totalPayloadSize; UploadJob *m_currentJob; QElapsedTimer m_timer; + quint64 m_prevElapsedTime; + bool m_updatePacketPending; const static quint16 MIN_PORT = 1739; const static quint16 MAX_PORT = 1764; @@ -79,6 +81,7 @@ void slotProcessedAmount(KJob *job, KJob::Unit unit, qulonglong amount); void slotResult(KJob *job) override; void startNextSubJob(); + void sendUpdatePacket(); }; #endif //COMPOSITEUPLOADJOB_H diff --git a/core/backends/lan/compositeuploadjob.cpp b/core/backends/lan/compositeuploadjob.cpp --- a/core/backends/lan/compositeuploadjob.cpp +++ b/core/backends/lan/compositeuploadjob.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2018 Erik Duisters + * Copyright 2018 Erik Duisters * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,6 +25,7 @@ #include #include "lanlinkprovider.h" #include +#include "plugins/share/shareplugin.h" CompositeUploadJob::CompositeUploadJob(const QString& deviceId, bool displayNotification) : KCompositeJob() @@ -39,6 +40,8 @@ , m_totalSendPayloadSize(0) , m_totalPayloadSize(0) , m_currentJob(nullptr) + , m_prevElapsedTime(0) + , m_updatePacketPending(false) { setCapabilities(Killable); @@ -203,14 +206,29 @@ } emitDescription(filename); + + if (m_running && m_currentJob && !m_updatePacketPending) { + m_updatePacketPending = true; + QMetaObject::invokeMethod(this, "sendUpdatePacket", Qt::QueuedConnection); + } return KCompositeJob::addSubjob(job); } else { qCDebug(KDECONNECT_CORE) << "CompositeUploadJob::addSubjob() - you can only add UploadJob's, ignoring"; return false; } } +void CompositeUploadJob::sendUpdatePacket() { + NetworkPacket np(PACKET_TYPE_SHARE_REQUEST_UPDATE); + np.set(QStringLiteral("numberOfFiles"), m_totalJobs); + np.set(QStringLiteral("totalPayloadSize"), m_totalPayloadSize); + + Daemon::instance()->getDevice(m_deviceId)->sendPacket(np); + + m_updatePacketPending = false; +} + bool CompositeUploadJob::doKill() { if (m_running) { @@ -226,13 +244,16 @@ Q_UNUSED(job); m_currentJobSendPayloadSize = amount; - quint64 uploaded = m_totalSendPayloadSize + m_currentJobSendPayloadSize; - setProcessedAmount(unit, uploaded); - const auto elapsed = m_timer.elapsed(); - if (elapsed > 0) { - emitSpeed((1000 * uploaded) / elapsed); + if (uploaded == m_totalPayloadSize || m_prevElapsedTime == 0 || m_timer.elapsed() - m_prevElapsedTime >= 100) { + m_prevElapsedTime = m_timer.elapsed(); + setProcessedAmount(unit, uploaded); + + const auto elapsed = m_timer.elapsed(); + if (elapsed > 0) { + emitSpeed((1000 * uploaded) / elapsed); + } } } diff --git a/core/backends/lan/landevicelink.cpp b/core/backends/lan/landevicelink.cpp --- a/core/backends/lan/landevicelink.cpp +++ b/core/backends/lan/landevicelink.cpp @@ -18,9 +18,10 @@ * along with this program. If not, see . */ +#include "landevicelink.h" + #include -#include "landevicelink.h" #include "core_debug.h" #include "kdeconnectconfig.h" #include "backends/linkprovider.h" @@ -116,7 +117,7 @@ if (m_socketLineReader->bytesAvailable() == 0) return; const QByteArray serializedPacket = m_socketLineReader->readLine(); - NetworkPacket packet(QString::null); + NetworkPacket packet((QString())); NetworkPacket::unserialize(serializedPacket, &packet); //qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket; diff --git a/core/backends/lan/lanpairinghandler.cpp b/core/backends/lan/lanpairinghandler.cpp --- a/core/backends/lan/lanpairinghandler.cpp +++ b/core/backends/lan/lanpairinghandler.cpp @@ -18,13 +18,14 @@ * along with this program. If not, see . */ +#include "lanpairinghandler.h" + #include #include "core_debug.h" #include "daemon.h" #include "kdeconnectconfig.h" #include "landevicelink.h" -#include "lanpairinghandler.h" #include "networkpackettypes.h" LanPairingHandler::LanPairingHandler(DeviceLink* deviceLink) diff --git a/core/backends/loopback/loopbackdevicelink.cpp b/core/backends/loopback/loopbackdevicelink.cpp --- a/core/backends/loopback/loopbackdevicelink.cpp +++ b/core/backends/loopback/loopbackdevicelink.cpp @@ -35,7 +35,7 @@ bool LoopbackDeviceLink::sendPacket(NetworkPacket& input) { - NetworkPacket output(QString::null); + NetworkPacket output((QString())); NetworkPacket::unserialize(input.serialize(), &output); //LoopbackDeviceLink does not need deviceTransferInfo diff --git a/core/daemon.h b/core/daemon.h --- a/core/daemon.h +++ b/core/daemon.h @@ -89,7 +89,7 @@ void onNewDeviceLink(const NetworkPacket& identityPacket, DeviceLink* dl); void onDeviceStatusChanged(); -private: +protected: void addDevice(Device* device); bool isDiscoveringDevices() const; void removeDevice(Device* d); diff --git a/core/filetransferjob.cpp b/core/filetransferjob.cpp --- a/core/filetransferjob.cpp +++ b/core/filetransferjob.cpp @@ -128,7 +128,7 @@ emitResult(); } else { - qCDebug(KDECONNECT_CORE) << "Received incomplete file, deleting"; + qCDebug(KDECONNECT_CORE) << "Received incomplete file ("<< m_written << "/" << m_size << "bytes ), deleting"; if (m_destination.isLocalFile() && QFile::exists(m_destination.toLocalFile())) { QFile::remove(m_destination.toLocalFile()); diff --git a/core/networkpacket.h b/core/networkpacket.h --- a/core/networkpacket.h +++ b/core/networkpacket.h @@ -27,7 +27,6 @@ #include #include #include -//#include #include #include diff --git a/daemon/kdeconnect.desktop b/daemon/kdeconnect.desktop --- a/daemon/kdeconnect.desktop +++ b/daemon/kdeconnect.desktop @@ -69,8 +69,8 @@ Comment[gl]=Conectar KDE co seu teléfono móbil. Comment[he]=חבר את KDE לפלאפון החכם שלך Comment[hu]=A KDE csatlakoztatása az okostelefonnal -Comment[id]=Sambung KDE dengan smartphone-mu -Comment[it]=Connetti KDE con il tuo smartphone +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 diff --git a/daemon/kdeconnectd.cpp b/daemon/kdeconnectd.cpp --- a/daemon/kdeconnectd.cpp +++ b/daemon/kdeconnectd.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -86,9 +87,14 @@ int main(int argc, char* argv[]) { QApplication app(argc, argv); - app.setApplicationName(QStringLiteral("kdeconnectd")); - app.setApplicationVersion(QStringLiteral(KDECONNECT_VERSION_STRING)); - app.setOrganizationDomain(QStringLiteral("kde.org")); + KAboutData aboutData( + QStringLiteral("kdeconnectd"), + i18n("KDE Connect Daemon"), + QStringLiteral(KDECONNECT_VERSION_STRING), + i18n("KDE Connect Daemon"), + KAboutLicense::GPL + ); + KAboutData::setApplicationData(aboutData); app.setQuitOnLastWindowClosed(false); KDBusService dbusService(KDBusService::Unique); diff --git a/data/kdeconnect-thunar.desktop b/data/kdeconnect-thunar.desktop --- a/data/kdeconnect-thunar.desktop +++ b/data/kdeconnect-thunar.desktop @@ -12,13 +12,16 @@ Name[en_GB]=Send via KDE Connect Name[es]=Enviar via KDE Connect Name[eu]=Bidali KDE Connect bidez +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[nl]=Via KDE Connect versturen Name[nn]=Send via KDE Connect +Name[pl]=Wyślij przez KDE Connect Name[pt]=Enviar pelo KDE Connect Name[pt_BR]=Enviar via KDE Connect +Name[sk]=Poslať súbor cez KDE Connect Name[sv]=Skicka via KDE-anslut Name[uk]=Надіслати за допомогою KDE Connect Name[x-test]=xxSend via KDE Connectxx diff --git a/data/org.kde.kdeconnect.kcm.appdata.xml b/data/org.kde.kdeconnect.kcm.appdata.xml --- a/data/org.kde.kdeconnect.kcm.appdata.xml +++ b/data/org.kde.kdeconnect.kcm.appdata.xml @@ -103,6 +103,7 @@

O KDE Connect oferece diversas funcionalidades para integrar o seu telefone com o seu computador: Lembre-se por favor que terá de instalar o KDE Connect com o seu computador para esta aplicação funcionar e deverá manter a versão no ambiente de trabalho actualizada com a versão para Android, para que as últimas funcionalidades funcionem.

O KDE Connect oferece diversas funcionalidades para integrar o seu celular com o seu computador: Lembre-se de que será necessário instalar o KDE Connect com o seu computador para este aplicativo funcionar e manter a versão no ambiente de trabalho atualizada com a versão para Android, para que as funcionalidades mais recentes funcionem.

KDE Connect предоставляет несколько функций для интеграции телефона и компьютера. Обратите внимание, что для работы этого приложения необходимо установить KDE Connect на свой компьютер и, для работы новейших функций, держать версию обновлённой в соответствии с версией для Android.

+

KDE Connect poskytuje viaceré funkcie na integráciu vášho telefónu a vášho počítača: Vedzte že potrebujete nainštalovať KDE Connect do vášho počítača aby táto aplikácia fungovala, a udržiavať počítačovú verziu aktuálnu z Android-ovou verziou aby fungovali najnovšie funkcie.

КДЕ‑конекција поседује вишеструке могућности интеграције вашег телефона и рачунара. Морате да инсталирате КДЕ‑конекцију на ваш рачунар да би ова апликација радила. Такође, нека вам обе апликације буду ажурне како бисте уживали у најновијим могућностима.

KDE‑konekcija poseduje višestruke mogućnosti integracije vašeg telefona i računara. Morate da instalirate KDE‑konekciju na vaš računar da bi ova aplikacija radila. Takođe, neka vam obe aplikacije budu ažurne kako biste uživali u najnovijim mogućnostima.

КДЕ‑конекција поседује вишеструке могућности интеграције вашег телефона и рачунара. Морате да инсталирате КДЕ‑конекцију на ваш рачунар да би ова апликација радила. Такође, нека вам обе апликације буду ажурне како бисте уживали у најновијим могућностима.

diff --git a/declarativeplugin/kdeconnectdeclarativeplugin.cpp b/declarativeplugin/kdeconnectdeclarativeplugin.cpp --- a/declarativeplugin/kdeconnectdeclarativeplugin.cpp +++ b/declarativeplugin/kdeconnectdeclarativeplugin.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "objectfactory.h" #include "responsewaiter.h" diff --git a/indicator/org.kde.kdeconnect.nonplasma.desktop b/indicator/org.kde.kdeconnect.nonplasma.desktop --- a/indicator/org.kde.kdeconnect.nonplasma.desktop +++ b/indicator/org.kde.kdeconnect.nonplasma.desktop @@ -21,6 +21,7 @@ Name[pt]=Indicador do KDE Connect Name[pt_BR]=Indicador do KDE Connect Name[ru]=Индикатор KDE Connect +Name[sk]=Ukozaovateľ KDE Connect Name[sr]=КДЕ‑конекцијин показатељ Name[sr@ijekavian]=КДЕ‑конекцијин показатељ Name[sr@ijekavianlatin]=KDE‑konekcijin pokazatelj @@ -49,7 +50,7 @@ Comment[he]=הצג מידע עך ההתקנים שלך Comment[hu]=Információk megjelenítése az eszközeiről Comment[id]=Displaikan informasi tentang perangkatmu -Comment[it]=Visualizza informazioni sui tuoi dispositivi +Comment[it]=Visualizza le informazioni sui tuoi dispositivi Comment[ko]=장치 정보 표시 Comment[nl]=Toon informatie over uw apparaten Comment[nn]=Vis informasjon om einingane dine diff --git a/interfaces/conversationmessage.cpp b/interfaces/conversationmessage.cpp --- a/interfaces/conversationmessage.cpp +++ b/interfaces/conversationmessage.cpp @@ -20,7 +20,6 @@ #include "conversationmessage.h" -#include #include diff --git a/interfaces/modeltest.cpp b/interfaces/modeltest.cpp --- a/interfaces/modeltest.cpp +++ b/interfaces/modeltest.cpp @@ -23,8 +23,6 @@ #include "modeltest.h" -#include - Q_DECLARE_METATYPE(QModelIndex) /*! diff --git a/interfaces/notificationsmodel.h b/interfaces/notificationsmodel.h --- a/interfaces/notificationsmodel.h +++ b/interfaces/notificationsmodel.h @@ -71,7 +71,7 @@ private Q_SLOTS: void notificationAdded(const QString& id); void notificationRemoved(const QString& id); - void notificationUpdated(const QString& id); + void notificationUpdated(); void refreshNotificationList(); void receivedNotifications(QDBusPendingCallWatcher* watcher); void clearNotifications(); diff --git a/interfaces/notificationsmodel.cpp b/interfaces/notificationsmodel.cpp --- a/interfaces/notificationsmodel.cpp +++ b/interfaces/notificationsmodel.cpp @@ -95,8 +95,6 @@ this, &NotificationsModel::notificationRemoved); connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::allNotificationsRemoved, this, &NotificationsModel::clearNotifications); - connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::notificationUpdated, - this, &NotificationsModel::notificationUpdated); refreshNotificationList(); @@ -107,6 +105,7 @@ { beginInsertRows(QModelIndex(), 0, 0); NotificationDbusInterface* dbusInterface = new NotificationDbusInterface(m_deviceId, id, this); + connect(dbusInterface, &NotificationDbusInterface::ready, this, &NotificationsModel::notificationUpdated); m_notificationList.prepend(dbusInterface); endInsertRows(); } @@ -266,9 +265,7 @@ } } -void NotificationsModel::notificationUpdated(const QString& id) +void NotificationsModel::notificationUpdated() { - //TODO only emit the affected indices - Q_UNUSED(id); Q_EMIT dataChanged(index(0,0), index(m_notificationList.size() - 1, 0)); } diff --git a/kcm/kcm_kdeconnect.desktop b/kcm/kcm_kdeconnect.desktop --- a/kcm/kcm_kdeconnect.desktop +++ b/kcm/kcm_kdeconnect.desktop @@ -68,8 +68,8 @@ Comment[gl]=Conecte e sincronice os seus dispositivos. Comment[he]=חבר וסנכרן את ההתקנים שלך Comment[hu]=Csatlakoztassa és szinkronizálja eszközeit -Comment[id]=Sambung dan sinkron perangkatmu -Comment[it]=Connetti e sincronizza i tuoi dispositivi +Comment[id]=Koneksikan dan sinkronkan perangkatmu +Comment[it]=Connette e sincronizza i tuoi dispositivi Comment[ko]=내 장치에 연결하고 동기화 Comment[nl]=Uw apparaten verbinden en synchroniseren Comment[nn]=Kopla til og synkroniser einingar diff --git a/kcm/org.kde.kdeconnect.kcm.desktop b/kcm/org.kde.kdeconnect.kcm.desktop --- a/kcm/org.kde.kdeconnect.kcm.desktop +++ b/kcm/org.kde.kdeconnect.kcm.desktop @@ -57,7 +57,7 @@ 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]=Sambung dan sinkron perangkatmu +GenericName[id]=Koneksikan dan sinkronkan perangkatmu GenericName[it]=Connetti e sincronizza i tuoi dispositivi GenericName[ko]=내 장치에 연결하고 동기화 GenericName[nl]=Uw apparaten verbinden en synchroniseren diff --git a/kio/kiokdeconnect.cpp b/kio/kiokdeconnect.cpp --- a/kio/kiokdeconnect.cpp +++ b/kio/kiokdeconnect.cpp @@ -143,7 +143,7 @@ } if (!mountreply.value()) { - error(KIO::ERR_COULD_NOT_MOUNT, i18n("Could not mount device filesystem")); + error(KIO::ERR_COULD_NOT_MOUNT, interface.getMountError()); return; } diff --git a/plasmoid/package/contents/ui/DeviceDelegate.qml b/plasmoid/package/contents/ui/DeviceDelegate.qml --- a/plasmoid/package/contents/ui/DeviceDelegate.qml +++ b/plasmoid/package/contents/ui/DeviceDelegate.qml @@ -255,7 +255,7 @@ iconSource: "list-add" tooltip: i18n("Add command") onClicked: rc.plugin.editCommands() - visible: rc.plugin.canAddCommand + visible: rc.plugin && rc.plugin.canAddCommand } } Repeater { diff --git a/plugins/battery/kdeconnect_battery.json b/plugins/battery/kdeconnect_battery.json --- a/plugins/battery/kdeconnect_battery.json +++ b/plugins/battery/kdeconnect_battery.json @@ -94,7 +94,7 @@ "Name[eu]": "Bateriaren monitorea", "Name[fi]": "Akkuvalvonta", "Name[fr]": "Moniteur de batterie", - "Name[gl]": "Vixilante da batería", + "Name[gl]": "Monitor da batería", "Name[hu]": "Akkumulátorfigyelő", "Name[ia]": "Controlator de batteria", "Name[id]": "Battery monitor", diff --git a/plugins/contacts/contactsplugin.cpp b/plugins/contacts/contactsplugin.cpp --- a/plugins/contacts/contactsplugin.cpp +++ b/plugins/contacts/contactsplugin.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -135,7 +135,7 @@ if (!line.startsWith("X-KDECONNECT-TIMESTAMP:")) { continue; } - QStringList parts = line.split(":"); + QStringList parts = line.split(QLatin1Char(':')); QString timestamp = parts[1]; qint32 remoteTimestamp = np.get(ID); diff --git a/plugins/contacts/kdeconnect_contacts.json b/plugins/contacts/kdeconnect_contacts.json --- a/plugins/contacts/kdeconnect_contacts.json +++ b/plugins/contacts/kdeconnect_contacts.json @@ -23,6 +23,7 @@ "Name[pl]": "Simon Redman", "Name[pt]": "Simon Redman", "Name[pt_BR]": "Simon Redman", + "Name[sk]": "Simon Redman", "Name[sv]": "Simon Redman", "Name[uk]": "Simon Redman", "Name[x-test]": "xxSimon Redmanxx", @@ -41,14 +42,15 @@ "Description[fi]": "Synkronoi yhteystiedot työpöydän ja yhdistetyn laitteen välillä", "Description[fr]": "Synchroniser les contacts entre l'ordinateur et le périphérique connecté", "Description[gl]": "Sincronizar contactos entre o escritorio e o dispositivo conectado", - "Description[id]": "Sinkronkan Kontak Antara Desktop dengan Perangkat Tersambung", + "Description[id]": "Sinkronkan Kontak Antara Desktop dengan Perangkat Terkoneksisi", "Description[it]": "Sincronizza i contatti tra il desktop e il dispositivo connesso", "Description[ko]": "데스크톱과 연결된 장치간 연락처 동기화", "Description[nl]": "Contactpersonen synchroniseren tussen het bureaublad en het verbonden apparaat", "Description[nn]": "Synkroniser kontaktar mellom skrivebordet og tilkopla eining", "Description[pl]": "Synchronizuj kontakty pomiędzy pulpitem i podłączonym urządzeniem", "Description[pt]": "Sincronizar os Contactos entre o Sistema e o Dispositivo Ligado", "Description[pt_BR]": "Sincroniza os contatos entre o sistema e o dispositivo conectado", + "Description[sk]": "Synchronizujte si kontakty medzi počítačom a pripojeným zariadením", "Description[sv]": "Synkronisera kontakter mellan skrivbordet och ansluten enhet", "Description[uk]": "Синхронізація контактів між комп'ютером і з'єднаним пристроєм", "Description[x-test]": "xxSynchronize Contacts Between the Desktop and the Connected Devicexx", @@ -77,6 +79,7 @@ "Name[pl]": "Kontakty", "Name[pt]": "Contactos", "Name[pt_BR]": "Contatos", + "Name[sk]": "Kontakty", "Name[sv]": "Kontakter", "Name[uk]": "Контакти", "Name[x-test]": "xxContactsxx", diff --git a/plugins/findthisdevice/kdeconnect_findthisdevice.json b/plugins/findthisdevice/kdeconnect_findthisdevice.json --- a/plugins/findthisdevice/kdeconnect_findthisdevice.json +++ b/plugins/findthisdevice/kdeconnect_findthisdevice.json @@ -22,6 +22,7 @@ "Name[pl]": "Friedrich W. H. Kossebau", "Name[pt]": "Friedrich W. H. Kossebau", "Name[pt_BR]": "Friedrich W. H. Kossebau", + "Name[sk]": "Friedrich W. H. Kossebau", "Name[sv]": "Friedrich W. H. Kossebau", "Name[uk]": "Friedrich W. H. Kossebau", "Name[x-test]": "xxFriedrich W. H. Kossebauxx", @@ -48,6 +49,7 @@ "Description[pl]": "Znajdź to urządzenie odgrywając na nim dźwięk", "Description[pt]": "Descubra este dispositivo, pondo-o a tocar um alarme de som", "Description[pt_BR]": "Encontre este dispositivo fazendo ele reproduzir um som", + "Description[sk]": "Nájsť váš telefón zahraním zvuku budíka", "Description[sv]": "Hitta apparaten genom att få den att spela ett alarmljud", "Description[uk]": "Знайти цей пристрій, наказавши йому відтворити дзвінок", "Description[x-test]": "xxFind this device by making it play an alarm soundxx", @@ -76,6 +78,7 @@ "Name[pl]": "Znajdywanie urządzenia", "Name[pt]": "Procurar este dispositivo", "Name[pt_BR]": "Procurar este dispositivo", + "Name[sk]": "Nájsť toto zariadenie", "Name[sv]": "Hitta apparaten", "Name[uk]": "Знайти цей пристрій", "Name[x-test]": "xxFind this devicexx", diff --git a/plugins/findthisdevice/kdeconnect_findthisdevice_config.desktop b/plugins/findthisdevice/kdeconnect_findthisdevice_config.desktop --- a/plugins/findthisdevice/kdeconnect_findthisdevice_config.desktop +++ b/plugins/findthisdevice/kdeconnect_findthisdevice_config.desktop @@ -21,8 +21,10 @@ Name[ko]=이 장치 플러그인 설정 찾기 Name[nl]=Plug-in-instellingen van dit apparaat zoeken Name[nn]=Innstillingar for einingsfinning +Name[pl]=Ustawienia wtyczki znajdowania tego urządzenia Name[pt]=Configuração do 'plugin' para Procurar Este Dispositivo Name[pt_BR]=Configurações do plugin Procurar Este Dispositivo +Name[sk]=Nastavenia pluginu na nájdenie zariadenia Name[sv]=Inställningar av insticksprogrammet Hitta apparaten Name[uk]=Параметри додатка пошуку пристрою Name[x-test]=xxFind This Device plugin settingsxx diff --git a/plugins/kdeconnect.notifyrc b/plugins/kdeconnect.notifyrc --- a/plugins/kdeconnect.notifyrc +++ b/plugins/kdeconnect.notifyrc @@ -261,7 +261,7 @@ Name[he]=שיחה שלא נענתה Name[hu]=Nem fogadott hívás Name[id]=Panggilan Tidak Terjawab -Name[it]=Chiamate perse +Name[it]=Chiamata persa Name[ko]=부재 중 전화 Name[nl]=Gemiste oproep Name[nn]=Tapt oppringing @@ -462,7 +462,7 @@ Comment[he]=הבטריה שלך הולכת להגמר Comment[hu]=Az akkumulátora feszültsége alacsony Comment[id]=Bateraimu dalam keadaan lemah -Comment[it]=La tua batteria è al livello basso +Comment[it]=La batteria è al livello basso Comment[ko]=배터리가 부족함 Comment[nl]=Uw batterij is bijna leeg Comment[nn]=Det er lite batteri att diff --git a/plugins/mousepad/waylandremoteinput.h b/plugins/mousepad/waylandremoteinput.h --- a/plugins/mousepad/waylandremoteinput.h +++ b/plugins/mousepad/waylandremoteinput.h @@ -21,6 +21,7 @@ #ifndef WAYLANDREMOTEINPUT_H #define WAYLANDREMOTEINPUT_H +#include #include "abstractremoteinput.h" namespace KWayland @@ -44,7 +45,7 @@ private: void setupWaylandIntegration(); - KWayland::Client::FakeInput* m_waylandInput; + QPointer m_waylandInput; bool m_waylandAuthenticationRequested; }; diff --git a/plugins/mousepad/waylandremoteinput.cpp b/plugins/mousepad/waylandremoteinput.cpp --- a/plugins/mousepad/waylandremoteinput.cpp +++ b/plugins/mousepad/waylandremoteinput.cpp @@ -46,6 +46,7 @@ m_waylandInput = registry->createFakeInput(name, version, this); } ); + connect(registry, &Registry::fakeInputRemoved, m_waylandInput, &QObject::deleteLater); registry->setup(); } diff --git a/plugins/mprisremote/mprisremoteplayer.h b/plugins/mprisremote/mprisremoteplayer.h --- a/plugins/mprisremote/mprisremoteplayer.h +++ b/plugins/mprisremote/mprisremoteplayer.h @@ -21,6 +21,8 @@ #include +class NetworkPacket; + class MprisRemotePlayer { public: diff --git a/plugins/mprisremote/mprisremoteplayer.cpp b/plugins/mprisremote/mprisremoteplayer.cpp --- a/plugins/mprisremote/mprisremoteplayer.cpp +++ b/plugins/mprisremote/mprisremoteplayer.cpp @@ -18,10 +18,11 @@ * along with this program. If not, see . */ -#include +#include "mprisremoteplayer.h" + #include -#include "mprisremoteplayer.h" +#include MprisRemotePlayer::MprisRemotePlayer() : m_playing(false) diff --git a/plugins/mprisremote/mprisremoteplugin.cpp b/plugins/mprisremote/mprisremoteplugin.cpp --- a/plugins/mprisremote/mprisremoteplugin.cpp +++ b/plugins/mprisremote/mprisremoteplugin.cpp @@ -50,7 +50,11 @@ return false; if (np.has(QStringLiteral("player"))) { - m_players[np.get(QStringLiteral("player"))]->parseNetworkPacket(np); + const QString player = np.get(QStringLiteral("player")); + if(!m_players.contains(player)) { + m_players[player] = new MprisRemotePlayer(); + } + m_players[player]->parseNetworkPacket(np); } if (np.has(QStringLiteral("playerList"))) { diff --git a/plugins/notifications/kdeconnect_notifications.json b/plugins/notifications/kdeconnect_notifications.json --- a/plugins/notifications/kdeconnect_notifications.json +++ b/plugins/notifications/kdeconnect_notifications.json @@ -122,7 +122,8 @@ }, "X-KdeConnect-OutgoingPacketType": [ "kdeconnect.notification.request", - "kdeconnect.notification.reply" + "kdeconnect.notification.reply", + "kdeconnect.notification.action" ], "X-KdeConnect-SupportedPacketType": [ "kdeconnect.notification" diff --git a/plugins/notifications/notification.h b/plugins/notifications/notification.h --- a/plugins/notifications/notification.h +++ b/plugins/notifications/notification.h @@ -62,17 +62,17 @@ bool silent() const { return m_silent; } void update(const NetworkPacket& np); bool isReady() const { return m_ready; } - KNotification* createKNotification(bool update, const NetworkPacket& np); + KNotification* createKNotification(const NetworkPacket& np); public Q_SLOTS: Q_SCRIPTABLE void dismiss(); Q_SCRIPTABLE void reply(); - void closed(); Q_SIGNALS: void dismissRequested(const QString& m_internalId); void replyRequested(); - void ready(); + Q_SCRIPTABLE void ready(); + void actionTriggered(const QString& key, const QString& action); private: QString m_internalId; @@ -84,12 +84,12 @@ QString m_requestReplyId; bool m_dismissable; bool m_hasIcon; - KNotification* m_notification; + QPointer m_notification; QDir m_imagesDir; bool m_silent; - bool m_closed; QString m_payloadHash; bool m_ready; + QStringList m_actions; void parseNetworkPacket(const NetworkPacket& np); void loadIcon(const NetworkPacket& np); diff --git a/plugins/notifications/notification.cpp b/plugins/notifications/notification.cpp --- a/plugins/notifications/notification.cpp +++ b/plugins/notifications/notification.cpp @@ -22,13 +22,16 @@ #include "notification_debug.h" #include +#include #include #include #include #include #include #include +#include + #include QMap Notification::s_downloadsInProgress; @@ -47,11 +50,10 @@ m_imagesDir = QDir::temp().absoluteFilePath(QStringLiteral("kdeconnect_") + username); m_imagesDir.mkpath(m_imagesDir.absolutePath()); QFile(m_imagesDir.absolutePath()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); - m_closed = false; m_ready = false; parseNetworkPacket(np); - createKNotification(false, np); + createKNotification(np); } Notification::~Notification() @@ -70,20 +72,19 @@ m_ready = true; Q_EMIT ready(); if (!m_silent) { - m_closed = false; m_notification->sendEvent(); } } void Notification::update(const NetworkPacket& np) { parseNetworkPacket(np); - createKNotification(!m_closed, np); + createKNotification(np); } -KNotification* Notification::createKNotification(bool update, const NetworkPacket& np) +KNotification* Notification::createKNotification(const NetworkPacket& np) { - if (!update) { + if (!m_notification) { m_notification = new KNotification(QStringLiteral("notification"), KNotification::CloseOnTimeout, this); m_notification->setComponentName(QStringLiteral("kdeconnect")); } @@ -106,6 +107,15 @@ m_notification->setText(escapedTitle+": "+escapedText); } + connect(m_notification, QOverload::of(&KNotification::activated), this, [this] (unsigned int actionIndex) { + // Do nothing for our own reply action + if(!m_requestReplyId.isEmpty() && actionIndex == 1) { + return; + } + // Notification action idices start at 1 + Q_EMIT actionTriggered(m_internalId, m_actions[actionIndex - 1]); + }); + m_hasIcon = m_hasIcon && !m_payloadHash.isEmpty(); if (!m_hasIcon) { @@ -117,11 +127,10 @@ } if (!m_requestReplyId.isEmpty()) { - m_notification->setActions(QStringList(i18n("Reply"))); + m_actions.prepend(i18n("Reply")); connect(m_notification, &KNotification::action1Activated, this, &Notification::reply); } - - connect(m_notification, &KNotification::closed, this, &Notification::closed); + m_notification->setActions(m_actions); return m_notification; } @@ -171,11 +180,6 @@ Q_EMIT replyRequested(); } -void Notification::closed() -{ - m_closed = true; -} - void Notification::parseNetworkPacket(const NetworkPacket& np) { m_internalId = np.get(QStringLiteral("id")); @@ -188,4 +192,11 @@ m_silent = np.get(QStringLiteral("silent")); m_payloadHash = np.get(QStringLiteral("payloadHash")); m_requestReplyId = np.get(QStringLiteral("requestReplyId"), QString()); + + m_actions.clear(); + + for (QJsonValue value : np.get(QStringLiteral("actions"))) { + m_actions.append(value.toString()); + } + } diff --git a/plugins/notifications/notificationsdbusinterface.h b/plugins/notifications/notificationsdbusinterface.h --- a/plugins/notifications/notificationsdbusinterface.h +++ b/plugins/notifications/notificationsdbusinterface.h @@ -52,6 +52,7 @@ public Q_SLOTS: Q_SCRIPTABLE QStringList activeNotifications(); Q_SCRIPTABLE void sendReply(const QString& replyId, const QString& message); + Q_SCRIPTABLE void sendAction(const QString& key, const QString& action); Q_SIGNALS: Q_SCRIPTABLE void notificationPosted(const QString& publicId); diff --git a/plugins/notifications/notificationsdbusinterface.cpp b/plugins/notifications/notificationsdbusinterface.cpp --- a/plugins/notifications/notificationsdbusinterface.cpp +++ b/plugins/notifications/notificationsdbusinterface.cpp @@ -74,35 +74,26 @@ if (id.startsWith(QLatin1String("org.kde.kdeconnect_tp::"))) id = id.mid(id.indexOf(QLatin1String("::")) + 2); removeNotification(id); - } else { - QString id = np.get(QStringLiteral("id")); + return; + } - if (!m_internalIdToPublicId.contains(id)) { - Notification* noti = new Notification(np, this); + QString id = np.get(QStringLiteral("id")); - if (noti->isReady()) { - addNotification(noti); - } else { - connect(noti, &Notification::ready, this, &NotificationsDbusInterface::notificationReady); - } + Notification* noti = nullptr; + + if (!m_internalIdToPublicId.contains(id)) { + noti = new Notification(np, this); + + if (noti->isReady()) { + addNotification(noti); } else { - QString pubId = m_internalIdToPublicId.value(id); - Notification* noti = m_notifications.value(pubId); - if (!noti) - return; - - noti->update(np); - - if (noti->isReady()) { - Q_EMIT notificationUpdated(pubId); - } else { - connect(noti, &Notification::ready, this, [this, pubId]{ - Q_EMIT notificationUpdated(pubId); - }); - } + connect(noti, &Notification::ready, this, &NotificationsDbusInterface::notificationReady); } - + } else { + QString pubId = m_internalIdToPublicId.value(id); + noti = m_notifications.value(pubId); } + noti->update(np); } void NotificationsDbusInterface::addNotification(Notification* noti) @@ -122,6 +113,8 @@ replyRequested(noti); }); + connect(noti, &Notification::actionTriggered, this, &NotificationsDbusInterface::sendAction); + const QString& publicId = newId(); m_notifications[publicId] = noti; m_internalIdToPublicId[internalId] = publicId; @@ -186,6 +179,14 @@ m_plugin->sendPacket(np); } +void NotificationsDbusInterface::sendAction(const QString& key, const QString& action) +{ + NetworkPacket np(PACKET_TYPE_NOTIFICATION_ACTION); + np.set("key", key); + np.set("action", action); + m_plugin->sendPacket(np); +} + QString NotificationsDbusInterface::newId() { return QString::number(++m_lastId); diff --git a/plugins/notifications/notificationsplugin.h b/plugins/notifications/notificationsplugin.h --- a/plugins/notifications/notificationsplugin.h +++ b/plugins/notifications/notificationsplugin.h @@ -25,6 +25,8 @@ #define PACKET_TYPE_NOTIFICATION_REQUEST QStringLiteral("kdeconnect.notification.request") #define PACKET_TYPE_NOTIFICATION_REPLY QStringLiteral("kdeconnect.notification.reply") +#define PACKET_TYPE_NOTIFICATION_ACTION QStringLiteral("kdeconnect.notification.action") + /* * This class is just a proxy for NotificationsDbusInterface diff --git a/plugins/remotecommands/kdeconnect_remotecommands.json b/plugins/remotecommands/kdeconnect_remotecommands.json --- a/plugins/remotecommands/kdeconnect_remotecommands.json +++ b/plugins/remotecommands/kdeconnect_remotecommands.json @@ -103,6 +103,7 @@ "Name[pt]": "Alojar os comandos remotos", "Name[pt_BR]": "Hospedar os comandos remotos", "Name[ru]": "Выполнение команд на устройстве", + "Name[sk]": "Vzdialene spúšťať príkazy", "Name[sr@ijekavian]": "Даљинске наредбе домаћина", "Name[sr@ijekavianlatin]": "Daljinske naredbe domaćina", "Name[sr@latin]": "Daljinske naredbe domaćina", diff --git a/plugins/remotekeyboard/kdeconnect_remotekeyboard.json b/plugins/remotekeyboard/kdeconnect_remotekeyboard.json --- a/plugins/remotekeyboard/kdeconnect_remotekeyboard.json +++ b/plugins/remotekeyboard/kdeconnect_remotekeyboard.json @@ -62,6 +62,7 @@ "Description[pt]": "Usar o seu teclado para enviar eventos de teclas para o seu dispositivo emparelhado", "Description[pt_BR]": "Use seu teclado para enviar eventos chace ao seu dispositivo pareado", "Description[ru]": "Используйте клавиатуру для отправки нажатий клавиш на сопряжённое устройство", + "Description[sk]": "Používajte vašu klávesnicu na posielanie stlačení klávesov do párovaných zariadení", "Description[sr@ijekavian]": "Користите тастатуру за куцање на упареном уређају", "Description[sr@ijekavianlatin]": "Koristite tastaturu za kucanje na uparenom uređaju", "Description[sr@latin]": "Koristite tastaturu za kucanje na uparenom uređaju", @@ -98,6 +99,7 @@ "Name[pt]": "Teclado remoto do ambiente de trabalho", "Name[pt_BR]": "Teclado remoto a partir da área de trabalho", "Name[ru]": "Удалённая клавиатура с компьютера", + "Name[sk]": "Vzdialená klávesnica z počítača", "Name[sr@ijekavian]": "Даљинска тастатура са радне површи", "Name[sr@ijekavianlatin]": "Daljinska tastatura sa radne površi", "Name[sr@latin]": "Daljinska tastatura sa radne površi", diff --git a/plugins/remotesystemvolume/kdeconnect_remotesystemvolume.json b/plugins/remotesystemvolume/kdeconnect_remotesystemvolume.json --- a/plugins/remotesystemvolume/kdeconnect_remotesystemvolume.json +++ b/plugins/remotesystemvolume/kdeconnect_remotesystemvolume.json @@ -11,6 +11,7 @@ "Name[en_GB]": "Nicolas Fella", "Name[es]": "Nicolas Fella", "Name[eu]": "Nicolas Fella", + "Name[fi]": "Nicolas Fella", "Name[fr]": "Nicolas Fella", "Name[gl]": "Nicolas Fella", "Name[it]": "Nicolas Fella", @@ -20,6 +21,7 @@ "Name[pl]": "Nicolas Fella", "Name[pt]": "Nicolas Fella", "Name[pt_BR]": "Nicolas Fella", + "Name[sk]": "Nicolas Fella", "Name[sv]": "Nicolas Fella", "Name[uk]": "Nicolas Fella", "Name[x-test]": "xxNicolas Fellaxx", @@ -35,14 +37,16 @@ "Description[en_GB]": "Control the volume of the connected device", "Description[es]": "Controle el volumen del dispositivo conectado", "Description[eu]": "Kontrolatu konektatutako gailuaren bolumena", + "Description[fi]": "Säädä kytketyn laitteen äänenvoimakkuutta", "Description[fr]": "Contrôler le volume du périphérique connecté", "Description[gl]": "Controlar o volume do dispositivo conectado", "Description[it]": "Controlla il volume del dispositivo connesso", "Description[nl]": "Bestuur het volume van het verbonden apparaat", "Description[nn]": "Juster lydstyrken til ekstern eining", "Description[pl]": "Sterowanie głośnością podłączonego urządzenia", "Description[pt]": "Controle o volume do dispositivo ligado", "Description[pt_BR]": "Controle o volume do dispositivo conectado", + "Description[sk]": "Ovládajte hlasitosť pripojeného zariadenia", "Description[sv]": "Kontrollera volymen på ansluten apparat", "Description[uk]": "Керування томом сховища даних на з'єднаному пристрої", "Description[x-test]": "xxControl the volume of the connected devicexx", @@ -60,14 +64,16 @@ "Name[en_GB]": "Remote system volume", "Name[es]": "Volumen del sistema remoto", "Name[eu]": "Urruneko sistemaren bolumena", + "Name[fi]": "Etäjärjestelmän äänenvoimakkuus", "Name[fr]": "Volume du système distant", "Name[gl]": "Volume de sistema remoto", "Name[it]": "Volume del sistema remoto", "Name[nl]": "Systeemvolume op afstand", "Name[nn]": "Ekstern lydstyrke", "Name[pl]": "Głośność na zdalnym systemie", "Name[pt]": "Volume do sistema remoto", "Name[pt_BR]": "Volume do sistema remoto", + "Name[sk]": "Hlasitosť vzdialeného systému", "Name[sv]": "Fjärrsystemvolym", "Name[uk]": "Віддалений системний том", "Name[x-test]": "xxRemote system volumexx", diff --git a/plugins/screensaver-inhibit/kdeconnect_screensaver_inhibit.json b/plugins/screensaver-inhibit/kdeconnect_screensaver_inhibit.json --- a/plugins/screensaver-inhibit/kdeconnect_screensaver_inhibit.json +++ b/plugins/screensaver-inhibit/kdeconnect_screensaver_inhibit.json @@ -56,7 +56,7 @@ "Description[fr]": "Inhiber l'économiseur d'écran quand le périphérique est connecté", "Description[gl]": "Desactivar o salvapantallas mentres o dispositivo estea conectado.", "Description[hu]": "A képernyővédő kikapcsolása, ha az eszköz csatlakoztatva van", - "Description[id]": "Hambat screensaver ketika perangkat telah tersambung", + "Description[id]": "Hambat screensaver ketika perangkat telah terkoneksi", "Description[it]": "Impedisce il salvaschermo quando il dispositivo è connesso", "Description[ko]": "장치가 연결되었을 때 화면 보호기 실행 중지", "Description[nl]": "Houdt de schermbeveiliging tegen wanneer het apparaat is verbonden", diff --git a/plugins/sendnotifications/notificationslistener.cpp b/plugins/sendnotifications/notificationslistener.cpp --- a/plugins/sendnotifications/notificationslistener.cpp +++ b/plugins/sendnotifications/notificationslistener.cpp @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "notificationslistener.h" #include #include @@ -33,7 +34,6 @@ #include #include -#include "notificationslistener.h" #include "sendnotificationsplugin.h" #include "sendnotification_debug.h" #include "notifyingapplication.h" diff --git a/plugins/sftp/mounter.cpp b/plugins/sftp/mounter.cpp --- a/plugins/sftp/mounter.cpp +++ b/plugins/sftp/mounter.cpp @@ -82,6 +82,11 @@ unmount(false); return; } + + if (np.has("errorMessage")) { + Q_EMIT failed(np.get("errorMessage", "")); + return; + } //This is the previous code, to access sftp server using KIO. Now we are //using the external binary sshfs, and accessing it as a local filesystem. diff --git a/plugins/sftp/sftpplugin.h b/plugins/sftp/sftpplugin.h --- a/plugins/sftp/sftpplugin.h +++ b/plugins/sftp/sftpplugin.h @@ -50,6 +50,7 @@ Q_SCRIPTABLE void unmount(); Q_SCRIPTABLE bool mountAndWait(); Q_SCRIPTABLE bool isMounted() const; + Q_SCRIPTABLE QString getMountError(); Q_SCRIPTABLE bool startBrowsing(); Q_SCRIPTABLE QString mountPoint(); @@ -71,6 +72,7 @@ QString deviceId; //Storing it to avoid accessing device() from the destructor which could cause a crash QVariantMap remoteDirectories; //Actually a QMap, but QDBus prefers this + QString mountError; }; diff --git a/plugins/sftp/sftpplugin.cpp b/plugins/sftp/sftpplugin.cpp --- a/plugins/sftp/sftpplugin.cpp +++ b/plugins/sftp/sftpplugin.cpp @@ -117,6 +117,16 @@ return d->m_mounter && d->m_mounter->isMounted(); } +QString SftpPlugin::getMountError() +{ + if (!mountError.isEmpty()) { + return mountError; + } else { + return i18n("Could not mount device filesystem"); + } +} + + bool SftpPlugin::startBrowsing() { if (mountAndWait()) { @@ -128,7 +138,7 @@ bool SftpPlugin::receivePacket(const NetworkPacket& np) { - if (!(fields_c - np.body().keys().toSet()).isEmpty()) { + if (!(fields_c - np.body().keys().toSet()).isEmpty() && !np.has("errorMessage")) { // packet is invalid return false; } @@ -147,6 +157,10 @@ remoteDirectories.insert(mountPoint(), i18n("All files")); remoteDirectories.insert(mountPoint() + "/DCIM/Camera", i18n("Camera pictures")); } + + if (np.has("errorMessage")) { + mountError = np.get("errorMessage"); + } return true; } diff --git a/plugins/share/CMakeLists.txt b/plugins/share/CMakeLists.txt --- a/plugins/share/CMakeLists.txt +++ b/plugins/share/CMakeLists.txt @@ -10,6 +10,7 @@ KF5::Notifications KF5::I18n KF5::KIOWidgets + KF5::Service ) ####################################### diff --git a/plugins/share/kdeconnect_share.json b/plugins/share/kdeconnect_share.json --- a/plugins/share/kdeconnect_share.json +++ b/plugins/share/kdeconnect_share.json @@ -123,7 +123,8 @@ "Website": "http://albertvaka.wordpress.com" }, "X-KdeConnect-OutgoingPacketType": [ - "kdeconnect.share.request" + "kdeconnect.share.request", + "kdeconnect.share.request.update" ], "X-KdeConnect-SupportedPacketType": [ "kdeconnect.share.request" diff --git a/plugins/share/shareplugin.h b/plugins/share/shareplugin.h --- a/plugins/share/shareplugin.h +++ b/plugins/share/shareplugin.h @@ -26,6 +26,7 @@ #include #define PACKET_TYPE_SHARE_REQUEST QStringLiteral("kdeconnect.share.request") +#define PACKET_TYPE_SHARE_REQUEST_UPDATE QStringLiteral("kdeconnect.share.request.update") class SharePlugin : public KdeConnectPlugin diff --git a/plugins/share/shareplugin.cpp b/plugins/share/shareplugin.cpp --- a/plugins/share/shareplugin.cpp +++ b/plugins/share/shareplugin.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "core/filetransferjob.h" @@ -121,14 +122,19 @@ } } else if (np.has(QStringLiteral("text"))) { QString text = np.get(QStringLiteral("text")); - if (!QStandardPaths::findExecutable(QStringLiteral("kate")).isEmpty()) { + + KService::Ptr service = KMimeTypeTrader::self()->preferredService(QStringLiteral("text/plain")); + const QString defaultApp = service ? service->desktopEntryName() : QString(); + + if (defaultApp == QLatin1String("org.kde.kate") || defaultApp == QLatin1String("org.kde.kwrite")) { QProcess* proc = new QProcess(); connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); - proc->start(QStringLiteral("kate"), QStringList(QStringLiteral("--stdin"))); + proc->start(defaultApp.section('.', 2,2), QStringList(QStringLiteral("--stdin"))); proc->write(text.toUtf8()); proc->closeWriteChannel(); } else { QTemporaryFile tmpFile; + tmpFile.setFileTemplate(QStringLiteral("kdeconnect-XXXXXX.txt")); tmpFile.setAutoRemove(false); tmpFile.open(); tmpFile.write(text.toUtf8()); diff --git a/plugins/sms/conversationsdbusinterface.cpp b/plugins/sms/conversationsdbusinterface.cpp --- a/plugins/sms/conversationsdbusinterface.cpp +++ b/plugins/sms/conversationsdbusinterface.cpp @@ -43,6 +43,12 @@ ConversationsDbusInterface::~ConversationsDbusInterface() { + // Wake all threads which were waiting for a reply from this interface + // This might result in some noise on dbus, but it's better than leaking a bunch of resources! + waitingForMessagesLock.lock(); + conversationsWaitingForMessages.clear(); + waitingForMessages.wakeAll(); + waitingForMessagesLock.unlock(); } QVariantList ConversationsDbusInterface::activeConversations() @@ -130,6 +136,12 @@ void ConversationsDbusInterface::updateConversation(const qint64& conversationID) { waitingForMessagesLock.lock(); + if (conversationsWaitingForMessages.contains(conversationID)) { + // This conversation is already being waited on, don't allow more than one thread to wait at a time + qCDebug(KDECONNECT_CONVERSATIONS) << "Not allowing two threads to wait for conversationID" << conversationID; + waitingForMessagesLock.unlock(); + return; + } qCDebug(KDECONNECT_CONVERSATIONS) << "Requesting conversation with ID" << conversationID << "from remote"; conversationsWaitingForMessages.insert(conversationID); m_smsInterface.requestConversation(conversationID); diff --git a/plugins/sms/kdeconnect_sms.json b/plugins/sms/kdeconnect_sms.json --- a/plugins/sms/kdeconnect_sms.json +++ b/plugins/sms/kdeconnect_sms.json @@ -22,6 +22,7 @@ "Name[pl]": "Simon Redman", "Name[pt]": "Simon Redman", "Name[pt_BR]": "Simon Redman", + "Name[sk]": "Simon Redman", "Name[sv]": "Simon Redman", "Name[uk]": "Simon Redman", "Name[x-test]": "xxSimon Redmanxx", @@ -37,14 +38,16 @@ "Description[en_GB]": "Send and receive SMS", "Description[es]": "Enviar y recibir SMS", "Description[eu]": "Bidali eta jaso SMSak", + "Description[fi]": "Lähetä ja vastaanota tekstiviestejä", "Description[fr]": "Envoyer et recevoir des SMS", "Description[gl]": "Enviar e recibir SMS", "Description[it]": "Invia e riceve SMS", "Description[nl]": "SMS verzenden en ontvangen", "Description[nn]": "Send og ta imot SMS-ar", "Description[pl]": "Wysyłaj i otrzymuj SMSy", "Description[pt]": "Enviar e receber SMS's", "Description[pt_BR]": "Envia e recebe SMS", + "Description[sk]": "Poslať a prijať SMS", "Description[sv]": "Skicka och ta emot SMS", "Description[uk]": "Надсилання і отримання SMS", "Description[x-test]": "xxSend and receive SMSxx", @@ -62,14 +65,16 @@ "Name[en_GB]": "SMS", "Name[es]": "SMS", "Name[eu]": "SMS", + "Name[fi]": "Tekstiviesti", "Name[fr]": "SMS", "Name[gl]": "SMS", "Name[it]": "SMS", "Name[nl]": "SMS", "Name[nn]": "SMS", "Name[pl]": "SMS", "Name[pt]": "SMS", "Name[pt_BR]": "SMS", + "Name[sk]": "SMS", "Name[sv]": "SMS", "Name[uk]": "SMS", "Name[x-test]": "xxSMSxx", diff --git a/plugins/sms/requestconversationworker.h b/plugins/sms/requestconversationworker.h --- a/plugins/sms/requestconversationworker.h +++ b/plugins/sms/requestconversationworker.h @@ -41,6 +41,13 @@ public Q_SLOTS: + /** + * Main body of this worker + * + * Reply to a request for messages and, if needed, wait for the remote to reply with more + * + * Emits conversationMessageRead for every message handled + */ void handleRequestConversation(); void work(); @@ -50,11 +57,24 @@ private: qint64 conversationID; - int start; - int end; + int start; // Start of range to request messages + size_t howMany; // Number of messages being requested ConversationsDbusInterface* parent; QThread* m_thread; + + /** + * Reply with all messages we currently have available in the requested range + * + * If the range specified by start and howMany is not in the range of messages in the conversation, + * reply with only as many messages as we have available in that range + * + * @param conversation Conversation to send messages from + * @param start Start of requested range, 0-indexed, inclusive + * @param howMany Maximum number of messages to return + * $return Number of messages processed + */ + size_t replyForConversation(const QList& conversation, int start, size_t howMany); }; #endif // REQUESTCONVERSATIONWORKER_H diff --git a/plugins/sms/requestconversationworker.cpp b/plugins/sms/requestconversationworker.cpp --- a/plugins/sms/requestconversationworker.cpp +++ b/plugins/sms/requestconversationworker.cpp @@ -28,10 +28,12 @@ //QObject(interface) conversationID(conversationID) , start(start) - , end(end) , parent(interface) , m_thread(new QThread) { + Q_ASSERT(end >= start && "Not allowed to have a negative-length range"); + howMany = end - start; + this->moveToThread(m_thread); connect(m_thread, &QThread::started, this, &RequestConversationWorker::handleRequestConversation); @@ -52,29 +54,37 @@ qCWarning(KDECONNECT_CONVERSATIONS) << "Got a conversationID for a conversation with no messages!" << conversationID; } - // TODO: Reply with all messages we currently have available, even if we don't have enough to completely fill the request // In case the remote takes awhile to respond, we should go ahead and do anything we can from the cache + size_t numHandled = replyForConversation(messagesList, start, howMany); - if (messagesList.length() <= end) { + if (numHandled < howMany) { + size_t numRemaining = howMany - numHandled; // If we don't have enough messages in cache, go get some more // TODO: Make Android interface capable of requesting small window of messages parent->updateConversation(conversationID); messagesList = parent->getConversation(conversationID); + //ConversationsDbusInterface::getConversation blocks until it sees new messages in the requested conversation + replyForConversation(messagesList, start + numHandled, numRemaining); } + Q_EMIT finished(); +} + +size_t RequestConversationWorker::replyForConversation(const QList& conversation, int start, size_t howMany) { + Q_ASSERT(start >= 0); // Messages are sorted in ascending order of keys, meaning the front of the list has the oldest // messages (smallest timestamp number) // Therefore, return the end of the list first (most recent messages) - int i = start; - for(auto it = messagesList.crbegin() + start; it != messagesList.crend(); ++it) { - Q_EMIT conversationMessageRead(it->toVariant()); - i++; - if (i >= end) { + int i = 0; + for(auto it = conversation.crbegin() + start; it != conversation.crend(); ++it) { + if (i >= howMany) { break; } + Q_EMIT conversationMessageRead(it->toVariant()); + i++; } - Q_EMIT finished(); + return i; } void RequestConversationWorker::work() diff --git a/plugins/systemvolume/kdeconnect_systemvolume.json b/plugins/systemvolume/kdeconnect_systemvolume.json --- a/plugins/systemvolume/kdeconnect_systemvolume.json +++ b/plugins/systemvolume/kdeconnect_systemvolume.json @@ -12,6 +12,7 @@ "Name[en_GB]": "Nicolas Fella", "Name[es]": "Nicolas Fella", "Name[eu]": "Nicolas Fella", + "Name[fi]": "Nicolas Fella", "Name[fr]": "Nicolas Fella", "Name[gl]": "Nicolas Fella", "Name[it]": "Nicolas Fella", @@ -21,6 +22,7 @@ "Name[pl]": "Nicolas Fella", "Name[pt]": "Nicolas Fella", "Name[pt_BR]": "Nicolas Fella", + "Name[sk]": "Nicolas Fella", "Name[sv]": "Nicolas Fella", "Name[uk]": "Nicolas Fella", "Name[x-test]": "xxNicolas Fellaxx", @@ -36,6 +38,7 @@ "Description[en_GB]": "Control the system volume from your phone", "Description[es]": "Controle el volumen del sistema desde su teléfono", "Description[eu]": "Kontrolatu sistemaren bolumena zure telefonotik", + "Description[fi]": "Säädä järjestelmän äänenvoimakkuutta puhelimestasi", "Description[fr]": "Contrôler le volume du système depuis votre téléphone", "Description[gl]": "Controlar o volume do sistema desde o teléfono", "Description[it]": "Controlla il volume di sistema dal tuo telefono", @@ -45,6 +48,7 @@ "Description[pl]": "Steruj głośnością systemu z telefonu", "Description[pt]": "Controle o volume do sistema a partir do seu telemóvel", "Description[pt_BR]": "Controle o volume do sistema com o seu telefone", + "Description[sk]": "Ovládajte hlasitosť systému z vášho telefónu", "Description[sv]": "Kontrollera systemvolymen från telefonen", "Description[uk]": "Керування системним томом з вашого телефону", "Description[x-test]": "xxControl the system volume from your phonexx", @@ -62,6 +66,7 @@ "Name[en_GB]": "System volume", "Name[es]": "Volumen del sistema", "Name[eu]": "Sistemaren bolumena", + "Name[fi]": "Järjestelmän äänenvoimakkuus", "Name[fr]": "Volume du système", "Name[gl]": "Volume do sistema", "Name[it]": "Volume di sistema", @@ -71,6 +76,7 @@ "Name[pl]": "Głośność na systemie", "Name[pt]": "Volume do sistema", "Name[pt_BR]": "Volume do sistema", + "Name[sk]": "Systémová hlasitosť", "Name[sv]": "Systemvolym", "Name[uk]": "Системний том", "Name[x-test]": "xxSystem volumexx", diff --git a/plugins/systemvolume/systemvolumeplugin-win.h b/plugins/systemvolume/systemvolumeplugin-win.h --- a/plugins/systemvolume/systemvolumeplugin-win.h +++ b/plugins/systemvolume/systemvolumeplugin-win.h @@ -1,3 +1,23 @@ +/** + * Copyright 2018 Jun Bo Bi + * + * 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 . + */ + #ifndef SYSTEMVOLUMEPLUGINWIN_H #define SYSTEMVOLUMEPLUGINWIN_H diff --git a/plugins/systemvolume/systemvolumeplugin-win.cpp b/plugins/systemvolume/systemvolumeplugin-win.cpp --- a/plugins/systemvolume/systemvolumeplugin-win.cpp +++ b/plugins/systemvolume/systemvolumeplugin-win.cpp @@ -1,16 +1,37 @@ -#include "systemvolumeplugin-win.h" -#include +/** + * Copyright 2018 Jun Bo Bi + * + * 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 . + */ -#include +#include "systemvolumeplugin-win.h" #include #include #include #include #include +#include + #include +#include + K_PLUGIN_FACTORY_WITH_JSON(KdeConnectPluginFactory, "kdeconnect_systemvolume.json", registerPlugin();) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SYSTEMVOLUME, "kdeconnect.plugin.systemvolume") @@ -322,4 +343,4 @@ return true; } -#include "systemvolumeplugin-win.moc" \ No newline at end of file +#include "systemvolumeplugin-win.moc" diff --git a/plugins/telephony/kdeconnect_telephony.json b/plugins/telephony/kdeconnect_telephony.json --- a/plugins/telephony/kdeconnect_telephony.json +++ b/plugins/telephony/kdeconnect_telephony.json @@ -64,6 +64,7 @@ "Description[pt]": "Mostrar notificações para as chamadas e SMS", "Description[pt_BR]": "Mostra notificações para chamadas e SMS", "Description[ru]": "Показ уведомлений для звонков и SMS", + "Description[sk]": "Zobraziť oznámenia pre hovory a SMS", "Description[sr@ijekavian]": "Приказује обавештења за позиве и СМС", "Description[sr@ijekavianlatin]": "Prikazuje obaveštenja za pozive i SMS", "Description[sr@latin]": "Prikazuje obaveštenja za pozive i SMS", diff --git a/plugins/telephony/telephonyplugin.h b/plugins/telephony/telephonyplugin.h --- a/plugins/telephony/telephonyplugin.h +++ b/plugins/telephony/telephonyplugin.h @@ -63,6 +63,7 @@ public: Q_SIGNALS: + Q_SCRIPTABLE void callReceived(const QString& event, const QString& number, const QString contactName); private Q_SLOTS: void sendMutePacket(); diff --git a/plugins/telephony/telephonyplugin.cpp b/plugins/telephony/telephonyplugin.cpp --- a/plugins/telephony/telephonyplugin.cpp +++ b/plugins/telephony/telephonyplugin.cpp @@ -74,6 +74,8 @@ #endif } + Q_EMIT callReceived(type, phoneNumber, contactName); + qCDebug(KDECONNECT_PLUGIN_TELEPHONY) << "Creating notification with type:" << type; KNotification* notification = new KNotification(type, flags, this); diff --git a/runners/CMakeLists.txt b/runners/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/runners/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(remotecommands) +add_subdirectory(findmyphone) diff --git a/runners/findmyphone/CMakeLists.txt b/runners/findmyphone/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/runners/findmyphone/CMakeLists.txt @@ -0,0 +1,7 @@ +add_definitions(-DTRANSLATION_DOMAIN="plasma_runner_kdeconnect_findmyphone") + +add_library(krunner_kdeconnect_findmyphone MODULE findmyphonerunner.cpp) +target_link_libraries(krunner_kdeconnect_findmyphone PUBLIC KF5::Runner KF5::I18n kdeconnectinterfaces) + +install(TARGETS krunner_kdeconnect_findmyphone DESTINATION ${KDE_INSTALL_PLUGINDIR}) +install(FILES plasma-runner-kdeconnect-findmyphone.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) diff --git a/runners/findmyphone/Messages.sh b/runners/findmyphone/Messages.sh new file mode 100644 --- /dev/null +++ b/runners/findmyphone/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/plasma_runner_kdeconnect_findmyphone.pot diff --git a/runners/findmyphone/findmyphonerunner.h b/runners/findmyphone/findmyphonerunner.h new file mode 100644 --- /dev/null +++ b/runners/findmyphone/findmyphonerunner.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright © 2018 Nicolas Fella * + * * + * 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 . * + ***************************************************************************/ + +#ifndef FINDMYPHONESRUNNER_H +#define FINDMYPHONESRUNNER_H + +#include + +#include "interfaces/dbusinterfaces.h" + +class FindMyPhoneRunner : public Plasma::AbstractRunner +{ +Q_OBJECT + +public: + FindMyPhoneRunner(QObject *parent, const QVariantList &args); + ~FindMyPhoneRunner() override; + + void match(Plasma::RunnerContext &context) override; + void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action) override; + +private: + DaemonDbusInterface m_daemonInterface; + +}; + +#endif + diff --git a/runners/findmyphone/findmyphonerunner.cpp b/runners/findmyphone/findmyphonerunner.cpp new file mode 100644 --- /dev/null +++ b/runners/findmyphone/findmyphonerunner.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright © 2018 Nicolas Fella * + * * + * 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 . * + ***************************************************************************/ + +#include "findmyphonerunner.h" + +#include +#include + +#include "interfaces/dbusinterfaces.h" + +K_EXPORT_PLASMA_RUNNER(installer, FindMyPhoneRunner) + +FindMyPhoneRunner::FindMyPhoneRunner(QObject *parent, const QVariantList &args) + : Plasma::AbstractRunner(parent, args) + , m_daemonInterface(new DaemonDbusInterface) +{ + Q_UNUSED(args) + + setObjectName(QStringLiteral("Find my device")); + setPriority(AbstractRunner::NormalPriority); +} + +FindMyPhoneRunner::~FindMyPhoneRunner() +{ +} + +void FindMyPhoneRunner::match(Plasma::RunnerContext &context) +{ + QDBusReply devicesReply = m_daemonInterface.devices(true, true); + + if (devicesReply.isValid()) { + + for (const QString& deviceId : devicesReply.value()) { + + DeviceDbusInterface deviceInterface(deviceId, this); + + if(!deviceInterface.hasPlugin(QStringLiteral("kdeconnect_findmyphone"))) { + continue; + } + + QString deviceName = deviceInterface.name(); + + if (deviceName.contains(context.query(), Qt::CaseInsensitive) || i18n("Ring").contains(context.query(), Qt::CaseInsensitive) || i18n("Find").contains(context.query(), Qt::CaseInsensitive)) { + + Plasma::QueryMatch match(this); + match.setType(Plasma::QueryMatch::PossibleMatch); + match.setId(deviceId); + match.setIconName(QStringLiteral("kdeconnect")); + match.setText(i18n("Find %1", deviceName)); + match.setData(deviceId); + context.addMatch(match); + } + } + } +} + +void FindMyPhoneRunner::run(const Plasma::RunnerContext &/*context*/, const Plasma::QueryMatch &match) +{ + FindMyPhoneDeviceDbusInterface findInterface(match.data().toString(), this); + + findInterface.ring(); +} + +#include "findmyphonerunner.moc" diff --git a/runners/findmyphone/plasma-runner-kdeconnect-findmyphone.desktop b/runners/findmyphone/plasma-runner-kdeconnect-findmyphone.desktop new file mode 100644 --- /dev/null +++ b/runners/findmyphone/plasma-runner-kdeconnect-findmyphone.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Name= Find my device +Name[ca]= Troba el meu dispositiu +Name[ca@valencia]= Troba el meu dispositiu +Name[cs]= Najít moje zařízení +Name[es]=Encontrar mi dispositivo +Name[gl]=Atopar o meu dispositivo +Name[it]= Trova il mio dispositivo +Name[nl]= Zoek mijn apparaat +Name[pt]= Procurar o meu dispositivo +Name[pt_BR]= Localizar meu dispositivo +Name[sv]=Hitta min apparat +Name[uk]=Знайти пристрій +Name[x-test]=xx Find my devicexx +Name[zh_TW]= 尋找我的裝置 +Comment=Ring connected devices +Comment[ca]=Fa sonar als dispositius connectats +Comment[ca@valencia]=Fa sonar als dispositius connectats +Comment[cs]=Prozvonit připojená zařízení +Comment[es]=Hacer sonar los dispositivos conectados +Comment[gl]=Facer soar os dispositivos conectados +Comment[it]=Fa squillare i dispositivi connessi +Comment[nl]=Bel verbonden apparaten +Comment[pt]=Tocar os dispositivos ligados +Comment[pt_BR]=Toca os dispositivos conectados +Comment[sv]=Ring anslutna apparater +Comment[uk]=Дзвінок на з'єднаних пристроях +Comment[x-test]=xxRing connected devicesxx +Comment[zh_TW]=讓連結的裝置發出鈴聲 +Icon=kdeconnect +Type=Service +X-KDE-ServiceTypes=Plasma/Runner + +X-KDE-Library=krunner_kdeconnect_findmyphone +X-KDE-PluginInfo-Author=Nicolas Fella +X-KDE-PluginInfo-Email=nicolas.fella@gmx.de +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/runners/remotecommands/CMakeLists.txt b/runners/remotecommands/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/runners/remotecommands/CMakeLists.txt @@ -0,0 +1,7 @@ +add_definitions(-DTRANSLATION_DOMAIN="plasma_runner_kdeconnect_remotecommands") + +add_library(krunner_kdeconnect_remotecommands MODULE remotecommandsrunner.cpp) +target_link_libraries(krunner_kdeconnect_remotecommands PUBLIC KF5::Runner kdeconnectinterfaces) + +install(TARGETS krunner_kdeconnect_remotecommands DESTINATION ${KDE_INSTALL_PLUGINDIR}) +install(FILES plasma-runner-kdeconnect-remotecommands.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) diff --git a/runners/remotecommands/Messages.sh b/runners/remotecommands/Messages.sh new file mode 100644 --- /dev/null +++ b/runners/remotecommands/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/plasma_runner_kdeconnect_remotecommands.pot diff --git a/runners/remotecommands/plasma-runner-kdeconnect-remotecommands.desktop b/runners/remotecommands/plasma-runner-kdeconnect-remotecommands.desktop new file mode 100644 --- /dev/null +++ b/runners/remotecommands/plasma-runner-kdeconnect-remotecommands.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Name=Run command +Name[ca]=Executa una ordre +Name[ca@valencia]=Executa una ordre +Name[cs]=Spustit příkaz +Name[es]=Ejecutar orden +Name[gl]=Executar unha orde +Name[it]=Esegui comando +Name[nl]=Commando uitvoeren +Name[pt]=Executar um comando +Name[pt_BR]=Executar comando +Name[sv]=Kör kommando +Name[uk]=Виконати команду +Name[x-test]=xxRun commandxx +Name[zh_TW]=執行指令 +Comment=Run commands on connected devices +Comment[ca]=Executa les ordres als dispositius connectats +Comment[ca@valencia]=Executa les ordres als dispositius connectats +Comment[cs]=Spustit příkazy na připojených zařízeních +Comment[es]=Ejecutar órdenes en los dispositivos conectados +Comment[gl]=Executar ordes nos dispositivos conectados +Comment[it]=Esegue dei comandi sui dispositivi connessi +Comment[nl]=Commando's op verbonden apparaten uitvoeren +Comment[pt]=Executar comandos nos dispositivos ligados +Comment[pt_BR]=Executa comandos nos dispositivos conectados +Comment[sv]=Kör kommando på anslutna apparater +Comment[uk]=Виконання команд на з'єднаних пристроях +Comment[x-test]=xxRun commands on connected devicesxx +Comment[zh_TW]=在連結的裝置上執行指令 +Icon=kdeconnect +Type=Service +X-KDE-ServiceTypes=Plasma/Runner + +X-KDE-Library=krunner_kdeconnect_remotecommands +X-KDE-PluginInfo-Author=Nicolas Fella +X-KDE-PluginInfo-Email=nicolas.fella@gmx.de +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/runners/remotecommands/remotecommandsrunner.h b/runners/remotecommands/remotecommandsrunner.h new file mode 100644 --- /dev/null +++ b/runners/remotecommands/remotecommandsrunner.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright © 2018 Nicolas Fella * + * * + * 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 . * + ***************************************************************************/ + +#ifndef REMOTECOMMANDSRUNNER_H +#define REMOTECOMMANDSRUNNER_H + +#include + +#include "interfaces/dbusinterfaces.h" + +class RemoteCommandsRunner : public Plasma::AbstractRunner +{ +Q_OBJECT + +public: + RemoteCommandsRunner(QObject *parent, const QVariantList &args); + ~RemoteCommandsRunner() override; + + void match(Plasma::RunnerContext &context) override; + void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action) override; + +private: + DaemonDbusInterface m_daemonInterface; + +}; + +#endif + diff --git a/runners/remotecommands/remotecommandsrunner.cpp b/runners/remotecommands/remotecommandsrunner.cpp new file mode 100644 --- /dev/null +++ b/runners/remotecommands/remotecommandsrunner.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright © 2018 Nicolas Fella * + * * + * 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 . * + ***************************************************************************/ + +#include "remotecommandsrunner.h" + +#include + +#include "interfaces/dbusinterfaces.h" + +K_EXPORT_PLASMA_RUNNER(installer, RemoteCommandsRunner) + +RemoteCommandsRunner::RemoteCommandsRunner(QObject *parent, const QVariantList &args) + : Plasma::AbstractRunner(parent, args) + , m_daemonInterface(new DaemonDbusInterface) +{ + Q_UNUSED(args) + + setObjectName(QStringLiteral("Run Commands")); + setPriority(AbstractRunner::HighestPriority); +} + +RemoteCommandsRunner::~RemoteCommandsRunner() +{ +} + +void RemoteCommandsRunner::match(Plasma::RunnerContext &context) +{ + QDBusReply devicesReply = m_daemonInterface.devices(true, true); + + if (devicesReply.isValid()) { + + for (const QString& deviceId : devicesReply.value()) { + + DeviceDbusInterface deviceInterface(deviceId, this); + + if(!deviceInterface.hasPlugin("kdeconnect_remotecommands")) { + continue; + } + + RemoteCommandsDbusInterface remoteCommandsInterface(deviceId, this); + + const auto cmds = QJsonDocument::fromJson(remoteCommandsInterface.commands()).object(); + + for (auto it = cmds.constBegin(), itEnd = cmds.constEnd(); it!=itEnd; ++it) { + const QJsonObject cont = it->toObject(); + + const QString deviceName = deviceInterface.name(); + const QString commandName = cont.value(QStringLiteral("name")).toString(); + + if (deviceName.contains(context.query(), Qt::CaseInsensitive) || commandName.contains(context.query(), Qt::CaseInsensitive)) { + + Plasma::QueryMatch match(this); + match.setType(Plasma::QueryMatch::PossibleMatch); + match.setId(it.key()); + match.setIconName(QStringLiteral("kdeconnect")); + match.setText(deviceName + ": " + commandName); + match.setSubtext(cont.value(QStringLiteral("command")).toString()); + match.setData(deviceId + "$" + it.key()); + context.addMatch(match); + } + } + } + } +} + +void RemoteCommandsRunner::run(const Plasma::RunnerContext &/*context*/, const Plasma::QueryMatch &match) +{ + RemoteCommandsDbusInterface remoteCommandsInterface(match.data().toString().split("$")[0], this); + + remoteCommandsInterface.triggerCommand(match.data().toString().split("$")[1]); +} + +#include "remotecommandsrunner.moc" diff --git a/smsapp/conversationlistmodel.h b/smsapp/conversationlistmodel.h --- a/smsapp/conversationlistmodel.h +++ b/smsapp/conversationlistmodel.h @@ -68,7 +68,7 @@ : public QStandardItemModel { Q_OBJECT - Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId) + Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) public: ConversationListModel(QObject* parent = nullptr); @@ -92,6 +92,9 @@ void createRowFromMessage(const QVariantMap& message); void printDBusError(const QDBusError& error); +Q_SIGNALS: + void deviceIdChanged(); + private: /** * Get all conversations currently known by the conversationsInterface, if any diff --git a/smsapp/conversationlistmodel.cpp b/smsapp/conversationlistmodel.cpp --- a/smsapp/conversationlistmodel.cpp +++ b/smsapp/conversationlistmodel.cpp @@ -31,7 +31,7 @@ : QStandardItemModel(parent) , m_conversationsInterface(nullptr) { - qCDebug(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "Constructing" << this; + //qCDebug(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "Constructing" << this; auto roles = roleNames(); roles.insert(FromMeRole, "fromMe"); roles.insert(AddressRole, "address"); @@ -53,6 +53,10 @@ return; } + if (deviceId == "") { + return; + } + qCDebug(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "setDeviceId" << deviceId << "of" << this; if (m_conversationsInterface) { @@ -63,6 +67,7 @@ } m_deviceId = deviceId; + Q_EMIT deviceIdChanged(); // This method still gets called *with a valid deviceID* when the device is not connected while the component is setting up // Detect that case and don't do anything. @@ -187,7 +192,7 @@ bool matchingPhoneNumber = longerNumber.endsWith(shorterNumber) && shorterNumber.length() * 2 >= longerNumber.length(); if (address == email || matchingPhoneNumber) { - qCDebug(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "Matched" << address << "to" << person->name(); + //qCDebug(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "Matched" << address << "to" << person->name(); return person; } diff --git a/smsapp/conversationmodel.cpp b/smsapp/conversationmodel.cpp --- a/smsapp/conversationmodel.cpp +++ b/smsapp/conversationmodel.cpp @@ -51,7 +51,8 @@ m_threadId = threadId; clear(); - if (threadId != INVALID_THREAD_ID) { + knownMessageIDs.clear(); + if (m_threadId != INVALID_THREAD_ID && m_deviceId != "") { requestMoreMessages(); } } diff --git a/smsapp/main.cpp b/smsapp/main.cpp --- a/smsapp/main.cpp +++ b/smsapp/main.cpp @@ -31,7 +31,6 @@ #include #include #include -#include int main(int argc, char *argv[]) { diff --git a/smsapp/org.kde.kdeconnect.sms.desktop b/smsapp/org.kde.kdeconnect.sms.desktop --- a/smsapp/org.kde.kdeconnect.sms.desktop +++ b/smsapp/org.kde.kdeconnect.sms.desktop @@ -15,8 +15,10 @@ Name[ko]=KDE Connect SMS Name[nl]=KDE Connect SMS Name[nn]=KDE Connect-SMS +Name[pl]=KDE Connect SMS Name[pt]=SMS do KDE Connect Name[pt_BR]=KDE Connect SMS +Name[sk]=KDE Connect SMS Name[sv]=KDE-anslut SMS Name[uk]=KDE Connect SMS Name[x-test]=xxKDE Connect SMSxx @@ -38,8 +40,10 @@ GenericName[ko]=SMS GenericName[nl]=SMS GenericName[nn]=SMS +GenericName[pl]=SMS GenericName[pt]=SMS GenericName[pt_BR]=SMS +GenericName[sk]=SMS GenericName[sv]=SMS GenericName[uk]=SMS GenericName[x-test]=xxSMSxx @@ -61,8 +65,10 @@ Comment[ko]=텍스트 메시징 Comment[nl]=Tekstberichten versturen Comment[nn]=Tekstmeldingar +Comment[pl]=Wiadomości tekstowe Comment[pt]=Mensagem de Texto Comment[pt_BR]=Mensagem de texto +Comment[sk]=Posielanie správ Comment[sv]=Textmeddelanden Comment[uk]=Обмін текстовими повідомленнями Comment[x-test]=xxText Messagingxx diff --git a/smsapp/qml/ConversationDisplay.qml b/smsapp/qml/ConversationDisplay.qml --- a/smsapp/qml/ConversationDisplay.qml +++ b/smsapp/qml/ConversationDisplay.qml @@ -21,7 +21,7 @@ */ import QtQuick 2.1 -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.1 import org.kde.people 1.0 import org.kde.kirigami 2.4 as Kirigami @@ -34,7 +34,9 @@ readonly property QtObject person: PersonData { id: person } - property QtObject device + + property bool deviceConnected + property string deviceId property int conversationId property string phoneNumber @@ -54,7 +56,6 @@ } function sendMessage() { - console.log("sending sms", page.phoneNumber) model.sourceModel.sendReplyToConversation(message.text) message.text = "" } @@ -66,7 +67,7 @@ sortOrder: Qt.AscendingOrder sortRole: ConversationModel.DateRole sourceModel: ConversationModel { - deviceId: device.id() + deviceId: page.deviceId threadId: page.conversationId } } @@ -120,7 +121,7 @@ } footer: RowLayout { - enabled: page.device + enabled: page.deviceConnected ScrollView { Layout.maximumHeight: page.height / 3 Layout.fillWidth: true diff --git a/smsapp/qml/ConversationList.qml b/smsapp/qml/ConversationList.qml --- a/smsapp/qml/ConversationList.qml +++ b/smsapp/qml/ConversationList.qml @@ -30,9 +30,28 @@ Kirigami.ScrollablePage { + id: page + ToolTip { + id: noDevicesWarning + visible: !devicesCombo.enabled + text: "⚠️ " + i18n("No devices available") + " ⚠️" + + MouseArea { + // Detect mouseover and show another tooltip with more information + anchors.fill: parent + hoverEnabled: true + + ToolTip.visible: containsMouse + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + // TODO: Wrap text if line is too long for the screen + ToolTip.text: i18n("No new messages can be sent or received, but you can browse cached content") + } + } + footer: ComboBox { id: devicesCombo enabled: count > 0 + displayText: !enabled ? i18n("No devices available") : undefined model: DevicesSortProxyModel { id: devicesModel //TODO: make it possible to filter if they can do sms @@ -43,18 +62,17 @@ } textRole: "display" } - - Label { - text: i18n("No devices available") - anchors.centerIn: parent - visible: !devicesCombo.enabled - } + readonly property bool deviceConnected: devicesCombo.enabled readonly property QtObject device: devicesCombo.currentIndex >= 0 ? devicesModel.data(devicesModel.index(devicesCombo.currentIndex, 0), DevicesModel.DeviceRole) : null + readonly property alias lastDeviceId: conversationListModel.deviceId - Component { + ConversationDisplay { id: chatView - ConversationDisplay {} + ConversationDisplay { + deviceId: page.lastDeviceId + deviceConnected: page.deviceConnected + } } ListView { @@ -66,6 +84,7 @@ sortRole: ConversationListModel.DateRole filterCaseSensitivity: Qt.CaseInsensitive sourceModel: ConversationListModel { + id: conversationListModel deviceId: device ? device.id() : "" } } @@ -113,9 +132,19 @@ personUri: model.personUri, phoneNumber: address, conversationId: model.conversationId, - device: device}) + }) } - onClicked: { startChat(); } + onClicked: { + startChat(); + view.currentIndex = index + } + // Keep the currently-open chat highlighted even if this element is not focused + highlighted: chatView.conversationId == model.conversationId + } + + Component.onCompleted: { + currentIndex = -1 + focus = true } } diff --git a/tests/lanlinkprovidertest.cpp b/tests/lanlinkprovidertest.cpp --- a/tests/lanlinkprovidertest.cpp +++ b/tests/lanlinkprovidertest.cpp @@ -31,6 +31,7 @@ #include #include #include +#include /* * This class tests the working of LanLinkProvider under different conditions that when identity packet is received over TCP, over UDP and same when the device is paired. diff --git a/tests/networkpackettests.cpp b/tests/networkpackettests.cpp --- a/tests/networkpackettests.cpp +++ b/tests/networkpackettests.cpp @@ -23,7 +23,6 @@ #include "core/networkpacket.h" #include -#include QTEST_GUILESS_MAIN(NetworkPacketTests); diff --git a/tests/sendfiletest.cpp b/tests/sendfiletest.cpp --- a/tests/sendfiletest.cpp +++ b/tests/sendfiletest.cpp @@ -103,9 +103,14 @@ kcc->addTrustedDevice(deviceId, deviceName, deviceType); kcc->setDeviceProperty(deviceId, QStringLiteral("certificate"), QString::fromLatin1(kcc->certificate().toPem())); // Using same certificate from kcc, instead of generating + //We need the device to be loaded on the daemon, otherwise CompositeUploadJob will get a null device + Device* device = new Device(this, deviceId); + m_daemon->addDevice(device); + QSharedPointer f(new QFile(aFile)); NetworkPacket np(PACKET_TYPE_SHARE_REQUEST); - np.setPayload(f, aFile.size()); + np.setPayload(f, f->size()); + CompositeUploadJob* job = new CompositeUploadJob(deviceId, false); UploadJob* uj = new UploadJob(np); job->addSubjob(uj); @@ -115,19 +120,21 @@ f->open(QIODevice::ReadWrite); - FileTransferJob* ft = new FileTransferJob(f, aFile.size(), QUrl::fromLocalFile(destFile)); + FileTransferJob* ft = np.createPayloadTransferJob(QUrl::fromLocalFile(destFile)); QSignalSpy spyTransfer(ft, &KJob::result); ft->start(); - QVERIFY(spyTransfer.count() || spyTransfer.wait(1000000000)); + QVERIFY(spyTransfer.count() || spyTransfer.wait()); - if (ft->error()) qWarning() << "fterror" << ft->errorString(); + if (ft->error()) { + qWarning() << "fterror" << ft->errorString(); + } QCOMPARE(ft->error(), 0); - // HACK | FIXME: Why does this break the test? - //QCOMPARE(spyUpload.count(), 1); + + QCOMPARE(spyUpload.count(), 1); QFile resultFile(destFile), originFile(aFile); QVERIFY(resultFile.open(QIODevice::ReadOnly)); diff --git a/tests/testdaemon.h b/tests/testdaemon.h --- a/tests/testdaemon.h +++ b/tests/testdaemon.h @@ -33,6 +33,10 @@ { } + void addDevice(Device* device) { + Daemon::addDevice(device); + } + void reportError(const QString & title, const QString & description) override { qWarning() << "error:" << title << description; @@ -50,7 +54,7 @@ return m_nam; } - Q_SCRIPTABLE virtual void sendSimpleNotification(const QString &eventId, const QString &title, const QString &text, const QString &iconName) override + Q_SCRIPTABLE virtual void sendSimpleNotification(const QString &eventId, const QString &title, const QString &text, const QString &iconName) override { qDebug() << eventId << title << text << iconName; } diff --git a/tests/testnotificationlistener.cpp b/tests/testnotificationlistener.cpp --- a/tests/testnotificationlistener.cpp +++ b/tests/testnotificationlistener.cpp @@ -136,6 +136,10 @@ m_applications = value; } + QSharedPointer iconForIconName(const QString& iconName) const { + return NotificationsListener::iconForIconName(iconName); + } + protected: bool parseImageDataArgument(const QVariant& argument, int& width, int& height, int& rowStride, int& bitsPerSample, @@ -370,7 +374,7 @@ QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); - QCOMPARE(d->getLastPacket()->payloadSize(), fi.size()); + QCOMPARE(d->getLastPacket()->payloadSize(), listener->iconForIconName(fi.baseName())->size()); // works also with absolute paths retId = listener->Notify(appName, replacesId, iconName, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); diff --git a/tests/testsslsocketlinereader.cpp b/tests/testsslsocketlinereader.cpp --- a/tests/testsslsocketlinereader.cpp +++ b/tests/testsslsocketlinereader.cpp @@ -123,10 +123,18 @@ m_clientSocket->setPeerVerifyMode(QSslSocket::VerifyPeer); m_clientSocket->addCaCertificate(serverSocket->localCertificate()); - connect(m_clientSocket, &QSslSocket::encrypted, &m_loop, &QEventLoop::quit); + int connected_sockets = 0; + auto connected_lambda = [&](){ + connected_sockets++; + if (connected_sockets >= 2) { + m_loop.quit(); + } + }; + connect(serverSocket, &QSslSocket::encrypted, connected_lambda); + connect(m_clientSocket, &QSslSocket::encrypted, connected_lambda); serverSocket->startServerEncryption(); m_clientSocket->startClientEncryption(); - m_loop.exec(); + m_loop.exec(); //Block until QEventLoop::quit gets called by the lambda // Both client and server socket should be encrypted here and should have remote certificate because VerifyPeer is used QVERIFY2(m_clientSocket->isOpen(), "Client socket already closed"); @@ -183,10 +191,18 @@ m_clientSocket->setPeerVerifyName(QStringLiteral("Test Server")); m_clientSocket->setPeerVerifyMode(QSslSocket::QueryPeer); - connect(m_clientSocket, &QSslSocket::encrypted, &m_loop, &QEventLoop::quit); + int connected_sockets = 0; + auto connected_lambda = [&](){ + connected_sockets++; + if (connected_sockets >= 2) { + m_loop.quit(); + } + }; + connect(serverSocket, &QSslSocket::encrypted, connected_lambda); + connect(m_clientSocket, &QSslSocket::encrypted, connected_lambda); serverSocket->startServerEncryption(); m_clientSocket->startClientEncryption(); - m_loop.exec(); + m_loop.exec(); //Block until QEventLoop::quit gets called by the lambda QVERIFY2(m_clientSocket->isOpen(), "Client socket already closed"); QVERIFY2(serverSocket->isOpen(), "Server socket already closed"); diff --git a/urlhandler/org.kde.kdeconnect.telhandler.desktop b/urlhandler/org.kde.kdeconnect.telhandler.desktop --- a/urlhandler/org.kde.kdeconnect.telhandler.desktop +++ b/urlhandler/org.kde.kdeconnect.telhandler.desktop @@ -9,6 +9,7 @@ Name[en_GB]=KDE Connect Phone URL Handler Name[es]=Controlador de URL de teléfonos de KDE Connect Name[eu]=KDE Connect-en telefono-URL-kudeatzailea +Name[fi]=KDE Connectin puhelinverkko-osoitekäsittelijä Name[fr]=Gestionnaire d'URL téléphoniques de KDE Connect Name[gl]=Manexador de URL de teléfono de KDE Connect Name[id]=Penanganan URL Phone KDE Connect