File indexing completed on 2024-05-19 05:37:57

0001 /*
0002     SPDX-FileCopyrightText: 2007-2009 Shawn Starr <shawn.starr@rogers.com>
0003     SPDX-FileCopyrightText: 2009 Aaron Seigo <aseigo@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "weatherengine.h"
0009 
0010 #include <KPluginMetaData>
0011 #include <KSycoca>
0012 
0013 #include <Plasma5Support/DataContainer>
0014 #include <Plasma5Support/PluginLoader>
0015 
0016 #include "weatherenginedebug.h"
0017 
0018 // Constructor
0019 WeatherEngine::WeatherEngine(QObject *parent)
0020     : Plasma5Support::DataEngine(parent)
0021 {
0022     m_reconnectTimer.setSingleShot(true);
0023     connect(&m_reconnectTimer, &QTimer::timeout, this, &WeatherEngine::startReconnect);
0024 
0025     // Globally notify all plugins to remove their sources (and unload plugin)
0026     connect(this, &Plasma5Support::DataEngine::sourceRemoved, this, &WeatherEngine::removeIonSource);
0027 
0028     QNetworkInformation::load(QNetworkInformation::Feature::Reachability);
0029     connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, &WeatherEngine::onOnlineStateChanged);
0030 
0031     // Get the list of available plugins but don't load them
0032     connect(KSycoca::self(), &KSycoca::databaseChanged, this, &WeatherEngine::updateIonList);
0033 
0034     updateIonList();
0035 }
0036 
0037 // Destructor
0038 WeatherEngine::~WeatherEngine()
0039 {
0040 }
0041 
0042 /* FIXME: Q_PROPERTY functions to update the list of available plugins */
0043 
0044 void WeatherEngine::updateIonList()
0045 {
0046     removeAllData(QStringLiteral("ions"));
0047     const auto infos = Plasma5Support::PluginLoader::listDataEngineMetaData(QStringLiteral("weatherengine"));
0048     for (const KPluginMetaData &info : infos) {
0049         // We want to provide the short ion name, but pluginId is the full plugin library name
0050         const QString ionName = info.pluginId().split(QLatin1Char('_')).last();
0051         const QString data = QStringLiteral("%1|%2").arg(info.name()).arg(ionName);
0052         setData(QStringLiteral("ions"), info.pluginId(), data);
0053     }
0054 }
0055 
0056 /**
0057  * SLOT: Remove the datasource from the ion and unload plugin if needed
0058  */
0059 void WeatherEngine::removeIonSource(const QString &source)
0060 {
0061     QString ionName;
0062     IonInterface *ion = ionForSource(source, &ionName);
0063     if (ion) {
0064         ion->removeSource(source);
0065 
0066         // track used ions
0067         QHash<QString, int>::Iterator it = m_ionUsage.find(ionName);
0068 
0069         if (it == m_ionUsage.end()) {
0070             qCWarning(WEATHER) << "Removing ion source without being added before:" << source;
0071         } else {
0072             // no longer used?
0073             if (it.value() <= 1) {
0074                 // forget about it
0075                 m_ionUsage.erase(it);
0076                 disconnect(ion, &IonInterface::forceUpdate, this, &WeatherEngine::forceUpdate);
0077                 qCDebug(WEATHER) << "Ion no longer used as source:" << ionName;
0078             } else {
0079                 --(it.value());
0080             }
0081         }
0082     } else {
0083         qCWarning(WEATHER) << "Could not find ion to remove source for:" << source;
0084     }
0085 }
0086 
0087 /**
0088  * SLOT: Push out new data to applet
0089  */
0090 void WeatherEngine::dataUpdated(const QString &source, const Plasma5Support::DataEngine::Data &data)
0091 {
0092     qCDebug(WEATHER) << "dataUpdated() for:" << source;
0093     setData(source, data);
0094 }
0095 
0096 /**
0097  * SLOT: Set up each Ion for the first time and get any data
0098  */
0099 bool WeatherEngine::sourceRequestEvent(const QString &source)
0100 {
0101     QString ionName;
0102     IonInterface *ion = ionForSource(source, &ionName);
0103 
0104     if (!ion) {
0105         qCWarning(WEATHER) << "Could not find ion to request source for:" << source;
0106         return false;
0107     }
0108 
0109     // track used ions
0110     QHash<QString, int>::Iterator it = m_ionUsage.find(ionName);
0111     if (it == m_ionUsage.end()) {
0112         m_ionUsage.insert(ionName, 1);
0113         connect(ion, &IonInterface::forceUpdate, this, &WeatherEngine::forceUpdate);
0114         qCDebug(WEATHER) << "Ion now used as source:" << ionName;
0115     } else {
0116         ++(*it);
0117     }
0118 
0119     // we should connect to the ion anyway, even if the network
0120     // is down. when it comes up again, then it will be refreshed
0121     ion->connectSource(source, this);
0122 
0123     qCDebug(WEATHER) << "sourceRequestEvent(): Network is: " << QNetworkInformation::instance()->reachability();
0124     if (QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online) {
0125         setData(source, Data());
0126         return true;
0127     }
0128 
0129     if (!containerForSource(source)) {
0130         // it is an async reply, we need to set up the data anyways
0131         setData(source, Data());
0132     }
0133 
0134     return true;
0135 }
0136 
0137 /**
0138  * SLOT: update the Applet with new data from all ions loaded.
0139  */
0140 bool WeatherEngine::updateSourceEvent(const QString &source)
0141 {
0142     qCDebug(WEATHER) << "updateSourceEvent(): Network is: " << QNetworkInformation::instance()->reachability();
0143     if (QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online) {
0144         return false;
0145     }
0146 
0147     IonInterface *ion = ionForSource(source);
0148     if (!ion) {
0149         qCWarning(WEATHER) << "Could not find ion to update source for:" << source;
0150         return false;
0151     }
0152 
0153     return ion->updateSourceEvent(source);
0154 }
0155 
0156 void WeatherEngine::onOnlineStateChanged(QNetworkInformation::Reachability reachability)
0157 {
0158     if (reachability == QNetworkInformation::Reachability::Online) {
0159         qCDebug(WEATHER) << "starting m_reconnectTimer";
0160         // allow the network to settle down and actually come up
0161         m_reconnectTimer.start(1000);
0162     } else {
0163         m_reconnectTimer.stop();
0164     }
0165 }
0166 
0167 void WeatherEngine::startReconnect()
0168 {
0169     for (QHash<QString, int>::ConstIterator it = m_ionUsage.constBegin(); it != m_ionUsage.constEnd(); ++it) {
0170         const QString &ionName = it.key();
0171         IonInterface *ion = qobject_cast<IonInterface *>(dataEngine(ionName));
0172 
0173         if (ion) {
0174             qCDebug(WEATHER) << "Resetting ion" << ion;
0175             ion->reset();
0176         } else {
0177             qCWarning(WEATHER) << "Could not find ion to reset:" << ionName;
0178         }
0179     }
0180 }
0181 
0182 void WeatherEngine::forceUpdate(IonInterface *ion, const QString &source)
0183 {
0184     Q_UNUSED(ion);
0185     Plasma5Support::DataContainer *container = containerForSource(source);
0186     if (container) {
0187         qCDebug(WEATHER) << "immediate update of" << source;
0188         container->forceImmediateUpdate();
0189     } else {
0190         qCWarning(WEATHER) << "inexplicable failure of" << source;
0191     }
0192 }
0193 
0194 IonInterface *WeatherEngine::ionForSource(const QString &source, QString *ionName)
0195 {
0196     const int offset = source.indexOf(QLatin1Char('|'));
0197 
0198     if (offset < 1) {
0199         return nullptr;
0200     }
0201 
0202     const QString name = source.left(offset);
0203 
0204     IonInterface *result = qobject_cast<IonInterface *>(dataEngine(name));
0205 
0206     if (result && ionName) {
0207         *ionName = name.split(QLatin1Char('_')).last();
0208     }
0209 
0210     return result;
0211 }
0212 
0213 K_PLUGIN_CLASS_WITH_JSON(WeatherEngine, "plasma-dataengine-weather.json")
0214 
0215 #include "weatherengine.moc"