summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-12-14 03:26:46 +0100
committerJavier <dev.git@javispedro.com>2014-12-14 03:26:46 +0100
commitf40514fe681f5163deb5f579140ef4f7ac77f5a8 (patch)
treec3edddde3cbc98a797d5a1a7b745c2c5d3b99eba /daemon
parentdf30ca18eebd2dfec03c589b607d45a5891cf2b2 (diff)
add icons to the slots managament UI
Diffstat (limited to 'daemon')
-rw-r--r--daemon/appinfo.cpp25
-rw-r--r--daemon/appinfo.h9
-rw-r--r--daemon/appmanager.cpp142
-rw-r--r--daemon/appmanager.h2
-rw-r--r--daemon/manager.cpp5
5 files changed, 170 insertions, 13 deletions
diff --git a/daemon/appinfo.cpp b/daemon/appinfo.cpp
index fd43248..4397abc 100644
--- a/daemon/appinfo.cpp
+++ b/daemon/appinfo.cpp
@@ -1,5 +1,6 @@
-#include "appinfo.h"
#include <QSharedData>
+#include <QBuffer>
+#include "appinfo.h"
struct AppInfoData : public QSharedData {
QUuid uuid;
@@ -13,6 +14,7 @@ struct AppInfoData : public QSharedData {
AppInfo::Capabilities capabilities;
QHash<QString, int> keyInts;
QHash<int, QString> keyNames;
+ QImage menuIcon;
QString path;
};
@@ -21,6 +23,7 @@ AppInfo::AppInfo() : d(new AppInfoData)
d->versionCode = 0;
d->watchface = false;
d->jskit = false;
+ d->capabilities = 0;
}
AppInfo::AppInfo(const AppInfo &rhs) : d(rhs.d)
@@ -154,6 +157,26 @@ int AppInfo::valueForAppKey(const QString &key) const
return d->keyInts.value(key, -1);
}
+QImage AppInfo::menuIcon() const
+{
+ return d->menuIcon;
+}
+
+QByteArray AppInfo::menuIconAsPng() const
+{
+ QByteArray data;
+ QBuffer buf(&data);
+ buf.open(QIODevice::WriteOnly);
+ d->menuIcon.save(&buf, "PNG");
+ buf.close();
+ return data;
+}
+
+void AppInfo::setMenuIcon(const QImage &img)
+{
+ d->menuIcon = img;
+}
+
QString AppInfo::path() const
{
return d->path;
diff --git a/daemon/appinfo.h b/daemon/appinfo.h
index 6f97639..3d5c4b4 100644
--- a/daemon/appinfo.h
+++ b/daemon/appinfo.h
@@ -1,10 +1,10 @@
#ifndef APPINFO_H
#define APPINFO_H
-#include <QObject>
+#include <QSharedDataPointer>
#include <QUuid>
#include <QHash>
-#include <QSharedDataPointer>
+#include <QImage>
class AppInfoData;
@@ -28,6 +28,7 @@ public:
Q_PROPERTY(bool watchface READ isWatchface WRITE setWatchface)
Q_PROPERTY(bool jskit READ isJSKit WRITE setJSKit)
Q_PROPERTY(Capabilities capabilities READ capabilities WRITE setCapabilities)
+ Q_PROPERTY(QImage menuIcon READ menuIcon WRITE setMenuIcon)
Q_PROPERTY(QString path READ path WRITE setPath)
public:
@@ -71,6 +72,10 @@ public:
bool hasAppKey(const QString &key) const;
int valueForAppKey(const QString &key) const;
+ QImage menuIcon() const;
+ QByteArray menuIconAsPng() const;
+ void setMenuIcon(const QImage &img);
+
QString path() const;
void setPath(const QString &string);
diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp
index 10f2e3e..8745160 100644
--- a/daemon/appmanager.cpp
+++ b/daemon/appmanager.cpp
@@ -4,6 +4,17 @@
#include <QJsonArray>
#include <QDir>
#include "appmanager.h"
+#include "unpacker.h"
+#include "stm32crc.h"
+
+namespace {
+struct ResourceEntry {
+ int index;
+ quint32 offset;
+ quint32 length;
+ quint32 crc;
+};
+}
AppManager::AppManager(QObject *parent)
: QObject(parent),
@@ -116,18 +127,55 @@ void AppManager::scanApp(const QString &path)
info.setWatchface(watchapp["watchface"].toBool());
info.setJSKit(appDir.exists("pebble-js-app.js"));
- const QJsonArray capabilities = root["capabilities"].toArray();
- AppInfo::Capabilities caps = 0;
- for (QJsonArray::const_iterator it = capabilities.constBegin(); it != capabilities.constEnd(); ++it) {
- QString cap = (*it).toString();
- if (cap == "location") caps |= AppInfo::Location;
- if (cap == "configurable") caps |= AppInfo::Configurable;
+ if (root.contains("capabilities")) {
+ const QJsonArray capabilities = root["capabilities"].toArray();
+ AppInfo::Capabilities caps = 0;
+ for (auto it = capabilities.constBegin(); it != capabilities.constEnd(); ++it) {
+ QString cap = (*it).toString();
+ if (cap == "location") caps |= AppInfo::Location;
+ if (cap == "configurable") caps |= AppInfo::Configurable;
+ }
+ info.setCapabilities(caps);
}
- info.setCapabilities(caps);
- const QJsonObject appkeys = root["appKeys"].toObject();
- for (QJsonObject::const_iterator it = appkeys.constBegin(); it != appkeys.constEnd(); ++it) {
- info.addAppKey(it.key(), it.value().toInt());
+ if (root.contains("appKeys")) {
+ const QJsonObject appkeys = root["appKeys"].toObject();
+ for (auto it = appkeys.constBegin(); it != appkeys.constEnd(); ++it) {
+ info.addAppKey(it.key(), it.value().toInt());
+ }
+ }
+
+ if (root.contains("resources")) {
+ const QJsonObject resources = root["resources"].toObject();
+ const QJsonArray media = resources["media"].toArray();
+ int index = 0;
+
+ for (auto it = media.constBegin(); it != media.constEnd(); ++it) {
+ const QJsonObject res = (*it).toObject();
+ const QJsonValue menuIcon = res["menuIcon"];
+
+ bool is_menu_icon = false;
+ switch (menuIcon.type()) {
+ case QJsonValue::Bool:
+ is_menu_icon = menuIcon.toBool();
+ break;
+ case QJsonValue::String:
+ is_menu_icon = !menuIcon.toString().isEmpty();
+ break;
+ default:
+ break;
+ }
+
+ if (is_menu_icon) {
+ QByteArray data = extractFromResourcePack(appDir.filePath("app_resources.pbpack"), index);
+ if (!data.isEmpty()) {
+ QImage icon = decodeResourceImage(data);
+ info.setMenuIcon(icon);
+ }
+ }
+
+ index++;
+ }
}
info.setPath(path);
@@ -143,3 +191,77 @@ void AppManager::scanApp(const QString &path)
const char *type = info.isWatchface() ? "watchface" : "app";
logger()->debug() << "found installed" << type << info.shortName() << info.versionLabel() << "with uuid" << info.uuid().toString();
}
+
+QByteArray AppManager::extractFromResourcePack(const QString &file, int wanted_id) const
+{
+ QFile f(file);
+ if (!f.open(QIODevice::ReadOnly)) {
+ logger()->warn() << "cannot open resource file" << f.fileName();
+ return QByteArray();
+ }
+
+ QByteArray data = f.readAll();
+ Unpacker u(data);
+
+ int num_files = u.readLE<quint32>();
+ u.readLE<quint32>(); // crc for entire file
+ u.readLE<quint32>(); // timestamp
+
+ logger()->debug() << "reading" << num_files << "resources from" << file;
+
+ QList<ResourceEntry> table;
+
+ for (int i = 0; i < num_files; i++) {
+ ResourceEntry e;
+ e.index = u.readLE<quint32>();
+ e.offset = u.readLE<quint32>();
+ e.length = u.readLE<quint32>();
+ e.crc = u.readLE<quint32>();
+
+ if (u.bad()) {
+ logger()->warn() << "short read on resource file";
+ return QByteArray();
+ }
+
+ table.append(e);
+ }
+
+ if (wanted_id >= table.size()) {
+ logger()->warn() << "specified resource does not exist";
+ return QByteArray();
+ }
+
+ const ResourceEntry &e = table[wanted_id];
+
+ int offset = 12 + 256 * 16 + e.offset;
+
+ QByteArray res = data.mid(offset, e.length);
+
+ Stm32Crc crc;
+ crc.addData(res);
+
+ if (crc.result() != e.crc) {
+ logger()->warn() << "CRC failure in resource" << e.index << "on file" << file;
+ return QByteArray();
+ }
+
+ return res;
+}
+
+QImage AppManager::decodeResourceImage(const QByteArray &data) const
+{
+ Unpacker u(data);
+ int scanline = u.readLE<quint16>();
+ u.skip(sizeof(quint16) + sizeof(quint32));
+ int width = u.readLE<quint16>();
+ int height = u.readLE<quint16>();
+
+ QImage img(width, height, QImage::Format_MonoLSB);
+ const uchar *src = reinterpret_cast<const uchar *>(&data.constData()[12]);
+ for (int line = 0; line < height; ++line) {
+ memcpy(img.scanLine(line), src, qMin(scanline, img.bytesPerLine()));
+ src += scanline;
+ }
+
+ return img;
+}
diff --git a/daemon/appmanager.h b/daemon/appmanager.h
index 1725c14..d5e5ba1 100644
--- a/daemon/appmanager.h
+++ b/daemon/appmanager.h
@@ -30,6 +30,8 @@ signals:
private:
void scanApp(const QString &path);
+ QByteArray extractFromResourcePack(const QString &file, int id) const;
+ QImage decodeResourceImage(const QByteArray &data) const;
private:
QFileSystemWatcher *_watcher;
diff --git a/daemon/manager.cpp b/daemon/manager.cpp
index 25908e4..212a1d7 100644
--- a/daemon/manager.cpp
+++ b/daemon/manager.cpp
@@ -380,6 +380,11 @@ QVariantList PebbledProxy::AllApps() const
m.insert("company-name", QVariant::fromValue(info.companyName()));
m.insert("version-label", QVariant::fromValue(info.versionLabel()));
m.insert("is-watchface", QVariant::fromValue(info.isWatchface()));
+
+ if (!info.menuIcon().isNull()) {
+ m.insert("menu-icon", QVariant::fromValue(info.menuIconAsPng()));
+ }
+
l.append(QVariant::fromValue(m));
}