summaryrefslogtreecommitdiff
path: root/daemon/appmanager.cpp
blob: d06681e6ea275e244004b37c3951310ce21b0711 (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
#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);
}

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

const AppManager::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.uuid = QUuid(root["uuid"].toString());
    info.shortName = root["shortName"].toString();
    info.longName = root["longName"].toString();
    info.company = root["companyName"].toString();
    info.versionCode = root["versionCode"].toInt();
    info.versionLabel = root["versionLabel"].toString();

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

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

    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();
}