diff --git a/CMakeLists.txt b/CMakeLists.txt index 55cf5c5c..6d2f1468 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,127 +1,127 @@ cmake_minimum_required(VERSION 3.0) project(kdeconnect) if (SAILFISHOS) set(KF5_MIN_VERSION "5.36.0") set(QT_MIN_VERSION "5.6.0") else() set(KF5_MIN_VERSION "5.64.0") set(QT_MIN_VERSION "5.10.0") endif() set(QCA_MIN_VERSION "2.1.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMAddTests) include(ECMSetupVersion) include(ECMInstallIcons) include(FeatureSummary) include(GenerateExportHeader) if (NOT SAILFISHOS) include(ECMQMLModules) endif() include(KDEConnectMacros.cmake) ecm_setup_version(1.4.0 VARIABLE_PREFIX KDECONNECT VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/kdeconnect-version.h ) if (SAILFISHOS) set(KF5_REQUIRED_COMPONENTS I18n CoreAddons Config) set(KF5_OPTIONAL_COMPONENTS) set(Qca_LIBRARY CONAN_PKG::Qca-qt5) add_definitions(-DSAILFISHOS) else() find_package(Qca-qt5 ${QCA_MIN_VERSION} REQUIRED) set(Qca_LIBRARY qca-qt5) - set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Kirigami2 People) + set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Solid Kirigami2 People) set(KF5_OPTIONAL_COMPONENTS DocTools) 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 ) if(UNIX AND NOT APPLE) find_package(KF5PulseAudioQt) endif() find_package(KF5PeopleVCard) set_package_properties(KF5PeopleVCard PROPERTIES PURPOSE "Read vcards from the file system" URL "https://phabricator.kde.org/source/kpeoplevcard/" TYPE RUNTIME ) add_definitions(-DHAVE_KIO) ecm_find_qmlmodule(org.kde.people 1.0) ecm_find_qmlmodule(QtQuick.Particles 2.0) endif() add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS -DQT_NO_CAST_FROM_ASCII) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS DBus Quick Network Multimedia) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS ${KF5_REQUIRED_COMPONENTS}) if (KF5_OPTIONAL_COMPONENTS) find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS ${KF5_OPTIONAL_COMPONENTS}) endif() if (NOT ZSH_AUTOCOMPLETE_DIR) set(ZSH_AUTOCOMPLETE_DIR "${CMAKE_INSTALL_PREFIX}/share/zsh/site-functions") endif() option(PRIVATE_DBUS_ENABLED "Use private dbus session for kdeconnect" OFF) if(PRIVATE_DBUS_ENABLED OR APPLE) add_compile_definitions(USE_PRIVATE_DBUS) endif() add_subdirectory(core) add_subdirectory(plugins) add_subdirectory(interfaces) if (NOT SAILFISHOS) add_subdirectory(icon) add_subdirectory(data) add_subdirectory(cli) add_subdirectory(declarativeplugin) add_subdirectory(kcm) add_subdirectory(kcmplugin) add_subdirectory(daemon) add_subdirectory(app) add_subdirectory(indicator) add_subdirectory(urlhandler) add_subdirectory(nautilus-extension) add_subdirectory(fileitemactionplugin) add_subdirectory(smsapp) add_subdirectory(settings) if(NOT WIN32) add_subdirectory(kio) add_subdirectory(plasmoid) endif() endif() if(KF5DocTools_FOUND) add_subdirectory(doc) endif() if(BUILD_TESTING) add_subdirectory(tests) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/plugins/battery/CMakeLists.txt b/plugins/battery/CMakeLists.txt index 4c471d98..00c5477b 100644 --- a/plugins/battery/CMakeLists.txt +++ b/plugins/battery/CMakeLists.txt @@ -1,12 +1,13 @@ set(kdeconnect_battery_SRCS batteryplugin.cpp batterydbusinterface.cpp ) kdeconnect_add_plugin(kdeconnect_battery JSON kdeconnect_battery.json SOURCES ${kdeconnect_battery_SRCS}) target_link_libraries(kdeconnect_battery kdeconnectcore Qt5::DBus + KF5::Solid KF5::I18n ) diff --git a/plugins/battery/README b/plugins/battery/README index 8812ba1a..6b28dd38 100644 --- a/plugins/battery/README +++ b/plugins/battery/README @@ -1,23 +1,25 @@ This plugins receives packages with type "kdeconnect.battery" and reads the following fields: isCharging (boolean): If the battery of the peer device is charging currentCharge (int): The charge % of the peer device thresholdEvent (int) [optional when = 0, see below]: means that a battery threshold event were fired on the remote device: 0 := no event. generally not transmitted. 1 := battery entered in low state This is an int so in the future we'll be able to subscribe to more events. (see BatteryPlugin.ThresholdBatteryEvent) - Symmetrically, it sends its own battery information in packages with the same -format. +format. We only look for the 'primary' battery of the system, if one is present. + + +Support for devices with multiple batteries isn't there yet. It also sends packages with type kdeconnect.battery and a field "request": true, to ask the peer device to send a package like the mentioned above, and should also answer this same kind of packages with its own information. If the battery is low and discharging, it will notify the user. diff --git a/plugins/battery/batteryplugin.cpp b/plugins/battery/batteryplugin.cpp index 5371be56..dc79e04a 100644 --- a/plugins/battery/batteryplugin.cpp +++ b/plugins/battery/batteryplugin.cpp @@ -1,81 +1,155 @@ /** * Copyright 2013 Albert Vaca * * 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 "batteryplugin.h" #include #include +#include +#include +#include + #include #include "batterydbusinterface.h" K_PLUGIN_CLASS_WITH_JSON(BatteryPlugin, "kdeconnect_battery.json") Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_BATTERY, "kdeconnect.plugin.battery") BatteryPlugin::BatteryPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , batteryDbusInterface(new BatteryDbusInterface(device())) { - //TODO: Add battery reporting, could be based on: - // https://www.linux-apps.com/content/show.php/battery+plasmoid+with+remaining+time?content=120309 + //TODO: Our protocol should support a dynamic number of batteries. + //Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); } void BatteryPlugin::connected() { NetworkPacket np(PACKET_TYPE_BATTERY_REQUEST, {{QStringLiteral("request"),true}}); sendPacket(np); + + const auto batteryDevice = Solid::DeviceInterface::Type::Battery; + const auto primary = Solid::Battery::BatteryType::PrimaryBattery; + + QList batteries = Solid::Device::listFromQuery(Solid::Predicate(batteryDevice, QStringLiteral("type"), primary)); + + if (batteries.isEmpty()) { + qCWarning(KDECONNECT_PLUGIN_BATTERY) << "No Primary Battery detected on this system. This may be a bug."; + QList allBatteries = Solid::Device::listFromType(batteryDevice); + qCWarning(KDECONNECT_PLUGIN_BATTERY) << "Total quantity of batteries found: " << allBatteries.size(); + return; + } + + const Solid::Battery* chosen = batteries.first().as(); + + connect(chosen, &Solid::Battery::chargeStateChanged, this, &BatteryPlugin::chargeChanged); + connect(chosen, &Solid::Battery::chargePercentChanged, this, &BatteryPlugin::chargeChanged); + + // Explicitly send the current charge + chargeChanged(); +} + +void BatteryPlugin::chargeChanged() +{ + + bool isAnyBatteryCharging = false; + int batteryQuantity = 0; + int cumulativeCharge = 0; + + const auto batteryDevice = Solid::DeviceInterface::Type::Battery; + const auto primary = Solid::Battery::BatteryType::PrimaryBattery; + + QList batteries = Solid::Device::listFromQuery(Solid::Predicate(batteryDevice, QStringLiteral("type"), primary)); + + for (auto device : batteries) { + const Solid::Battery* battery = device.as(); + + // Don't look at batteries that have been detached + if (battery->isPresent()) { + batteryQuantity++; + cumulativeCharge += battery->chargePercent(); + if (battery->chargeState() == Solid::Battery::ChargeState::Charging) { + isAnyBatteryCharging = true; + } + } + } + + if (batteryQuantity == 0) { + qCWarning(KDECONNECT_PLUGIN_BATTERY) << "Primary Battery seems to have been removed. Suspending packets until it is reconnected."; + return; + } + + // Load a new Battery object to represent the first device in the list + Solid::Battery* chosen = batteries.first().as(); + + // Prepare an outgoing network packet + NetworkPacket status(PACKET_TYPE_BATTERY, {{}}); + status.set(QStringLiteral("isCharging"), isAnyBatteryCharging); + status.set(QStringLiteral("currentCharge"), cumulativeCharge / batteryQuantity); + // FIXME: In future, we should consider sending an array of battery objects + status.set(QStringLiteral("batteryQuantity"), batteryQuantity); + // We consider primary battery to be low if it will only last for 5 minutes or + // less. This doesn't necessarily work if (for example) Solid finds multiple + // batteries. + if (chosen->remainingTime() < 600 && chosen->chargeState() == Solid::Battery::ChargeState::Discharging) { + status.set(QStringLiteral("thresholdEvent"), (int)ThresholdBatteryLow); + } else { + status.set(QStringLiteral("thresholdEvent"), (int)ThresholdNone); + } + sendPacket(status); } BatteryPlugin::~BatteryPlugin() { //FIXME: Qt dbus does not allow to remove an adaptor! (it causes a crash in // the next dbus access to its parent). The implication of not deleting this // is that disabling the plugin does not remove the interface (that will // return outdated values) and that enabling it again instantiates a second // adaptor. This is also a memory leak until the entire device is destroyed. //batteryDbusInterface->deleteLater(); } bool BatteryPlugin::receivePacket(const NetworkPacket& np) { bool isCharging = np.get(QStringLiteral("isCharging"), false); int currentCharge = np.get(QStringLiteral("currentCharge"), -1); int thresholdEvent = np.get(QStringLiteral("thresholdEvent"), (int)ThresholdNone); if (batteryDbusInterface->charge() != currentCharge || batteryDbusInterface->isCharging() != isCharging ) { batteryDbusInterface->updateValues(isCharging, currentCharge); } if ( thresholdEvent == ThresholdBatteryLow && !isCharging ) { Daemon::instance()->sendSimpleNotification(QStringLiteral("batteryLow"), i18nc("device name: low battery", "%1: Low Battery", device()->name()), i18n("Battery at %1%", currentCharge), QStringLiteral("battery-040")); } return true; } #include "batteryplugin.moc" diff --git a/plugins/battery/batteryplugin.h b/plugins/battery/batteryplugin.h index 5eed972f..c631e88d 100644 --- a/plugins/battery/batteryplugin.h +++ b/plugins/battery/batteryplugin.h @@ -1,56 +1,62 @@ /** * Copyright 2013 Albert Vaca * * 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 BATTERYPLUGIN_H #define BATTERYPLUGIN_H #include #include +#define PACKET_TYPE_BATTERY QStringLiteral("kdeconnect.battery") #define PACKET_TYPE_BATTERY_REQUEST QStringLiteral("kdeconnect.battery.request") Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_BATTERY) class BatteryDbusInterface; class BatteryPlugin : public KdeConnectPlugin { Q_OBJECT public: explicit BatteryPlugin(QObject* parent, const QVariantList& args); ~BatteryPlugin() override; bool receivePacket(const NetworkPacket& np) override; void connected() override; + // NB: This may be connected to zero or two signals in Solid::Battery - + // chargePercentageChanged and chargeStatusChanged. + // See inline comments for further details + void chargeChanged(); + private: // Keep these values in sync with THRESHOLD* constants in // kdeconnect-android:BatteryPlugin.java // see README for their meaning enum ThresholdBatteryEvent { ThresholdNone = 0, ThresholdBatteryLow = 1 }; BatteryDbusInterface* batteryDbusInterface; }; #endif diff --git a/plugins/battery/kdeconnect_battery.json b/plugins/battery/kdeconnect_battery.json index 84079c4a..cc2a2b7d 100644 --- a/plugins/battery/kdeconnect_battery.json +++ b/plugins/battery/kdeconnect_battery.json @@ -1,139 +1,141 @@ { "KPlugin": { "Authors": [ { "Email": "albertvaka@gmail.com", "Name": "Albert Vaca", "Name[ar]": "Albert Vaca", "Name[ast]": "Albert Vaca", "Name[ca@valencia]": "Albert Vaca", "Name[ca]": "Albert Vaca", "Name[cs]": "Albert Vaca", "Name[da]": "Albert Vaca", "Name[de]": "Albert Vaca", "Name[el]": "Albert Vaca", "Name[en_GB]": "Albert Vaca", "Name[es]": "Albert Vaca", "Name[et]": "Albert Vaca", "Name[eu]": "Albert Vaca", "Name[fi]": "Albert Vaca", "Name[fr]": "Albert Vaca", "Name[gl]": "Albert Vaca", "Name[id]": "Albert Vaca", "Name[it]": "Albert Vaca", "Name[ko]": "Albert Vaca", "Name[lt]": "Albert Vaca", "Name[nl]": "Albert Vaca", "Name[nn]": "Albert Vaca", "Name[pl]": "Albert Vaca", "Name[pt]": "Albert Vaca", "Name[pt_BR]": "Albert Vaca", "Name[ru]": "Albert Vaca", "Name[sk]": "Albert Vaca", "Name[sr@ijekavian]": "Алберт Вака Синтора", "Name[sr@ijekavianlatin]": "Albert Vaka Sintora", "Name[sr@latin]": "Albert Vaka Sintora", "Name[sr]": "Алберт Вака Синтора", "Name[sv]": "Albert Vaca", "Name[tr]": "Albert Vaca", "Name[uk]": "Albert Vaca", "Name[x-test]": "xxAlbert Vacaxx", "Name[zh_CN]": "Albert Vaca", "Name[zh_TW]": "Albert Vaca" } ], "Description": "Show your phone battery next to your computer battery", "Description[ar]": "أظهر بطّاريّة الهاتف بجانب بطّاريّة الحاسوب", "Description[ast]": "Amuesa la batería del preséu al llau de la del ordenador", "Description[ca@valencia]": "Mostra la bateria del telèfon al costat de la bateria de l'ordinador", "Description[ca]": "Mostra la bateria del telèfon al costat de la bateria de l'ordinador", "Description[cs]": "Zobrazit baterii vedle baterie počítače", "Description[da]": "Vis din telefons batteri ved siden af dit computerbatteri", "Description[de]": "Zeigt den Akku Ihres Telefons neben dem Akku des Rechners", "Description[el]": "Εμφάνιση μπαταρίας συσκευής δίπλα στη μπαταρία του υπολογιστή", "Description[en_GB]": "Show your phone battery next to your computer battery", "Description[es]": "Mostrar la batería del teléfono junto a la batería del equipo", "Description[et]": "Telefoniaku näitamine otse arvutiaku kõrval", "Description[eu]": "Erakutsi telefonoaren bateria ordenagailuaren bateriaren ondoan", "Description[fi]": "Näytä puhelimesi akku tietokoneen akun rinnalla", "Description[fr]": "Affichez la batterie de votre téléphone à côté de la batterie de votre ordinateur", "Description[gl]": "Mostra o nivel de batería do teléfono canda o nivel de batería do computador", "Description[hu]": "A telefon akkumulátorának megjelenítése a számítógépé mellett", "Description[id]": "Tampilkan baterai teleponmu di sebelah baterai komputermu", "Description[it]": "Mostra la batteria del telefono accanto a quella del computer", "Description[ja]": "コンピュータのバッテリの次に端末のバッテリーを表示", "Description[ko]": "컴퓨터 배터리와 휴대폰 배터리 같이 표시", "Description[lt]": "Rodyti telefono akumuliatorių šalia kompiuterio akumuliatoriaus", "Description[nl]": "Uw telefoonbatterij naast uw computerbatterij tonen", "Description[nn]": "Vis telefonbatteriet ved sida av datamaskinbatteriet", "Description[pl]": "Pokaż baterię swojego telefonu obok baterii komputera", "Description[pt]": "Mostrar a bateria do seu telefone ao lado da do seu computador", "Description[pt_BR]": "Mostra a bateria do seu celular ao lado da bateria do computador", "Description[ru]": "Показ уровня заряда батареи телефона рядом с индикатором батареи компьютера", "Description[sk]": "Zobrazenie batérie vášho telefónu spolu s batériou počítača", "Description[sr@ijekavian]": "Приказ батерије телефона у системској касети", "Description[sr@ijekavianlatin]": "Prikaz baterije telefona u sistemskoj kaseti", "Description[sr@latin]": "Prikaz baterije telefona u sistemskoj kaseti", "Description[sr]": "Приказ батерије телефона у системској касети", "Description[sv]": "Visa telefonens batteri intill datorbatteriet", "Description[tr]": "Bilgisayar pilinizin yanında telefon pil durumunu göster", "Description[uk]": "Показ даних щодо рівня заряду акумулятора на телефоні поряд з даними щодо рівня заряду акумулятора комп’ютера", "Description[x-test]": "xxShow your phone battery next to your computer batteryxx", "Description[zh_CN]": "在计算机电池旁显示手机电池", "Description[zh_TW]": "在您的電腦電池電量旁邊顯示手機電池電量", "EnabledByDefault": true, "Icon": "preferences-system-power-management", "Id": "kdeconnect_battery", "License": "GPL", "Name": "Battery monitor", "Name[ar]": "مرقاب البطّاريّة", "Name[ca@valencia]": "Monitor de la bateria", "Name[ca]": "Monitor de la bateria", "Name[cs]": "Monitor baterie", "Name[da]": "Batteriovervågning", "Name[de]": "Akkuüberwachung", "Name[el]": "Παρακολούθηση μπαταρίας", "Name[en_GB]": "Battery monitor", "Name[es]": "Monitor de batería", "Name[et]": "Aku jälgija", "Name[eu]": "Bateriaren monitorea", "Name[fi]": "Akkuvalvonta", "Name[fr]": "Moniteur de batterie", "Name[gl]": "Monitor da batería", "Name[hu]": "Akkumulátorfigyelő", "Name[ia]": "Controlator de batteria", "Name[id]": "Battery monitor", "Name[it]": "Monitor della batteria", "Name[ja]": "バッテリーモニター", "Name[ko]": "배터리 모니터", "Name[lt]": "Akumuliatoriaus prižiūryklė", "Name[nl]": "Batterijmonitor", "Name[nn]": "Batteriovervaking", "Name[pl]": "Monitor baterii", "Name[pt]": "Monitor da bateria", "Name[pt_BR]": "Monitor de bateria", "Name[ru]": "Индикатор батареи", "Name[sk]": "Monitor batérie", "Name[sr@ijekavian]": "Надзор батерије", "Name[sr@ijekavianlatin]": "Nadzor baterije", "Name[sr@latin]": "Nadzor baterije", "Name[sr]": "Надзор батерије", "Name[sv]": "Batteriövervakare", "Name[tr]": "Pil izleyici", "Name[uk]": "Монітор акумулятора", "Name[x-test]": "xxBattery monitorxx", "Name[zh_CN]": "电池监视器", "Name[zh_TW]": "電池監視器", "ServiceTypes": [ "KdeConnect/Plugin" ], "Version": "0.1", "Website": "https://albertvaka.wordpress.com" }, "X-KdeConnect-OutgoingPacketType": [ + "kdeconnect.battery", "kdeconnect.battery.request" ], "X-KdeConnect-SupportedPacketType": [ - "kdeconnect.battery" + "kdeconnect.battery", + "kdeconnect.battery.request" ] }