summaryrefslogtreecommitdiff
path: root/daemon/appmanager.cpp
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-11-30 17:27:08 +0100
committerJavier <dev.git@javispedro.com>2014-11-30 17:37:19 +0100
commit49f1261bf9d635d5e3d881e87a93ed4e76abfe90 (patch)
tree4432ecd771a3aca533914d02f9947cb497493395 /daemon/appmanager.cpp
parentd55d1d472d5876f90dd95301d9f3b6bef6f4c494 (diff)
allow receiving responses to commands in watchconnector
* the skeleton is in place for watchconnector to allow query->response messages. I've used call/cc style because it is impossible to make QBluetoothSocket synchronous (waitForReadyRead() is a no-op) * remove watchcommands, instead create musicmanager to listen for the music endpoint. The other (simpler) endpoints are now listened in watchconnector itself. hangupAll() slot is moved to voicecallmanager. * instead of emitting signals for each received message, listeners can now register for receiving messages targeted towards a given endpoint * when reading from bluetoothsocket, properly handle short reads * remove useless 'watch' namespace * create appmanager, which mantains a database of installed apps (installed on the phone, that is; watch installed apps will come later) * all the *Managers are now instantiated by the main Manager itself * introduce Unpacker helper class for decoding watch messages * implement getAppbankStatus and getAppbankUuids messages and response parsers * remove file logging for now (20MB is bad for eMMC!) * use dbus object path /org/pebbled instead of /
Diffstat (limited to 'daemon/appmanager.cpp')
-rw-r--r--daemon/appmanager.cpp109
1 files changed, 103 insertions, 6 deletions
diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp
index b4a3d68..34af3af 100644
--- a/daemon/appmanager.cpp
+++ b/daemon/appmanager.cpp
@@ -1,15 +1,112 @@
#include <QStandardPaths>
-#include <QUuid>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QDir>
#include "appmanager.h"
AppManager::AppManager(QObject *parent)
- : 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);
+}
+
+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();
}
-QString AppManager::getAppDir(const QUuid& uuid) const
+void AppManager::scanApp(const QString &path)
{
- return QStandardPaths::locate(QStandardPaths::DataLocation,
- QString("apps/%1").arg(uuid.toString()),
- QStandardPaths::LocateDirectory);
+ 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();
}