File indexing completed on 2024-12-22 05:13:39

0001 /*
0002     SPDX-FileCopyrightText: 2013-2016 Ivan Cukic <ivan.cukic(at)kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "activitiescache_p.h"
0008 #include "manager_p.h"
0009 
0010 #include <mutex>
0011 
0012 #include <QString>
0013 
0014 #include "mainthreadexecutor_p.h"
0015 
0016 namespace KActivities
0017 {
0018 static QString nulluuid = QStringLiteral("00000000-0000-0000-0000-000000000000");
0019 
0020 using kamd::utils::Mutable;
0021 
0022 std::shared_ptr<ActivitiesCache> ActivitiesCache::self()
0023 {
0024     static std::weak_ptr<ActivitiesCache> s_instance;
0025     static std::mutex singleton;
0026     std::lock_guard<std::mutex> singleton_lock(singleton);
0027 
0028     auto result = s_instance.lock();
0029 
0030     if (s_instance.expired()) {
0031         runInMainThread([&result] {
0032             result.reset(new ActivitiesCache());
0033             s_instance = result;
0034         });
0035     }
0036 
0037     return result;
0038 }
0039 
0040 ActivitiesCache::ActivitiesCache()
0041     : m_status(Consumer::NotRunning)
0042 {
0043     // qDebug() << "ActivitiesCache: Creating a new instance";
0044     using org::kde::ActivityManager::Activities;
0045 
0046     auto activities = Manager::self()->activities();
0047 
0048     connect(activities, &Activities::ActivityAdded, this, &ActivitiesCache::updateActivity);
0049     connect(activities, &Activities::ActivityChanged, this, &ActivitiesCache::updateActivity);
0050     connect(activities, &Activities::ActivityRemoved, this, &ActivitiesCache::removeActivity);
0051 
0052     connect(activities, &Activities::ActivityStateChanged, this, &ActivitiesCache::updateActivityState);
0053     connect(activities, &Activities::ActivityNameChanged, this, &ActivitiesCache::setActivityName);
0054     connect(activities, &Activities::ActivityDescriptionChanged, this, &ActivitiesCache::setActivityDescription);
0055     connect(activities, &Activities::ActivityIconChanged, this, &ActivitiesCache::setActivityIcon);
0056 
0057     connect(activities, &Activities::CurrentActivityChanged, this, &ActivitiesCache::setCurrentActivity);
0058 
0059     connect(Manager::self(), &Manager::serviceStatusChanged, this, &ActivitiesCache::setServiceStatus);
0060 
0061     // These are covered by ActivityStateChanged
0062     // signal void org.kde.ActivityManager.Activities.ActivityStarted(QString activity)
0063     // signal void org.kde.ActivityManager.Activities.ActivityStopped(QString activity)
0064 
0065     setServiceStatus(Manager::self()->isServiceRunning());
0066 }
0067 
0068 void ActivitiesCache::setServiceStatus(bool status)
0069 {
0070     // qDebug() << "Setting service status to:" << status;
0071     loadOfflineDefaults();
0072 
0073     if (status) {
0074         updateAllActivities();
0075     }
0076 }
0077 
0078 void ActivitiesCache::loadOfflineDefaults()
0079 {
0080     m_status = Consumer::NotRunning;
0081 
0082     m_activities.clear();
0083     m_activities << ActivityInfo(nulluuid, QString(), QString(), QString(), Info::Running);
0084     m_currentActivity = nulluuid;
0085 
0086     Q_EMIT serviceStatusChanged(m_status);
0087     Q_EMIT activityListChanged();
0088 }
0089 
0090 ActivitiesCache::~ActivitiesCache()
0091 {
0092     // qDebug() << "ActivitiesCache: Destroying the instance";
0093 }
0094 
0095 void ActivitiesCache::removeActivity(const QString &id)
0096 {
0097     // qDebug() << "Removing the activity";
0098 
0099     // Since we are sorting the activities by name now,
0100     // we can not use lower_bound to search for an activity
0101     // with a specified id
0102     const auto where = find(id);
0103 
0104     if (where != m_activities.end() && where->id == id) {
0105         m_activities.erase(where);
0106         Q_EMIT activityRemoved(id);
0107         Q_EMIT activityListChanged();
0108 
0109     } else {
0110         // qFatal("Requested to delete an non-existent activity");
0111     }
0112 }
0113 
0114 void ActivitiesCache::updateAllActivities()
0115 {
0116     // qDebug() << "Updating all";
0117     m_status = Consumer::Unknown;
0118     Q_EMIT serviceStatusChanged(m_status);
0119 
0120     // Loading the current activity
0121     auto call = Manager::self()->activities()->asyncCall(QStringLiteral("CurrentActivity"));
0122 
0123     onCallFinished(call, SLOT(setCurrentActivityFromReply(QDBusPendingCallWatcher *)));
0124 
0125     // Loading all the activities
0126     call = Manager::self()->activities()->asyncCall(QStringLiteral("ListActivitiesWithInformation"));
0127 
0128     onCallFinished(call, SLOT(setAllActivitiesFromReply(QDBusPendingCallWatcher *)));
0129 }
0130 
0131 void ActivitiesCache::updateActivity(const QString &id)
0132 {
0133     // qDebug() << "Updating activity" << id;
0134 
0135     auto call = Manager::self()->activities()->asyncCall(QStringLiteral("ActivityInformation"), id);
0136 
0137     onCallFinished(call, SLOT(setActivityInfoFromReply(QDBusPendingCallWatcher *)));
0138 }
0139 
0140 void ActivitiesCache::updateActivityState(const QString &id, int state)
0141 {
0142     auto where = getInfo<Mutable>(id);
0143 
0144     if (where && where->state != state) {
0145         auto isInvalid = [](int state) {
0146             return state == Info::Invalid || state == Info::Unknown;
0147         };
0148         auto isStopped = [](int state) {
0149             return state == Info::Stopped || state == Info::Starting;
0150         };
0151         auto isRunning = [](int state) {
0152             return state == Info::Running || state == Info::Stopping;
0153         };
0154 
0155         const bool runningStateChanged =
0156             (isInvalid(state) || isInvalid(where->state) || (isStopped(state) && isRunning(where->state)) || (isRunning(state) && isStopped(where->state)));
0157 
0158         where->state = state;
0159 
0160         if (runningStateChanged) {
0161             Q_EMIT runningActivityListChanged();
0162         }
0163 
0164         Q_EMIT activityStateChanged(id, state);
0165 
0166     } else {
0167         // qFatal("Requested to update the state of a non-existent activity");
0168     }
0169 }
0170 
0171 template<typename _Result, typename _Functor>
0172 void ActivitiesCache::passInfoFromReply(QDBusPendingCallWatcher *watcher, _Functor f)
0173 {
0174     QDBusPendingReply<_Result> reply = *watcher;
0175 
0176     if (!reply.isError()) {
0177         auto replyValue = reply.template argumentAt<0>();
0178         // qDebug() << "Got some reply" << replyValue;
0179 
0180         ((*this).*f)(replyValue);
0181     }
0182 
0183     watcher->deleteLater();
0184 }
0185 
0186 void ActivitiesCache::setActivityInfoFromReply(QDBusPendingCallWatcher *watcher)
0187 {
0188     // qDebug() << "reply...";
0189     passInfoFromReply<ActivityInfo>(watcher, &ActivitiesCache::setActivityInfo);
0190 }
0191 
0192 void ActivitiesCache::setAllActivitiesFromReply(QDBusPendingCallWatcher *watcher)
0193 {
0194     // qDebug() << "reply...";
0195     passInfoFromReply<ActivityInfoList>(watcher, &ActivitiesCache::setAllActivities);
0196 }
0197 
0198 void ActivitiesCache::setCurrentActivityFromReply(QDBusPendingCallWatcher *watcher)
0199 {
0200     // qDebug() << "reply...";
0201     passInfoFromReply<QString>(watcher, &ActivitiesCache::setCurrentActivity);
0202 }
0203 
0204 void ActivitiesCache::setActivityInfo(const ActivityInfo &info)
0205 {
0206     // qDebug() << "Setting activity info" << info.id;
0207 
0208     // Are we updating an existing activity, or adding a new one?
0209     const auto iter = find(info.id);
0210     const auto present = iter != m_activities.end();
0211     bool runningChanged = true;
0212     // If there is an activity with the specified id,
0213     // we are going to remove it, temporarily.
0214     if (present) {
0215         runningChanged = (*iter).state != info.state;
0216         m_activities.erase(iter);
0217     }
0218 
0219     // Now, we need to find where to insert the activity
0220     // and keep the cache sorted by name
0221     const auto where = lower_bound(info);
0222 
0223     m_activities.insert(where, info);
0224 
0225     if (present) {
0226         Q_EMIT activityChanged(info.id);
0227     } else {
0228         Q_EMIT activityAdded(info.id);
0229         Q_EMIT activityListChanged();
0230         if (runningChanged) {
0231             Q_EMIT runningActivityListChanged();
0232         }
0233     }
0234 }
0235 // clang-format off
0236 #define CREATE_SETTER(WHAT, What)                                              \
0237     void ActivitiesCache::setActivity##WHAT(const QString &id,                 \
0238                                             const QString &value)              \
0239     {                                                                          \
0240         auto where = getInfo<Mutable>(id);                                     \
0241                                                                                \
0242         if (where) {                                                           \
0243             where->What = value;                                               \
0244             Q_EMIT activity##WHAT##Changed(id, value);                           \
0245         }                                                                      \
0246     }
0247 // clang-format on
0248 
0249 CREATE_SETTER(Name, name)
0250 CREATE_SETTER(Description, description)
0251 CREATE_SETTER(Icon, icon)
0252 
0253 #undef CREATE_SETTER
0254 
0255 void ActivitiesCache::setAllActivities(const ActivityInfoList &_activities)
0256 {
0257     // qDebug() << "Setting all activities";
0258 
0259     m_activities.clear();
0260 
0261     const ActivityInfoList activities = _activities;
0262 
0263     for (const ActivityInfo &info : activities) {
0264         m_activities << info;
0265     }
0266 
0267     std::sort(m_activities.begin(), m_activities.end(), &infoLessThan);
0268 
0269     m_status = Consumer::Running;
0270     Q_EMIT serviceStatusChanged(m_status);
0271     Q_EMIT activityListChanged();
0272 }
0273 
0274 void ActivitiesCache::setCurrentActivity(const QString &activity)
0275 {
0276     // qDebug() << "Setting current activity to" << activity;
0277 
0278     if (m_currentActivity == activity) {
0279         return;
0280     }
0281 
0282     m_currentActivity = activity;
0283 
0284     Q_EMIT currentActivityChanged(activity);
0285 }
0286 
0287 } // namespace KActivities
0288 
0289 #include "moc_activitiescache_p.cpp"