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"