summaryrefslogtreecommitdiff
path: root/daemon/appmanager.cpp
blob: 867a15ee8c246f9c3317eae1d0910c0d90e2e05c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDir>
#include "appmanager.h"

AppManager::AppManager(QObject *parent)
    : QObject(parent),
      _watcher(new QFileSystemWatcher(this))
{
    connect(_watcher, &QFileSystemWatcher::directoryChanged,
            this, &AppManager::rescan);

    QDir dataDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
    if (!dataDir.exists("apps")) {
        if (!dataDir.mkdir("apps")) {
            logger()->warn() << "could not create dir" << dataDir.absoluteFilePath("apps");
        }
    }
    logger()->debug() << "install apps in" << dataDir.absoluteFilePath("apps");

    rescan();
}

QStringList AppManager::appPaths() const
{
    return QStandardPaths::locateAll(QStandardPaths::DataLocation,
                                     QLatin1String("apps"),
                                     QStandardPaths::LocateDirectory);
}

AppInfo AppManager::info(const QUuid &uuid) const
{
    return _apps.value(uuid);
}

AppInfo AppManager::info(const QString &name) const
{
    QUuid uuid = _names.value(name);
    if (!uuid.isNull()) {
        return info(uuid);
    } else {
        return AppInfo();
    }
}

void AppManager::rescan()
{
    QStringList watchedDirs = _watcher->directories();
    if (!watchedDirs.isEmpty()) _watcher->removePaths(watchedDirs);
    QStringList watchedFiles = _watcher->files();
    if (!watchedFiles.isEmpty()) _watcher->removePaths(watchedFiles);
    _apps.clear();
    _names.clear();

    Q_FOREACH(const QString &path, appPaths()) {
        QDir dir(path);
        _watcher->addPath(dir.absolutePath());
        logger()->debug() << "scanning dir" << dir.absolutePath();
        QStringList entries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Executable);
        logger()->debug() << "scanning dir results" << entries;
        Q_FOREACH(const QString &path, entries) {
            QString appPath = dir.absoluteFilePath(path);
            _watcher->addPath(appPath);
            if (dir.exists(path + "/appinfo.json")) {
                _watcher->addPath(appPath + "/appinfo.json");
                scanApp(appPath);
            }
        }
    }

    logger()->debug() << "now watching" << _watcher->directories() << _watcher->files();
}

void AppManager::scanApp(const QString &path)
{
    logger()->debug() << "scanning app" << path;
    QDir appDir(path);
    if (!appDir.isReadable()) {
        logger()->warn() << "app" << appDir.absolutePath() << "is not readable";
        return;
    }

    QFile appInfoFile(path + "/appinfo.json");
    if (!appInfoFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        logger()->warn() << "cannot open app info file" << appInfoFile.fileName() << ":"
                         << appInfoFile.errorString();
        return;
    }

    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(appInfoFile.readAll(), &parseError);
    if (parseError.error != QJsonParseError::NoError) {
        logger()->warn() << "cannot parse app info file" << appInfoFile.fileName() << ":"
                         << parseError.errorString();
        return;
    }

    const QJsonObject root = doc.object();
    AppInfo info;
    info.setUuid(QUuid(root["uuid"].toString()));
    info.setShortName(root["shortName"].toString());
    info.setLongName(root["longName"].toString());
    info.setCompanyName(root["companyName"].toString());
    info.setVersionCode(root["versionCode"].toInt());
    info.setVersionLabel(root["versionLabel"].toString());

    const QJsonObject watchapp = root["watchapp"].toObject();
    info.setWatchface(watchapp["watchface"].toBool());
    info.setJSKit(appDir.exists("pebble-js-app.js"));

    const QJsonObject appkeys = root["appKeys"].toObject();
    for (QJsonObject::const_iterator it = appkeys.constBegin(); it != appkeys.constEnd(); ++it) {
        info.addAppKey(it.key(), it.value().toInt());
    }

    info.setPath(path);

    if (info.uuid().isNull() || info.shortName().isEmpty()) {
        logger()->warn() << "invalid or empty uuid/name in" << appInfoFile.fileName();
        return;
    }

    _apps.insert(info.uuid(), info);
    _names.insert(info.shortName(), info.uuid());

    const char *type = info.isWatchface() ? "watchface" : "app";
    logger()->debug() << "found installed" << type << info.shortName() << info.versionLabel() << "with uuid" << info.uuid().toString();
}