[-]
[+]
|
Changed |
salmeta.spec
|
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/qml/pages/AddWidget.qml
^
|
@@ -19,11 +19,14 @@
}
delegate : BackgroundItem {
- contentHeight: Theme.itemSizeMedium
+ id: widgetDelegate
+ contentHeight: Theme.itemSizeSmall
Label {
- x: Theme.paddingLarge
- text: model.description
+ anchors.left: parent.left
+ anchors.margins: Theme.paddingLarge
+ anchors.verticalCenter: parent.verticalCenter
+ text: model.description + (!widgetDelegate.enabled ? qsTr(" (does not fit)") : "")
truncationMode: TruncationMode.Elide
color: highlighted ? Theme.highlightColor : Theme.primaryColor
}
@@ -32,6 +35,8 @@
curWidgets.addWidget(model.url, addToPage, addToPos, model.size);
pageStack.pop();
}
+
+ enabled: !curWidgets.widgetOverlaps(addToPage, addToPos, model.size);
}
}
}
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/qml/pages/MainPage.qml
^
|
@@ -227,7 +227,7 @@
Label {
x: Theme.paddingLarge
- text: "TODO"
+ text: "TODO: List of notification types"
}
}
}
|
[-]
[+]
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface0.qml
^
|
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 96/2
+ height: 96/2
+
+ Image {
+ anchors.fill: parent
+ source: "builtinface0.png"
+ }
+}
|
[-]
[+]
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface1.qml
^
|
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 96
+ height: 96/2
+
+ Image {
+ anchors.fill: parent
+ source: "builtinface3.png"
+ }
+}
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface2.png
^
|
(renamed from qml/watch/faces/builtinface_what.png)
|
[-]
[+]
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface2.qml
^
|
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 96
+ height: 96
+
+ Image {
+ anchors.fill: parent
+ source: "builtinface2.png"
+ }
+}
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface4.qml
^
|
@@ -6,6 +6,6 @@
Image {
anchors.fill: parent
- source: "builtinface4.png"
+ source: "builtinface3.png"
}
}
|
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface5.png
^
|
[-]
[+]
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/faces/builtinface5.qml
^
|
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 96
+ height: 96
+
+ Image {
+ anchors.fill: parent
+ source: "builtinface5.png"
+ }
+}
|
[-]
[+]
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/icons
^
|
+(directory)
|
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/icons/notification-email.png
^
|
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/icons/notification-facebook.png
^
|
|
Added |
salmeta-0.1.3.tar.bz2/qml/watch/icons/notification-sms.png
^
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/rpm/salmeta.spec
^
|
@@ -13,11 +13,11 @@
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: Metawatch manager application
-Version: 0.1.2
+Version: 0.1.3
Release: 1
Group: Communications/Bluetooth
License: GPLv3
-URL: http://javispedro.com
+URL: https://gitorious.org/javispedro-jolla-misc/salmeta/
Source0: %{name}-%{version}.tar.bz2
Source100: salmeta.yaml
Requires: sailfishsilica-qt5 >= 0.10.9
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/rpm/salmeta.yaml
^
|
@@ -1,9 +1,9 @@
Name: salmeta
Summary: Metawatch manager application
-Version: 0.1.2
+Version: 0.1.3
Release: 1
Group: Communications/Bluetooth
-URL: http://javispedro.com
+URL: https://gitorious.org/javispedro-jolla-misc/salmeta/
License: GPLv3
Sources:
- '%{name}-%{version}.tar.bz2'
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/salmeta.pro
^
|
@@ -45,8 +45,8 @@
qml/pages/MainPage.qml \
qml/watch/WidgetView.qml \
qml/watch/WatchView.qml qml/watch/add_widget.png \
- qml/watch/faces/builtinface3.qml qml/watch/faces/builtinface3.png \
- qml/watch/faces/builtinface4.qml qml/watch/faces/builtinface4.png \
+ qml/watch/faces/*.qml qml/watch/faces/*.png \
+ qml/watch/icons/*.png \
qml/pages/AddWidget.qml \
qml/watch/notification.png
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/src/availablewidgetsmodel.cpp
^
|
@@ -53,17 +53,42 @@
// Load builtin widgets
WidgetInfo info;
+ info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface0.qml"));
+ info.setDescription(tr("Watchface: 1x1 Small"));
+ info.setSize(WidgetInfo::Size1Q);
+ Q_ASSERT(info.builtinClockfaceId() == 0); // Ensure face ID is autodetected from passed URL
+ _widgets.append(info);
+
+
+ info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface1.qml"));
+ info.setDescription(tr("Watchface: 2x1 Horizontal"));
+ info.setSize(WidgetInfo::Size2QHorizontal);
+ Q_ASSERT(info.builtinClockfaceId() == 1);
+ _widgets.append(info);
+
+ info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface2.qml"));
+ info.setDescription(tr("Watchface: 2x2 MetaWatch logo"));
+ info.setSize(WidgetInfo::Size4Q);
+ Q_ASSERT(info.builtinClockfaceId() == 2);
+ _widgets.append(info);
+
info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface3.qml"));
- info.setDescription("Builtin watchface #3");
+ info.setDescription(tr("Watchface: 2x2 Big numbers"));
info.setSize(WidgetInfo::Size4Q);
- Q_ASSERT(info.builtinClockfaceId() == 3); // Autodetected from passed URL
+ Q_ASSERT(info.builtinClockfaceId() == 3);
_widgets.append(info);
info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface4.qml"));
- info.setDescription("Builtin watchface #4");
+ info.setDescription(tr("Watchface: 2x2 Fish"));
info.setSize(WidgetInfo::Size4Q);
Q_ASSERT(info.builtinClockfaceId() == 4);
_widgets.append(info);
+ info.setUrl(SailfishApp::pathTo("qml/watch/faces/builtinface5.qml"));
+ info.setDescription(tr("Watchface: 2x2 Hanzi"));
+ info.setSize(WidgetInfo::Size4Q);
+ Q_ASSERT(info.builtinClockfaceId() == 5);
+ _widgets.append(info);
+
endResetModel();
}
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/src/controller.cpp
^
|
@@ -80,12 +80,15 @@
const QString address = _settings->value(setting_address).toString();
const QString transport_name = _settings->value(setting_transport).toString();
- qDebug() << "Using transport" << transport_name;
-
- MetaWatch::TransportType transport = MetaWatch::TransportBluetoothLowEnergy; // Default
+ MetaWatch::TransportType transport;
if (transport_name.compare("bluetooth", Qt::CaseInsensitive) == 0) {
+ qDebug() << "Using Bluetooth (RFCOMM)";
transport = MetaWatch::TransportBluetooth;
+ } else {
+ // Default to BLE
+ qDebug() << "Using Bluetooth Low Energy (GATT)";
+ transport = MetaWatch::TransportBluetoothLowEnergy;
}
if (_metawatch) {
@@ -196,6 +199,14 @@
}
break;
+ case MetaWatch::WatchModeNotification:
+ if (_curMode != mode) {
+ _curMode = mode;
+ emit modeChanged();
+ }
+ // Watch will often go back to the same page once it returns to idle mode,
+ // so let's not clobber _curPage.
+ break;
default:
qWarning() << "Unhandled metawatch mode:" << mode;
break;
@@ -228,15 +239,19 @@
QFont large("MetaWatch Large 16pt");
large.setPixelSize(16);
+ // Render notification background
p.drawImage(0, 0, QImage(SailfishApp::pathTo("qml/watch/notification.png").toLocalFile()));
- icon.paint(&p, 0, 96 - 24, 24, 24, Qt::AlignLeft | Qt::AlignBottom);
+
+ // Render the associated icon on the bottom left corner
+ icon.paint(&p, 0, 96 - 24, 28, 24, Qt::AlignLeft | Qt::AlignBottom);
QString title = summary;
bool title_drawn_fully = false;
- if (count > 0) {
+ if (count > 1) {
title = QString("%1 %2").arg(count).arg(summary);
}
+ // Render title area
p.setFont(large);
if (!title.isEmpty()) {
QRect area(2, 2, 96 - 18, 18);
@@ -253,6 +268,8 @@
const int max_y = 96 - 24;
int y = 26;
+ // Now render the contents
+ // If the title did not fit in the title area, also draw it fully here.
p.setFont(small);
if (!title_drawn_fully && !title.isEmpty()) {
QRect r;
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/src/notificationmonitor.cpp
^
|
@@ -2,11 +2,52 @@
#include <QtGui/QIcon>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
+
+#include <sailfishapp.h>
+
#include "notificationmonitor.h"
#include "notifications_adaptor.h"
static NotificationMonitor *global_monitor = 0;
+namespace
+{
+
+QHash<QString, QString> icons_for_categories;
+
+QIcon get_builtin_icon(const QString &iconName)
+{
+ QIcon icon;
+
+ QString filePath = SailfishApp::pathTo("qml/watch/icons/" + iconName + ".png").toLocalFile();
+ icon.addFile(filePath);
+
+ return icon;
+}
+
+QIcon icon_for_category(const QString &category)
+{
+ if (icons_for_categories.isEmpty()) {
+ icons_for_categories.insert("x-nemo.email", "notification-email");
+ icons_for_categories.insert("x-nemo.messaging.im", "notification-sms");
+ icons_for_categories.insert("x-nemo.messaging.sms", "notification-sms");
+ icons_for_categories.insert("x-nemo.messaging.mms", "notification-sms");
+ icons_for_categories.insert("x-nemo.social.twitter.mention", "notification-twitter");
+ icons_for_categories.insert("x-nemo.social.twitter.tweet", "notification-twitter");
+ icons_for_categories.insert("x-nemo.social.facebook.notification", "notification-facebook");
+ icons_for_categories.insert("x-nemo.social.facebook.statuspost", "notification-facebook");
+ }
+
+ QString iconName = icons_for_categories.value(category, QString());
+ if (!iconName.isEmpty()) {
+ return get_builtin_icon(iconName);
+ } else {
+ return QIcon();
+ }
+}
+
+}
+
NotificationMonitor::NotificationMonitor(QObject *parent) :
QObject(parent)
{
@@ -46,18 +87,29 @@
Q_ASSERT(calledFromDBus());
setDelayedReply(true);
+ // If the notification mentions a specific icon, then use it.
+ // But otherwise let's prefer our builtin icons.
if (app_icon.startsWith("/")) {
icon = QIcon(app_icon);
} else if (app_icon.startsWith("file:")) {
QUrl url(app_icon);
icon = QIcon(url.toLocalFile());
} else {
- icon = QIcon::fromTheme(app_icon);
+ QString category = hints.value("category").toString();
+ // Let's hardcode a few categories for now..
+ if (!category.isEmpty()) {
+ icon = icon_for_category(category);
+ }
}
int count = hints.value("x-nemo-item-count").toInt();
QDateTime dateTime = hints.value("x-nemo-timestamp").toDateTime();
+ if (summary.isEmpty() && body.isEmpty()) {
+ // Avoid sending empty notifications to watch.
+ return 0;
+ }
+
emit incomingNotification(app_name, icon, summary, count, body, dateTime);
return 0;
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/src/widgetinfomodel.cpp
^
|
@@ -1,4 +1,5 @@
#include <QtCore/QDebug>
+#include <QtCore/QBitArray>
#include <QtCore/QStringList>
#include "widgetinfomodel.h"
@@ -10,6 +11,82 @@
return QString("widget%1_").arg(index);
}
+WidgetInfo::WidgetPosition canonicalize_widget_pos(WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size)
+{
+ switch (size) {
+ case WidgetInfo::Size1Q:
+ return pos; // Any position is valid
+ case WidgetInfo::Size2QHorizontal:
+ switch (pos) {
+ case WidgetInfo::PosNW:
+ case WidgetInfo::PosNE:
+ return WidgetInfo::PosNW;
+ case WidgetInfo::PosSW:
+ case WidgetInfo::PosSE:
+ return WidgetInfo::PosSW;
+ }
+ break;
+ case WidgetInfo::Size2QVertical:
+ switch (pos) {
+ case WidgetInfo::PosNW:
+ case WidgetInfo::PosSW:
+ return WidgetInfo::PosNW;
+ case WidgetInfo::PosNE:
+ case WidgetInfo::PosSE:
+ return WidgetInfo::PosNE;
+ }
+ break;
+ case WidgetInfo::Size4Q:
+ return WidgetInfo::PosNW; // 4Q widgets use entire screen
+ }
+
+ return pos;
+}
+
+QBitArray used_positions(WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size)
+{
+ QBitArray used(4, false);
+
+ switch (size) {
+ case WidgetInfo::Size1Q:
+ used[pos] = true;
+ break;
+ case WidgetInfo::Size2QHorizontal:
+ switch (pos) {
+ case WidgetInfo::PosNW:
+ case WidgetInfo::PosNE:
+ used[WidgetInfo::PosNW] = true;
+ used[WidgetInfo::PosNE] = true;
+ break;
+ case WidgetInfo::PosSW:
+ case WidgetInfo::PosSE:
+ used[WidgetInfo::PosNW] = true;
+ used[WidgetInfo::PosNE] = true;
+ break;
+ }
+ break;
+ case WidgetInfo::Size2QVertical:
+ switch (pos) {
+ case WidgetInfo::PosNW:
+ case WidgetInfo::PosSW:
+ used[WidgetInfo::PosNW] = true;
+ used[WidgetInfo::PosSW] = true;
+ break;
+ case WidgetInfo::PosNE:
+ case WidgetInfo::PosSE:
+ used[WidgetInfo::PosNE] = true;
+ used[WidgetInfo::PosSE] = true;
+ break;
+ }
+ break;
+ case WidgetInfo::Size4Q:
+ used.fill(true);
+ break;
+ }
+
+ return used;
+}
+
}
WidgetInfoModel::WidgetInfoModel(const QString &settingsPrefix, QObject *parent) :
@@ -69,23 +146,21 @@
return _widgets.toList();
}
-void WidgetInfoModel::reload()
+bool WidgetInfoModel::widgetOverlaps(int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size) const
{
- beginResetModel();
- _widgets.resize(16);
+ QBitArray usedPos = used_positions(pos, size);
for (int i = 0; i < _widgets.size(); i++) {
- WidgetInfo &info = _widgets[i];
- const QString base = get_widget_dconf_base(i);
+ if (_widgets[i].url().isEmpty()) continue;
+ if (_widgets[i].page() != page) continue;
- info.setInvert(_settings->value(base + "invert").toBool());
- info.setPage(_settings->value(base + "page").toInt());
- info.setSize(static_cast<WidgetInfo::WidgetSize>(_settings->value(base + "size").toInt()));
- info.setPosition(static_cast<WidgetInfo::WidgetPosition>(_settings->value(base + "position").toInt()));
- info.setUrl(_settings->value(base + "url").toUrl());
+ QBitArray intersection = usedPos & used_positions(_widgets[i].position(), _widgets[i].size());
+ if (intersection.count(true) > 0) {
+ return true;
+ }
}
- endResetModel();
+ return false;
}
int WidgetInfoModel::addWidget(const QUrl &url, int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size)
@@ -93,11 +168,18 @@
int slot = findEmptySlot();
if (slot == -1) {
qWarning() << "No empty slots!"; // This shouldn't happen
- return slot;
+ return -1;
}
+ pos = canonicalize_widget_pos(pos, size);
+
qDebug() << "Adding widget" << url << page << pos << size;
+ if (widgetOverlaps(page, pos, size)) {
+ qWarning() << "Can't add the widget: it overlaps with an existing one!";
+ return -1;
+ }
+
const QString base = get_widget_dconf_base(slot);
_settings->setValue(base + "invert", QVariant::fromValue<bool>(false));
@@ -122,6 +204,25 @@
_settings->setValue(base + "position", QVariant());
}
+void WidgetInfoModel::reload()
+{
+ beginResetModel();
+ _widgets.resize(16);
+
+ for (int i = 0; i < _widgets.size(); i++) {
+ WidgetInfo &info = _widgets[i];
+ const QString base = get_widget_dconf_base(i);
+
+ info.setInvert(_settings->value(base + "invert").toBool());
+ info.setPage(_settings->value(base + "page").toInt());
+ info.setSize(static_cast<WidgetInfo::WidgetSize>(_settings->value(base + "size").toInt()));
+ info.setPosition(static_cast<WidgetInfo::WidgetPosition>(_settings->value(base + "position").toInt()));
+ info.setUrl(_settings->value(base + "url").toUrl());
+ }
+
+ endResetModel();
+}
+
int WidgetInfoModel::findEmptySlot()
{
for (int i = 0; i < _widgets.size(); i++) {
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/src/widgetinfomodel.h
^
|
@@ -27,12 +27,15 @@
QList<WidgetInfo> toList() const;
+ Q_INVOKABLE bool widgetOverlaps(int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size) const;
+
+ Q_INVOKABLE int addWidget(const QUrl &url, int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size);
+ Q_INVOKABLE void removeWidget(int widgetId);
+
signals:
public slots:
void reload();
- int addWidget(const QUrl &url, int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size);
- void removeWidget(int widgetId);
private:
int findEmptySlot();
|
[-]
[+]
|
Changed |
salmeta-0.1.3.tar.bz2/translations/salmeta.ts
^
|
@@ -2,6 +2,40 @@
<!DOCTYPE TS>
<TS version="2.0">
<context>
+ <name>AddWidget</name>
+ <message>
+ <source> (does not fit)</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AvailableWidgetsModel</name>
+ <message>
+ <source>Watchface: 1x1 Small</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Watchface: 2x1 Horizontal</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Watchface: 2x2 MetaWatch logo</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Watchface: 2x2 Big numbers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Watchface: 2x2 Fish</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Watchface: 2x2 Hanzi</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>MainPage</name>
<message>
<source>Not done yet</source>
|
[-]
[+]
|
Changed |
salmeta.yaml
^
|
@@ -1,9 +1,9 @@
Name: salmeta
Summary: Metawatch manager application
-Version: 0.1.2
+Version: 0.1.3
Release: 1
Group: Communications/Bluetooth
-URL: http://javispedro.com
+URL: https://gitorious.org/javispedro-jolla-misc/salmeta/
License: GPLv3
Sources:
- '%{name}-%{version}.tar.bz2'
|