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