SPDX-FileCopyrightText: 2013-2016 Ivan Cukic <ivan.cukic(at)kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "activitiescache_p.h"
#include "manager_p.h"
#include <mutex>
#include <QString>
#include "mainthreadexecutor_p.h"
namespace KActivities
{
static QString nulluuid = QStringLiteral("00000000-0000-0000-0000-000000000000");
using kamd::utils::Mutable;
std::shared_ptr<ActivitiesCache> ActivitiesCache::self()
{
static std::weak_ptr<ActivitiesCache> s_instance;
static std::mutex singleton;
std::lock_guard<std::mutex> singleton_lock(singleton);
auto result = s_instance.lock();
if (s_instance.expired()) {
runInMainThread([&result] {
result.reset(new ActivitiesCache());
s_instance = result;
});
}
return result;
}
ActivitiesCache::ActivitiesCache()
: m_status(Consumer::NotRunning)
{
using org::kde::ActivityManager::Activities;
auto activities = Manager::self()->activities();
connect(activities, &Activities::ActivityAdded, this, &ActivitiesCache::updateActivity);
connect(activities, &Activities::ActivityChanged, this, &ActivitiesCache::updateActivity);
connect(activities, &Activities::ActivityRemoved, this, &ActivitiesCache::removeActivity);
connect(activities, &Activities::ActivityStateChanged, this, &ActivitiesCache::updateActivityState);
connect(activities, &Activities::ActivityNameChanged, this, &ActivitiesCache::setActivityName);
connect(activities, &Activities::ActivityDescriptionChanged, this, &ActivitiesCache::setActivityDescription);
connect(activities, &Activities::ActivityIconChanged, this, &ActivitiesCache::setActivityIcon);
connect(activities, &Activities::CurrentActivityChanged, this, &ActivitiesCache::setCurrentActivity);
connect(Manager::self(), &Manager::serviceStatusChanged, this, &ActivitiesCache::setServiceStatus);
setServiceStatus(Manager::self()->serviceStatus());
}
void ActivitiesCache::setServiceStatus(Manager::ServiceStatus status)
{
loadOfflineDefaults();
auto oldStatus = m_status;
switch (status) {
case Manager::NotRunning:
m_status = Consumer::NotRunning;
break;
case Manager::Starting:
m_status = Consumer::Unknown;
break;
case Manager::Running:
m_status = Consumer::Unknown;
updateAllActivities();
break;
}
if (m_status != oldStatus) {
Q_EMIT serviceStatusChanged(m_status);
}
}
void ActivitiesCache::loadOfflineDefaults()
{
m_activities.clear();
m_activities << ActivityInfo(nulluuid, QString(), QString(), QString(), Info::Running);
m_currentActivity = nulluuid;
Q_EMIT activityListChanged();
}
ActivitiesCache::~ActivitiesCache()
{
}
void ActivitiesCache::removeActivity(const QString &id)
{
const auto where = find(id);
if (where != m_activities.end() && where->id == id) {
m_activities.erase(where);
Q_EMIT activityRemoved(id);
Q_EMIT activityListChanged();
} else {
}
}
void ActivitiesCache::updateAllActivities()
{
auto call = Manager::self()->activities()->asyncCall(QStringLiteral("CurrentActivity"));
onCallFinished(call, SLOT(setCurrentActivityFromReply(QDBusPendingCallWatcher *)));
call = Manager::self()->activities()->asyncCall(QStringLiteral("ListActivitiesWithInformation"));
onCallFinished(call, SLOT(setAllActivitiesFromReply(QDBusPendingCallWatcher *)));
}
void ActivitiesCache::updateActivity(const QString &id)
{
auto call = Manager::self()->activities()->asyncCall(QStringLiteral("ActivityInformation"), id);
onCallFinished(call, SLOT(setActivityInfoFromReply(QDBusPendingCallWatcher *)));
}
void ActivitiesCache::updateActivityState(const QString &id, int state)
{
auto where = getInfo<Mutable>(id);
if (where && where->state != state) {
auto isInvalid = [](int state) {
return state == Info::Invalid || state == Info::Unknown;
};
auto isStopped = [](int state) {
return state == Info::Stopped || state == Info::Starting;
};
auto isRunning = [](int state) {
return state == Info::Running || state == Info::Stopping;
};
const bool runningStateChanged =
(isInvalid(state) || isInvalid(where->state) || (isStopped(state) && isRunning(where->state)) || (isRunning(state) && isStopped(where->state)));
where->state = state;
if (runningStateChanged) {
Q_EMIT runningActivityListChanged();
}
Q_EMIT activityStateChanged(id, state);
} else {
}
}
template<typename _Result, typename _Functor>
void ActivitiesCache::passInfoFromReply(QDBusPendingCallWatcher *watcher, _Functor f)
{
QDBusPendingReply<_Result> reply = *watcher;
if (!reply.isError()) {
auto replyValue = reply.template argumentAt<0>();
((*this).*f)(replyValue);
}
watcher->deleteLater();
}
void ActivitiesCache::setActivityInfoFromReply(QDBusPendingCallWatcher *watcher)
{
passInfoFromReply<ActivityInfo>(watcher, &ActivitiesCache::setActivityInfo);
}
void ActivitiesCache::setAllActivitiesFromReply(QDBusPendingCallWatcher *watcher)
{
passInfoFromReply<ActivityInfoList>(watcher, &ActivitiesCache::setAllActivities);
}
void ActivitiesCache::setCurrentActivityFromReply(QDBusPendingCallWatcher *watcher)
{
passInfoFromReply<QString>(watcher, &ActivitiesCache::setCurrentActivity);
}
void ActivitiesCache::setActivityInfo(const ActivityInfo &info)
{
const auto iter = find(info.id);
const auto present = iter != m_activities.end();
bool runningChanged = true;
if (present) {
runningChanged = (*iter).state != info.state;
m_activities.erase(iter);
}
const auto where = lower_bound(info);
m_activities.insert(where, info);
if (present) {
Q_EMIT activityChanged(info.id);
} else {
Q_EMIT activityAdded(info.id);
Q_EMIT activityListChanged();
if (runningChanged) {
Q_EMIT runningActivityListChanged();
}
}
}
#define CREATE_SETTER(WHAT, What) \
void ActivitiesCache::setActivity##WHAT(const QString &id, \
const QString &value) \
{ \
auto where = getInfo<Mutable>(id); \
\
if (where) { \
where->What = value; \
Q_EMIT activity##WHAT##Changed(id, value); \
} \
}
CREATE_SETTER(Name, name)
CREATE_SETTER(Description, description)
CREATE_SETTER(Icon, icon)
#undef CREATE_SETTER
void ActivitiesCache::setAllActivities(const ActivityInfoList &_activities)
{
m_activities.clear();
const ActivityInfoList activities = _activities;
for (const ActivityInfo &info : activities) {
m_activities << info;
}
std::sort(m_activities.begin(), m_activities.end(), &infoLessThan);
m_status = Consumer::Running;
Q_EMIT serviceStatusChanged(m_status);
Q_EMIT activityListChanged();
}
void ActivitiesCache::setCurrentActivity(const QString &activity)
{
if (m_currentActivity == activity) {
return;
}
m_currentActivity = activity;
Q_EMIT currentActivityChanged(activity);
}
}
#include "moc_activitiescache_p.cpp"