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"