File indexing completed on 2024-05-19 05:55:49

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Han Young <hanyoung@protonmail.com>
0003  * SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
0004  * SPDX-FileCopyrightText: 2021 Nicolas Fella <nicolas.fella@gmx.de>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "weatherlocation.h"
0010 
0011 #include <QDir>
0012 #include <QFile>
0013 #include <QJsonArray>
0014 #include <QQmlEngine>
0015 #include <QTimeZone>
0016 #include <QTimer>
0017 
0018 #include <utility>
0019 
0020 #include "global.h"
0021 #include "kweathersettings.h"
0022 #include "locationquerymodel.h"
0023 
0024 WeatherLocation::WeatherLocation(QString locationId,
0025                                  QString locationName,
0026                                  QString timeZone,
0027                                  float latitude,
0028                                  float longitude,
0029                                  KWeatherCore::WeatherForecast forecast)
0030     : m_forecast(forecast)
0031     , m_locationName(std::move(locationName))
0032     , m_locationId(std::move(locationId))
0033     , m_timeZone(std::move(timeZone))
0034     , m_latitude(latitude)
0035     , m_longitude(longitude)
0036 {
0037     this->m_timer = new QTimer(this);
0038     connect(m_timer, &QTimer::timeout, this, &WeatherLocation::updateCurrentDateTime);
0039     this->m_timer->start(60 - QDateTime::currentDateTime().currentMSecsSinceEpoch() % 60);
0040 
0041     m_lastUpdated = forecast.createdTime();
0042     Q_EMIT lastUpdatedChanged();
0043 
0044     update();
0045 
0046     determineCurrentForecast();
0047 
0048     connect(this, &WeatherLocation::selectedDayChanged, this, [this] {
0049         m_hourForecasts.clear();
0050         if (!m_forecast.dailyWeatherForecast().empty()) {
0051             const auto hourForecasts = m_forecast.dailyWeatherForecast()[m_selectedDay].hourlyWeatherForecast();
0052             for (const KWeatherCore::HourlyWeatherForecast &hour : hourForecasts) {
0053                 m_hourForecasts << QVariant::fromValue(hour);
0054             }
0055         }
0056         Q_EMIT hourForecastsChanged();
0057     });
0058 }
0059 
0060 WeatherLocation::WeatherLocation()
0061 {
0062 }
0063 
0064 WeatherLocation *WeatherLocation::load(const QString &groupName)
0065 {
0066     auto config = KWeatherSettings::self()->config()->group(KWeather::WEATHER_LOCATIONS_CFG_GROUP).group(groupName);
0067     if (config.isValid()) {
0068         return new WeatherLocation(groupName,
0069                                    config.readEntry("locationName"),
0070                                    config.readEntry("timezone"),
0071                                    config.readEntry("latitude").toFloat(),
0072                                    config.readEntry("longitude").toFloat());
0073     } else {
0074         return nullptr;
0075     }
0076 }
0077 
0078 void WeatherLocation::save()
0079 {
0080     auto config = KWeatherSettings::self()->config()->group(KWeather::WEATHER_LOCATIONS_CFG_GROUP).group(locationId());
0081     config.writeEntry("locationName", locationName());
0082     config.writeEntry("latitude", latitude());
0083     config.writeEntry("longitude", longitude());
0084     config.writeEntry("timezone", m_timeZone);
0085     config.sync();
0086 }
0087 
0088 void WeatherLocation::saveOrder(int index)
0089 {
0090     auto config = KWeatherSettings::self()->config()->group(KWeather::WEATHER_LOCATIONS_CFG_GROUP).group(locationId());
0091     config.writeEntry("index", index);
0092     config.sync();
0093 }
0094 
0095 int WeatherLocation::index()
0096 {
0097     auto config = KWeatherSettings::self()->config()->group(KWeather::WEATHER_LOCATIONS_CFG_GROUP).group(locationId());
0098     auto res = config.readEntry("index");
0099     if (res.isEmpty()) {
0100         return -1;
0101     } else {
0102         return res.toInt();
0103     }
0104 }
0105 
0106 void WeatherLocation::deleteConfig()
0107 {
0108     auto config = KWeatherSettings::self()->config()->group(KWeather::WEATHER_LOCATIONS_CFG_GROUP);
0109     config.deleteGroup(locationId());
0110     config.sync();
0111 }
0112 
0113 const QString &WeatherLocation::locationId() const
0114 {
0115     return m_locationId;
0116 }
0117 
0118 const QString &WeatherLocation::locationName() const
0119 {
0120     return m_locationName;
0121 }
0122 
0123 const QString &WeatherLocation::timeZone() const
0124 {
0125     return m_timeZone;
0126 }
0127 
0128 float WeatherLocation::latitude() const
0129 {
0130     return m_latitude;
0131 }
0132 
0133 float WeatherLocation::longitude() const
0134 {
0135     return m_longitude;
0136 }
0137 
0138 QString WeatherLocation::lastUpdatedFormatted() const
0139 {
0140     return QLocale().toString(lastUpdated().time(), QLocale::ShortFormat).toLower();
0141 }
0142 
0143 const QDateTime &WeatherLocation::lastUpdated() const
0144 {
0145     return m_lastUpdated;
0146 }
0147 
0148 QString WeatherLocation::currentTimeFormatted() const
0149 {
0150     return QLocale().toString(currentDateTime().time(), QLocale::ShortFormat).toLower();
0151 }
0152 
0153 QString WeatherLocation::currentDateFormatted() const
0154 {
0155     return QLocale().toString(currentDateTime().toLocalTime().date(), QLocale::LongFormat);
0156 }
0157 
0158 QDateTime WeatherLocation::currentDateTime() const
0159 {
0160     return QDateTime::currentDateTime().toTimeZone(QTimeZone(m_timeZone.toUtf8()));
0161 }
0162 
0163 const QString &WeatherLocation::backgroundComponent() const
0164 {
0165     return m_backgroundComponent;
0166 }
0167 
0168 const QColor &WeatherLocation::backgroundColor() const
0169 {
0170     return m_backgroundColor;
0171 }
0172 
0173 const QColor &WeatherLocation::textColor() const
0174 {
0175     return m_textColor;
0176 }
0177 
0178 const QColor &WeatherLocation::cardBackgroundColor() const
0179 {
0180     return m_cardBackgroundColor;
0181 }
0182 
0183 const QColor &WeatherLocation::cardTextColor() const
0184 {
0185     return m_cardTextColor;
0186 }
0187 
0188 const QColor &WeatherLocation::cardSecondaryTextColor() const
0189 {
0190     return m_cardSecondaryTextColor;
0191 }
0192 
0193 const QColor &WeatherLocation::iconColor() const
0194 {
0195     return m_iconColor;
0196 }
0197 
0198 bool WeatherLocation::darkTheme() const
0199 {
0200     return m_isDarkTheme;
0201 }
0202 
0203 QVariantList WeatherLocation::dayForecasts() const
0204 {
0205     return m_dayForecasts;
0206 }
0207 
0208 QVariantList WeatherLocation::hourForecasts() const
0209 {
0210     return m_hourForecasts;
0211 }
0212 
0213 int WeatherLocation::selectedDay() const
0214 {
0215     return m_selectedDay;
0216 }
0217 
0218 void WeatherLocation::setSelectedDay(int selectedDay)
0219 {
0220     if (selectedDay != m_selectedDay) {
0221         m_selectedDay = selectedDay;
0222         Q_EMIT selectedDayChanged();
0223     }
0224 }
0225 
0226 QVariant WeatherLocation::currentHourForecast()
0227 {
0228     if (!m_forecast.dailyWeatherForecast().empty() && !m_forecast.dailyWeatherForecast()[0].hourlyWeatherForecast().empty()) {
0229         return QVariant::fromValue(m_forecast.dailyWeatherForecast().begin()->hourlyWeatherForecast()[0]);
0230     }
0231     return {};
0232 }
0233 
0234 void WeatherLocation::updateData(KWeatherCore::WeatherForecast forecasts)
0235 {
0236     m_forecast = forecasts;
0237 
0238     // first time the timezone is empty
0239     if (m_timeZone.isEmpty()) {
0240         m_timeZone = forecasts.timezone();
0241         Q_EMIT timeZoneChanged();
0242         save();
0243     }
0244 
0245     determineCurrentForecast();
0246     m_lastUpdated = forecasts.createdTime();
0247     Q_EMIT lastUpdatedChanged();
0248 
0249     m_hourForecasts.clear();
0250     if (!m_forecast.dailyWeatherForecast().empty() && m_forecast.dailyWeatherForecast().size() > m_selectedDay) {
0251         const auto hourForecasts = m_forecast.dailyWeatherForecast()[m_selectedDay].hourlyWeatherForecast();
0252         for (const KWeatherCore::HourlyWeatherForecast &hour : hourForecasts) {
0253             m_hourForecasts << QVariant::fromValue(hour);
0254         }
0255     }
0256     Q_EMIT hourForecastsChanged();
0257 
0258     m_dayForecasts.clear();
0259     for (const KWeatherCore::DailyWeatherForecast &day : m_forecast.dailyWeatherForecast()) {
0260         m_dayForecasts << QVariant::fromValue(day);
0261     }
0262     Q_EMIT dayForecastsChanged();
0263 
0264     Q_EMIT weatherRefresh(m_forecast);
0265     Q_EMIT stopLoadingIndicator();
0266 
0267     updateCurrentDateTime();
0268 }
0269 
0270 void WeatherLocation::determineCurrentForecast()
0271 {
0272     if (m_forecast.dailyWeatherForecast().empty())
0273         return;
0274 
0275     auto currentWeather = m_forecast.dailyWeatherForecast().begin()->hourlyWeatherForecast().begin();
0276     m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/ClearDay.qml");
0277 
0278     bool isDayStyle = false; // make sure that if the background is definitively day, the colours match that
0279     m_sun = false;
0280     m_rain = false;
0281     m_cloud = false;
0282     m_snow = false;
0283     m_star = false;
0284     if (currentWeather->weatherIcon() == QStringLiteral("weather-clear")) {
0285         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/ClearDay.qml");
0286         isDayStyle = true;
0287         m_topColor = {255, 193, 7}; //"#ffc107"
0288         m_bottomColor = {255, 193, 7};
0289         m_sun = true;
0290     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-clear-night")) {
0291         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/ClearNight.qml");
0292         m_topColor = {69, 90, 100}; // "#455a64"
0293         m_bottomColor = {38, 50, 56}; // "#263238"
0294         m_star = true;
0295     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-clouds")) {
0296         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/CloudyDay.qml");
0297         isDayStyle = true;
0298         m_topColor = {0, 188, 212}; // #00bcd4
0299         m_bottomColor = {36, 163, 222}; // #24a3de
0300         m_cloudColor = {224, 247, 250}; // #e0f7fa
0301         m_cloud = true;
0302     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-clouds-night") || currentWeather->weatherIcon() == QStringLiteral("weather-overcast")) {
0303         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/CloudyNight.qml");
0304         m_topColor = {69, 90, 100}; // #455a64
0305         m_bottomColor = {38, 50, 56}; // #263238
0306         m_cloudColor = {176, 190, 197}; // #b0bec5
0307         m_star = true;
0308         m_cloud = true;
0309     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-few-clouds")) {
0310         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/PartlyCloudyDay.qml");
0311         isDayStyle = true;
0312         m_topColor = {0, 188, 212}; // #00bcd4
0313         m_bottomColor = {36, 163, 222}; // #24a3de
0314         m_cloudColor = {224, 247, 250}; // #e0f7fa
0315         m_cloud = true;
0316     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-few-clouds-night")) {
0317         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/PartlyCloudyNight.qml");
0318         m_topColor = {69, 90, 100}; // "#455a64"
0319         m_bottomColor = {38, 50, 56}; // "#263238"
0320         m_cloudColor = {176, 190, 197}; // #b0bec5
0321         m_star = true;
0322         m_cloud = true;
0323     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-fog") || currentWeather->weatherIcon() == QStringLiteral("weather-mist")) {
0324         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/Misty.qml");
0325         isDayStyle = true;
0326         m_topColor = {194, 197, 203}; // #c2c5cb
0327         m_bottomColor = {91, 96, 107}; // #5b606b
0328         m_cloudColor = {218, 224, 236}; // #dae0ec
0329         m_cloud = true;
0330 
0331     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-freezing-rain") || currentWeather->weatherIcon() == QStringLiteral("weather-snow-hail")
0332                || currentWeather->weatherIcon() == QStringLiteral("weather-showers") || currentWeather->weatherIcon() == QStringLiteral("weather-showers-day")
0333                || currentWeather->weatherIcon() == QStringLiteral("weather-showers-scattered")
0334                || currentWeather->weatherIcon() == QStringLiteral("weather-showers-scattered-day")
0335                || currentWeather->weatherIcon() == QStringLiteral("weather-storm") || currentWeather->weatherIcon() == QStringLiteral("weather-storm-day")) {
0336         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/RainyDay.qml");
0337         isDayStyle = true;
0338         m_topColor = {54, 156, 203}; // #369ccb
0339         m_bottomColor = {26, 131, 179}; // #1A83B3
0340         m_cloudColor = {255, 255, 255}; // white
0341         m_cloud = true;
0342         m_rain = true;
0343     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-showers-night")
0344                || currentWeather->weatherIcon() == QStringLiteral("weather-showers-scattered-night")
0345                || currentWeather->weatherIcon() == QStringLiteral("weather-storm-night")) {
0346         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/RainyNight.qml");
0347         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/CloudyNight.qml");
0348         m_topColor = {69, 90, 100}; // #455a64
0349         m_bottomColor = {38, 50, 56}; // #263238
0350         m_cloudColor = {176, 190, 197}; // #b0bec5
0351         m_cloud = true;
0352         m_rain = true;
0353     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-hail") || currentWeather->weatherIcon() == QStringLiteral("weather-snow-scattered")
0354                || currentWeather->weatherIcon() == QStringLiteral("weather-snow")) {
0355         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/SnowyDay.qml");
0356         isDayStyle = true;
0357         m_topColor = {53, 217, 237}; // #35d9ed
0358         m_bottomColor = {36, 163, 222}; // #24a3de
0359         m_snow = true;
0360     } else if (currentWeather->weatherIcon() == QStringLiteral("weather-snow-scattered-night")) {
0361         m_backgroundComponent = QStringLiteral("qrc:/qt/qml/org/kde/kweather/backgrounds/qml/backgrounds/SnowyNight.qml");
0362         m_topColor = {69, 90, 100}; // #455a64
0363         m_bottomColor = {38, 50, 56}; // #263238
0364         m_snow = true;
0365     }
0366 
0367     if (isDayStyle) {
0368         m_cardBackgroundColor = QStringLiteral("#fefefe");
0369         m_cardTextColor = QStringLiteral("black");
0370         m_cardSecondaryTextColor = QStringLiteral("#404040");
0371         m_backgroundColor = QStringLiteral("#3daee2");
0372         m_textColor = m_cardTextColor;
0373         m_iconColor = QStringLiteral("#4d4d4d");
0374         m_isDarkTheme = false;
0375     } else {
0376         m_cardBackgroundColor = QStringLiteral("#333333");
0377         m_cardTextColor = QStringLiteral("#eeeeee");
0378         m_cardSecondaryTextColor = QStringLiteral("#C2C2C2");
0379         m_backgroundColor = QStringLiteral("#222222");
0380         m_textColor = m_cardTextColor;
0381         m_iconColor = QStringLiteral("white");
0382         m_isDarkTheme = true;
0383     }
0384 
0385     Q_EMIT currentForecastChanged();
0386 }
0387 
0388 void WeatherLocation::update()
0389 {
0390     auto pendingForecast = m_source.requestData(latitude(), longitude());
0391     connect(pendingForecast, &KWeatherCore::PendingWeatherForecast::finished, [this, pendingForecast] {
0392         this->updateData(pendingForecast->value());
0393         pendingForecast->deleteLater();
0394     });
0395 }
0396 
0397 void WeatherLocation::updateCurrentDateTime()
0398 {
0399     m_timer->setInterval(60000);
0400     Q_EMIT currentTimeChanged();
0401     Q_EMIT currentDateChanged();
0402 }
0403 
0404 const QColor &WeatherLocation::topColor() const
0405 {
0406     return m_topColor;
0407 }
0408 const QColor &WeatherLocation::bottomColor() const
0409 {
0410     return m_bottomColor;
0411 }
0412 const QColor &WeatherLocation::cloudColor() const
0413 {
0414     return m_cloudColor;
0415 }
0416 bool WeatherLocation::rain() const
0417 {
0418     return m_rain;
0419 }
0420 bool WeatherLocation::cloud() const
0421 {
0422     return m_cloud;
0423 }
0424 bool WeatherLocation::star() const
0425 {
0426     return m_star;
0427 }
0428 bool WeatherLocation::sun() const
0429 {
0430     return m_sun;
0431 }
0432 bool WeatherLocation::snow() const
0433 {
0434     return m_snow;
0435 }
0436 
0437 #include "moc_weatherlocation.cpp"