summaryrefslogtreecommitdiff
path: root/rockwork/qml
diff options
context:
space:
mode:
Diffstat (limited to 'rockwork/qml')
-rw-r--r--rockwork/qml/AppSettingsPage.qml78
-rw-r--r--rockwork/qml/AppStoreDetailsPage.qml278
-rw-r--r--rockwork/qml/AppStorePage.qml266
-rw-r--r--rockwork/qml/ContentPeerPickerPage.qml41
-rw-r--r--rockwork/qml/DeveloperToolsPage.qml157
-rw-r--r--rockwork/qml/FirmwareUpgradePage.qml58
-rw-r--r--rockwork/qml/HealthSettingsDialog.qml115
-rw-r--r--rockwork/qml/ImportPackagePage.qml32
-rw-r--r--rockwork/qml/InfoPage.qml86
-rw-r--r--rockwork/qml/InstalledAppDelegate.qml88
-rw-r--r--rockwork/qml/InstalledAppsPage.qml201
-rw-r--r--rockwork/qml/Main.qml53
-rw-r--r--rockwork/qml/MainMenuPage.qml317
-rw-r--r--rockwork/qml/NotificationsPage.qml88
-rw-r--r--rockwork/qml/PebbleModels.qml28
-rw-r--r--rockwork/qml/PebblesPage.qml69
-rw-r--r--rockwork/qml/ScreenshotsPage.qml107
-rw-r--r--rockwork/qml/SettingsPage.qml80
-rw-r--r--rockwork/qml/SystemAppIcon.qml67
19 files changed, 2209 insertions, 0 deletions
diff --git a/rockwork/qml/AppSettingsPage.qml b/rockwork/qml/AppSettingsPage.qml
new file mode 100644
index 0000000..d8d865b
--- /dev/null
+++ b/rockwork/qml/AppSettingsPage.qml
@@ -0,0 +1,78 @@
+import QtQuick 2.4
+import Ubuntu.Web 0.2
+import Ubuntu.Components 1.3
+import com.canonical.Oxide 1.0 as Oxide
+
+Page {
+ id: settings
+
+ property string uuid;
+ property string url;
+ property var pebble;
+
+ title: i18n.tr("App Settings")
+
+ WebContext {
+ id: webcontext
+ userAgent: "Mozilla/5.0 (Linux; Android 5.0; Nexus 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36 Ubuntu Touch (RockWork)"
+ }
+
+ WebView {
+ id: webview
+ anchors {
+ fill: parent
+ bottom: parent.bottom
+ }
+ width: parent.width
+ height: parent.height
+
+ context: webcontext
+ url: settings.url
+ preferences.localStorageEnabled: true
+ preferences.appCacheEnabled: true
+ preferences.javascriptCanAccessClipboard: true
+
+ function navigationRequestedDelegate(request) {
+ //The pebblejs:// protocol is handeled by the urihandler, as it appears we can't intercept it
+
+ var url = request.url.toString();
+ console.log(url, url.substring(0, 16));
+ if (url.substring(0, 16) == 'pebblejs://close') {
+ pebble.configurationClosed(settings.uuid, url);
+ request.action = Oxide.NavigationRequest.ActionReject;
+ pageStack.pop();
+ }
+ }
+
+ Component.onCompleted: {
+ preferences.localStorageEnabled = true;
+ }
+ }
+
+ ProgressBar {
+ height: units.dp(3)
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+
+ showProgressPercentage: false
+ value: (webview.loadProgress / 100)
+ visible: (webview.loading && !webview.lastLoadStopped)
+ }
+
+ Connections {
+ target: UriHandler
+ onOpened: {
+ if (uris && uris[0] && uris[0].length) {
+ var url = uris[0];
+
+ if (url.substring(0, 16) == 'pebblejs://close') {
+ pebble.configurationClosed(settings.uuid, url);
+ pageStack.pop();
+ }
+ }
+ }
+ }
+}
diff --git a/rockwork/qml/AppStoreDetailsPage.qml b/rockwork/qml/AppStoreDetailsPage.qml
new file mode 100644
index 0000000..696e3c6
--- /dev/null
+++ b/rockwork/qml/AppStoreDetailsPage.qml
@@ -0,0 +1,278 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.ListItems 1.3
+import QtGraphicalEffects 1.0
+
+Page {
+ id: root
+ title: i18n.tr("App details")
+
+ property var pebble: null
+ property var app: null
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: units.gu(1)
+
+ Item {
+ Layout.fillWidth: true
+ height: headerColumn.height + units.gu(1)
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ spacing: units.gu(1)
+ height: headerColumn.height
+
+ UbuntuShape {
+ id: iconShape
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+
+ source: Image {
+ height: iconShape.height
+ width: iconShape.width
+ source: root.app.icon
+ }
+ }
+
+ ColumnLayout {
+ id: headerColumn
+ Layout.fillWidth: true
+ Label {
+ text: root.app.name
+ fontSize: "large"
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ }
+ Label {
+ text: root.app.vendor
+ Layout.fillWidth: true
+ }
+ }
+
+ Button {
+ id: installButton
+ text: enabled ? i18n.tr("Install") : (installing && !installed ? i18n.tr("Installing...") : i18n.tr("Installed"))
+ color: UbuntuColors.green
+ enabled: !installed && !installing
+ property bool installing: false
+ property bool installed: root.pebble.installedApps.contains(root.app.storeId) || root.pebble.installedWatchfaces.contains(root.app.storeId)
+ Connections {
+ target: root.pebble.installedApps
+ onChanged: {
+ installButton.installed = root.pebble.installedApps.contains(root.app.storeId) || root.pebble.installedWatchfaces.contains(root.app.storeId)
+ }
+ }
+
+ Connections {
+ target: root.pebble.installedWatchfaces
+ onChanged: {
+ installButton.installed = root.pebble.installedApps.contains(root.app.storeId) || root.pebble.installedWatchfaces.contains(root.app.storeId)
+ }
+ }
+
+ onClicked: {
+ root.pebble.installApp(root.app.storeId)
+ installButton.installing = true
+ }
+ }
+ }
+ }
+
+ Flickable {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ contentHeight: contentColumn.height
+ bottomMargin: units.gu(1)
+ clip: true
+
+ Column {
+ id: contentColumn
+ width: parent.width
+ height: childrenRect.height
+
+ Image {
+ width: parent.width
+ // ss.w : ss.h = w : h
+ height: sourceSize.height * width / sourceSize.width
+ fillMode: Image.PreserveAspectFit
+ source: root.app.headerImage
+ }
+
+ RowLayout {
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ height: units.gu(6)
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Row {
+ anchors.centerIn: parent
+ spacing: units.gu(1)
+ Icon {
+ name: "like"
+ height: parent.height
+ width: height
+ }
+ Label {
+ text: root.app.hearts
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.preferredHeight: parent.height - units.gu(2)
+ Layout.preferredWidth: units.dp(1)
+ color: UbuntuColors.lightGrey
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Row {
+ anchors.centerIn: parent
+ spacing: units.gu(1)
+ Icon {
+ name: root.app.isWatchFace ? "clock-app-symbolic" : "stock_application"
+ height: parent.height
+ width: height
+ }
+ Label {
+ text: root.app.isWatchFace ? "Watchface" : "Watchapp"
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ anchors { left: parent.left; right: parent.right; margins: units.gu(1) }
+ spacing: units.gu(1)
+
+ PebbleModels {
+ id: modelModel
+ }
+
+
+ Item {
+ id: screenshotsItem
+ Layout.preferredHeight: units.gu(20)
+ Layout.fillWidth: true
+
+ property bool isRound: modelModel.get(root.pebble.model).shape === "round"
+
+ ListView {
+ id: screenshotsListView
+ anchors.centerIn: parent
+ width: parent.width
+ height: screenshotsItem.isRound ? units.gu(10) : units.gu(9.5)
+ orientation: ListView.Horizontal
+ spacing: units.gu(1)
+ snapMode: ListView.SnapToItem
+ preferredHighlightBegin: (screenshotsListView.width - height * .95) / 2
+ preferredHighlightEnd: (screenshotsListView.width + height * .95) / 2
+ highlightRangeMode: ListView.StrictlyEnforceRange
+
+ model: root.app.screenshotImages
+ delegate: AnimatedImage {
+ height: screenshotsListView.height
+ width: height * 0.95
+ fillMode: Image.PreserveAspectFit
+ source: modelData
+ }
+ }
+ Image {
+ id: watchImage
+ // ssw : ssh = w : h
+ height: parent.height
+ width: height * sourceSize.width / sourceSize.height
+ fillMode: Image.PreserveAspectFit
+ anchors.centerIn: parent
+ source: modelModel.get(root.pebble.model).image
+ Rectangle {
+ anchors.centerIn: parent
+ height: units.gu(10)
+ width: height
+ color: "black"
+ radius: screenshotsItem.isRound ? height / 2 : 0
+ }
+ }
+
+ OpacityMask {
+ anchors.fill: screenshotsListView
+ source: screenshotsListView
+ maskSource: maskRect
+ }
+
+ Rectangle {
+ id: maskRect
+ anchors.fill: screenshotsListView
+ color: "transparent"
+ visible: false
+
+ Rectangle {
+ color: "blue"
+ anchors.centerIn: parent
+ height: screenshotsListView.height
+ width: screenshotsItem.isRound ? height : height * 0.9
+ radius: screenshotsItem.isRound ? height / 2 : units.gu(.5)
+// anchors.fill: watchImage
+// anchors.margins: units.gu(5)
+// radius: modelModel.get(root.pebble.model).shape === "rectangle" ? units.gu(.5) : height / 2
+// visible: false
+ }
+ }
+
+ }
+
+ Label {
+ Layout.fillWidth: true
+ font.bold: true
+ text: i18n.tr("Description")
+ }
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: units.dp(1)
+ color: UbuntuColors.lightGrey
+ }
+
+ Label {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ wrapMode: Text.WordWrap
+ text: root.app.description
+ }
+
+ GridLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ columns: 2
+ columnSpacing: units.gu(1)
+ rowSpacing: units.gu(1)
+ Label {
+ text: i18n.tr("Developer")
+ font.bold: true
+ }
+ Label {
+ text: root.app.vendor
+ Layout.fillWidth: true
+ }
+ Label {
+ text: i18n.tr("Version")
+ font.bold: true
+ }
+ Label {
+ text: root.app.version
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/rockwork/qml/AppStorePage.qml b/rockwork/qml/AppStorePage.qml
new file mode 100644
index 0000000..bb8712b
--- /dev/null
+++ b/rockwork/qml/AppStorePage.qml
@@ -0,0 +1,266 @@
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import QtQuick.Layouts 1.1
+import RockWork 1.0
+
+Page {
+ id: root
+ title: showWatchApps ? i18n.tr("Add new watchapp") : i18n.tr("Add new watchface")
+
+ property var pebble: null
+ property bool showWatchApps: false
+ property bool showWatchFaces: false
+
+ property string link: ""
+
+ function fetchHome() {
+ if (showWatchApps) {
+ client.fetchHome(AppStoreClient.TypeWatchapp)
+ } else {
+ client.fetchHome(AppStoreClient.TypeWatchface)
+ }
+ }
+
+ head {
+ actions: [
+ Action {
+ iconName: "search"
+ onTriggered: {
+ if (searchField.shown) {
+ searchField.shown = false;
+ root.fetchHome();
+ } else {
+ searchField.shown = true;
+ }
+ }
+ }
+ ]
+ }
+
+ Component.onCompleted: {
+ if (root.link) {
+ client.fetchLink(link)
+ } else {
+ root.fetchHome()
+ }
+ }
+
+ AppStoreClient {
+ id: client
+ hardwarePlatform: pebble.hardwarePlatform
+ }
+
+ Item {
+ id: searchField
+ anchors { left: parent.left; right: parent.right; top: parent.top }
+ anchors.topMargin: shown ? 0 : -height
+ Behavior on anchors.topMargin { UbuntuNumberAnimation {} }
+ opacity: shown ? 1 : 0
+ Behavior on opacity { UbuntuNumberAnimation {} }
+ height: units.gu(6)
+
+ property bool shown: false
+ onShownChanged: {
+ if (shown) {
+ searchTextField.focus = true;
+ }
+ }
+
+ TextField {
+ id: searchTextField
+ anchors.centerIn: parent
+ width: parent.width - units.gu(2)
+ onDisplayTextChanged: {
+ searchTimer.restart()
+ }
+
+ Timer {
+ id: searchTimer
+ interval: 300
+ onTriggered: {
+ client.search(searchTextField.displayText, root.showWatchApps ? AppStoreClient.TypeWatchapp : AppStoreClient.TypeWatchface);
+ }
+ }
+ }
+ }
+
+ Item {
+ anchors { left: parent.left; top: searchField.bottom; right: parent.right; bottom: parent.bottom }
+ ListView {
+ anchors.fill: parent
+ model: ApplicationsFilterModel {
+ id: appsFilterModel
+ model: client.model
+ }
+ clip: true
+ section.property: "groupId"
+ section.labelPositioning: ViewSection.CurrentLabelAtStart |
+ ViewSection.InlineLabels
+ section.delegate: ListItem {
+ height: section ? label.implicitHeight + units.gu(3) : 0
+
+ Rectangle {
+ anchors.fill: parent
+ color: "white"
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ Label {
+ id: label
+ text: client.model.groupName(section)
+ fontSize: "large"
+// font.weight: Font.DemiBold
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+ AbstractButton {
+ Layout.fillHeight: true
+ implicitWidth: seeAllLabel.implicitWidth + height
+ Row {
+ anchors.verticalCenter: parent.verticalCenter
+ Label {
+ id: seeAllLabel
+ text: i18n.tr("See all")
+ }
+ Icon {
+ implicitHeight: parent.height
+ implicitWidth: height
+ name: "go-next"
+ }
+ }
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("AppStorePage.qml"), {pebble: root.pebble, link: client.model.groupLink(section), title: client.model.groupName(section)});
+ }
+ }
+ }
+ }
+
+ footer: Item {
+ height: client.model.links.length > 0 ? units.gu(6) : 0
+ width: parent.width
+
+ RowLayout {
+ anchors {
+ fill: parent
+ margins: units.gu(1)
+ }
+ spacing: units.gu(1)
+
+ Repeater {
+ model: client.model.links
+ Button {
+ text: client.model.linkName(client.model.links[index])
+ onClicked: client.fetchLink(client.model.links[index]);
+ color: UbuntuColors.orange
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+
+ delegate: ListItem {
+ height: delegateColumn.height + units.gu(2)
+
+ RowLayout {
+ id: delegateRow
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ spacing: units.gu(1)
+
+ AnimatedImage {
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+ source: model.icon
+ asynchronous: true
+// sourceSize.width: width
+// sourceSize.height: height
+ }
+
+ ColumnLayout {
+ id: delegateColumn
+ Layout.fillWidth: true;
+ Layout.fillHeight: true;
+ Label {
+ Layout.fillWidth: true
+ text: model.name
+ font.weight: Font.DemiBold
+ elide: Text.ElideRight
+ }
+ Label {
+ Layout.fillWidth: true
+ text: model.category
+ }
+ RowLayout {
+ Icon {
+ name: "like"
+ Layout.preferredHeight: parent.height
+ Layout.preferredWidth: height
+ implicitHeight: parent.height
+ }
+ Label {
+ Layout.fillWidth: true
+ text: model.hearts
+ }
+ Icon {
+ id: tickIcon
+ name: "tick"
+ implicitHeight: parent.height
+ Layout.preferredWidth: height
+ visible: root.pebble.installedApps.contains(model.storeId) || root.pebble.installedWatchfaces.contains(model.storeId)
+ Connections {
+ target: root.pebble.installedApps
+ onChanged: {
+ tickIcon.visible = root.pebble.installedApps.contains(model.storeId) || root.pebble.installedWatchfaces.contains(model.storeId)
+ }
+ }
+
+ Connections {
+ target: root.pebble.installedWatchfaces
+ onChanged: {
+ tickIcon.visible = root.pebble.installedApps.contains(model.storeId) || root.pebble.installedWatchfaces.contains(model.storeId)
+ }
+ }
+
+ }
+ }
+ }
+
+ }
+
+ onClicked: {
+ client.fetchAppDetails(model.storeId);
+ pageStack.push(Qt.resolvedUrl("AppStoreDetailsPage.qml"), {app: appsFilterModel.get(index), pebble: root.pebble})
+ }
+ }
+ }
+
+// RowLayout {
+// id: buttonRow
+// anchors { left: parent.left; bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
+// spacing: units.gu(1)
+// Button {
+// text: i18n.tr("Previous")
+// Layout.fillWidth: true
+// enabled: client.offset > 0
+// onClicked: {
+// client.previous()
+// }
+// }
+// Button {
+// text: i18n.tr("Next")
+// Layout.fillWidth: true
+// onClicked: {
+// client.next()
+// }
+// }
+// }
+ }
+
+ ActivityIndicator {
+ anchors.centerIn: parent
+ running: client.busy
+ }
+}
+
diff --git a/rockwork/qml/ContentPeerPickerPage.qml b/rockwork/qml/ContentPeerPickerPage.qml
new file mode 100644
index 0000000..7ee9702
--- /dev/null
+++ b/rockwork/qml/ContentPeerPickerPage.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Content 1.3
+import RockWork 1.0
+
+Page {
+ id: pickerPage
+ head {
+ locked: true
+ visible: false
+ }
+
+ property alias contentType: contentPeerPicker.contentType
+ property string itemName
+ property alias handler: contentPeerPicker.handler
+ property string filename
+
+ Component {
+ id: exportItemComponent
+ ContentItem {
+ name: pickerPage.itemName
+ }
+ }
+ ContentPeerPicker {
+ id: contentPeerPicker
+ anchors.fill: parent
+
+ onCancelPressed: pageStack.pop()
+
+ onPeerSelected: {
+ var transfer = peer.request();
+ var items = [];
+ var item = exportItemComponent.createObject();
+ item.url = "file://" + pickerPage.filename;
+ items.push(item)
+ transfer.items = items;
+ transfer.state = ContentTransfer.Charged;
+ pageStack.pop();
+ }
+ }
+}
diff --git a/rockwork/qml/DeveloperToolsPage.qml b/rockwork/qml/DeveloperToolsPage.qml
new file mode 100644
index 0000000..2f77254
--- /dev/null
+++ b/rockwork/qml/DeveloperToolsPage.qml
@@ -0,0 +1,157 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Popups 1.3
+import Ubuntu.Content 1.3
+
+Page {
+ id: root
+ title: i18n.tr("Developer Tools")
+
+ property var pebble: null
+
+ //Creating the menu list this way to allow the text field to be translatable (http://askubuntu.com/a/476331)
+ ListModel {
+ id: devMenuModel
+ dynamicRoles: true
+ }
+
+ Component.onCompleted: {
+ populateDevMenu();
+ }
+
+ function populateDevMenu() {
+ devMenuModel.clear();
+
+ devMenuModel.append({
+ icon: "camera-app-symbolic",
+ text: i18n.tr("Screenshots"),
+ page: "ScreenshotsPage.qml",
+ dialog: "",
+ color: "gold"
+ });
+ devMenuModel.append({
+ icon: "dialog-warning-symbolic",
+ text: i18n.tr("Report problem"),
+ page: "",
+ dialog: sendLogsComponent,
+ color: UbuntuColors.red
+ });
+ devMenuModel.append({
+ icon: "stock_application",
+ text: i18n.tr("Install app or watchface from file"),
+ page: "ImportPackagePage.qml",
+ dialog: null,
+ color: UbuntuColors.blue
+ });
+
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Repeater {
+ id: menuRepeater
+ model: devMenuModel
+ delegate: ListItem {
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+
+ UbuntuShape {
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+ backgroundColor: model.color
+ Icon {
+ anchors.fill: parent
+ anchors.margins: units.gu(.5)
+ name: model.icon
+ color: "white"
+ }
+ }
+
+
+ Label {
+ text: model.text
+ Layout.fillWidth: true
+ }
+ }
+
+ onClicked: {
+ if (model.page) {
+ pageStack.push(Qt.resolvedUrl(model.page), {pebble: root.pebble})
+ }
+ if (model.dialog) {
+ PopupUtils.open(model.dialog)
+ }
+ }
+ }
+ }
+
+ Item {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ }
+
+ Component {
+ id: sendLogsComponent
+ Dialog {
+ id: sendLogsDialog
+ title: i18n.tr("Report problem")
+ ActivityIndicator {
+ id: busyIndicator
+ visible: false
+ running: visible
+ }
+ Label {
+ text: i18n.tr("Preparing logs package...")
+ visible: busyIndicator.visible
+ horizontalAlignment: Text.AlignHCenter
+ fontSize: "large"
+ }
+
+ Connections {
+ target: root.pebble
+ onLogsDumped: {
+ if (success) {
+ var filename = "/tmp/pebble.log"
+ pageStack.push(Qt.resolvedUrl("ContentPeerPickerPage.qml"), {itemName: i18n.tr("pebble.log"),handler: ContentHandler.Share, contentType: ContentType.All, filename: filename })
+ }
+ PopupUtils.close(sendLogsDialog)
+ }
+ }
+
+ Button {
+ text: i18n.tr("Send rockworkd.log")
+ color: UbuntuColors.blue
+ visible: !busyIndicator.visible
+ onClicked: {
+ var filename = homePath + "/.cache/upstart/rockworkd.log"
+ pageStack.push(Qt.resolvedUrl("ContentPeerPickerPage.qml"), {itemName: i18n.tr("rockworkd.log"),handler: ContentHandler.Share, contentType: ContentType.All, filename: filename })
+ PopupUtils.close(sendLogsDialog)
+ }
+ }
+ Button {
+ text: i18n.tr("Send watch logs")
+ color: UbuntuColors.blue
+ visible: !busyIndicator.visible
+ onClicked: {
+ busyIndicator.visible = true
+ root.pebble.dumpLogs("/tmp/pebble.log")
+ }
+ }
+ Button {
+ text: i18n.tr("Cancel")
+ color: UbuntuColors.red
+ visible: !busyIndicator.visible
+ onClicked: {
+ PopupUtils.close(sendLogsDialog)
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/rockwork/qml/FirmwareUpgradePage.qml b/rockwork/qml/FirmwareUpgradePage.qml
new file mode 100644
index 0000000..3281a12
--- /dev/null
+++ b/rockwork/qml/FirmwareUpgradePage.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Page {
+ id: root
+ title: i18n.tr("Firmware upgrade")
+
+ property var pebble: null
+
+ Column {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ spacing: units.gu(2)
+
+ Label {
+ text: i18n.tr("A new firmware upgrade is available for your Pebble smartwatch.")
+ fontSize: "large"
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+
+ Label {
+ text: i18n.tr("Currently installed firmware: %1").arg("<b>" + root.pebble.softwareVersion + "</b>")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+
+ Label {
+ text: i18n.tr("Candidate firmware version: %1").arg("<b>" + root.pebble.candidateVersion + "</b>")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+
+ Label {
+ text: "<b>" + i18n.tr("Release Notes: %1").arg("</b><br>" + root.pebble.firmwareReleaseNotes)
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+
+ Label {
+ text: "<b>" + i18n.tr("Important:") + "</b> " + i18n.tr("This update will also upgrade recovery data. Make sure your Pebble smartwarch is connected to a power adapter.")
+ width: parent.width
+ wrapMode: Text.WordWrap
+ visible: root.pebble.candidateVersion.indexOf("mig") > 0
+ }
+
+ Button {
+ text: "Upgrade now"
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: UbuntuColors.blue
+ onClicked: {
+ root.pebble.performFirmwareUpgrade();
+ pageStack.pop();
+ }
+ }
+ }
+}
+
diff --git a/rockwork/qml/HealthSettingsDialog.qml b/rockwork/qml/HealthSettingsDialog.qml
new file mode 100644
index 0000000..94e5d22
--- /dev/null
+++ b/rockwork/qml/HealthSettingsDialog.qml
@@ -0,0 +1,115 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Popups 1.3
+import Ubuntu.Components.ListItems 1.3
+
+Dialog {
+ id: root
+ title: i18n.tr("Health settings")
+
+ property var healthParams: null
+
+ signal accepted();
+
+ RowLayout {
+ Label {
+ text: i18n.tr("Health app enabled")
+ Layout.fillWidth: true
+ }
+ Switch {
+ id: enabledSwitch
+ checked: healthParams["enabled"]
+ }
+ }
+
+ ItemSelector {
+ id: genderSelector
+ model: [i18n.tr("Female"), i18n.tr("Male")]
+ selectedIndex: root.healthParams["gender"] === "female" ? 0 : 1
+ }
+
+ RowLayout {
+ Label {
+ text: i18n.tr("Age")
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: ageField
+ inputMethodHints: Qt.ImhDigitsOnly
+ text: healthParams["age"]
+ Layout.preferredWidth: units.gu(10)
+ }
+ }
+
+ RowLayout {
+ Label {
+ text: i18n.tr("Height (cm)")
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: heightField
+ inputMethodHints: Qt.ImhDigitsOnly
+ text: healthParams["height"]
+ Layout.preferredWidth: units.gu(10)
+ }
+ }
+
+ RowLayout {
+ Label {
+ text: i18n.tr("Weight")
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: weightField
+ inputMethodHints: Qt.ImhDigitsOnly
+ text: healthParams["weight"]
+ Layout.preferredWidth: units.gu(10)
+ }
+ }
+
+ RowLayout {
+ Label {
+ text: i18n.tr("I want to be more active")
+ Layout.fillWidth: true
+ }
+ Switch {
+ id: moreActiveSwitch
+ checked: healthParams["moreActive"]
+ }
+ }
+
+ RowLayout {
+ Label {
+ text: i18n.tr("I want to sleep more")
+ Layout.fillWidth: true
+ }
+ Switch {
+ id: sleepMoreSwitch
+ checked: healthParams["sleepMore"]
+ }
+ }
+
+
+ Button {
+ text: i18n.tr("OK")
+ color: UbuntuColors.green
+ onClicked: {
+ root.healthParams["enabled"] = enabledSwitch.checked;
+ root.healthParams["gender"] = genderSelector.selectedIndex == 0 ? "female" : "male"
+ root.healthParams["age"] = ageField.text;
+ root.healthParams["height"] = heightField.text;
+ root.healthParams["weight"] = weightField.text;
+ root.healthParams["moreActive"] = moreActiveSwitch.checked;
+ root.healthParams["sleepMore"] = sleepMoreSwitch.checked;
+ root.accepted();
+ PopupUtils.close(root);
+ }
+ }
+ Button {
+ text: i18n.tr("Cancel")
+ color: UbuntuColors.red
+ onClicked: PopupUtils.close(root)
+ }
+}
+
diff --git a/rockwork/qml/ImportPackagePage.qml b/rockwork/qml/ImportPackagePage.qml
new file mode 100644
index 0000000..4f86f78
--- /dev/null
+++ b/rockwork/qml/ImportPackagePage.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Content 1.3
+
+Page {
+ id: root
+ title: i18n.tr("Import watchapp or watchface")
+
+ property var pebble: null
+
+ ContentPeerPicker {
+ anchors.fill: parent
+ handler: ContentHandler.Source
+ contentType: ContentType.All
+ showTitle: false
+
+ onPeerSelected: {
+ var transfer = peer.request();
+
+ transfer.stateChanged.connect(function() {
+ if (transfer.state == ContentTransfer.Charged) {
+ for (var i = 0; i < transfer.items.length; i++) {
+ print("sideloading package", transfer.items[i].url)
+ root.pebble.sideloadApp(transfer.items[i].url)
+ }
+ pageStack.pop();
+ }
+ })
+ }
+ }
+}
+
diff --git a/rockwork/qml/InfoPage.qml b/rockwork/qml/InfoPage.qml
new file mode 100644
index 0000000..3eec387
--- /dev/null
+++ b/rockwork/qml/InfoPage.qml
@@ -0,0 +1,86 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.ListItems 1.3
+
+Page {
+ title: "About RockWork"
+
+ Flickable {
+ anchors.fill: parent
+ contentHeight: contentColumn.height + units.gu(4)
+
+ ColumnLayout {
+ id: contentColumn
+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(2) }
+ spacing: units.gu(2)
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: units.gu(2)
+ UbuntuShape {
+ source: Image {
+ anchors.fill: parent
+ source: "artwork/rockwork.svg"
+ }
+ height: units.gu(6)
+ width: height
+ }
+
+ Label {
+ text: i18n.tr("Version %1").arg(version)
+ Layout.fillWidth: true
+ fontSize: "large"
+ }
+ }
+
+ ThinDivider {}
+
+ Label {
+ text: i18n.tr("Contributors")
+ Layout.fillWidth: true
+ font.bold: true
+ }
+ Label {
+ text: "Michael Zanetti<br>Brian Douglas<br>Katharine Berry"
+ Layout.fillWidth: true
+ }
+
+ ThinDivider {}
+
+ Label {
+ text: i18n.tr("Legal")
+ Layout.fillWidth: true
+ font.bold: true
+ }
+
+ Label {
+ text: "This program is free software: you can redistribute it and/or modify" +
+ "it under the terms of the GNU General Public License as published by" +
+ "the Free Software Foundation, version 3 of the License.<br>" +
+
+ "This program is distributed in the hope that it will be useful," +
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of" +
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" +
+ "GNU General Public License for more details.<br>" +
+
+ "You should have received a copy of the GNU General Public License" +
+ "along with this program. If not, see <http://www.gnu.org/licenses/>."
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ }
+
+ Label {
+ text: i18n.tr("This application is neither affiliated with nor endorsed by Pebble Technology Corp.")
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ }
+ Label {
+ text: i18n.tr("Pebble is a trademark of Pebble Technology Corp.")
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ }
+ }
+ }
+}
+
diff --git a/rockwork/qml/InstalledAppDelegate.qml b/rockwork/qml/InstalledAppDelegate.qml
new file mode 100644
index 0000000..89f6ba8
--- /dev/null
+++ b/rockwork/qml/InstalledAppDelegate.qml
@@ -0,0 +1,88 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import RockWork 1.0
+
+ListItem {
+ id: root
+
+ property string uuid: ""
+ property string name: ""
+ property string iconSource: ""
+ property string vendor: ""
+ property bool hasSettings: false
+ property alias hasGrip: grip.visible
+ property bool isSystemApp: false
+
+ signal deleteApp();
+ signal configureApp();
+
+ leadingActions: ListItemActions {
+ actions: [
+ Action {
+ visible: !root.isSystemApp
+ iconName: "delete"
+ onTriggered: {
+ root.deleteApp();
+ }
+ }
+ ]
+ }
+
+ trailingActions: ListItemActions {
+ actions: [
+ Action {
+ visible: root.hasSettings
+ iconName: "settings"
+ onTriggered: {
+ print("settings triggered")
+ root.configureApp();
+ }
+ }
+ ]
+ }
+
+ RowLayout {
+ anchors {
+ fill: parent
+ margins: units.gu(1)
+ }
+ spacing: units.gu(1)
+
+ SystemAppIcon {
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+ isSystemApp: root.isSystemApp
+ uuid: root.uuid
+ iconSource: root.iconSource
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ Label {
+ text: root.name
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: root.vendor
+ Layout.fillWidth: true
+ fontSize: "small"
+ }
+ }
+
+ Item {
+ id: grip
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+ opacity: (root.contentMoving || root.swiped || root.dragging) ? 0 : 1
+ Behavior on opacity { UbuntuNumberAnimation {} }
+ Icon {
+ width: units.gu(3)
+ height: width
+ anchors.centerIn: parent
+ name: "grip-large"
+ }
+ }
+ }
+}
diff --git a/rockwork/qml/InstalledAppsPage.qml b/rockwork/qml/InstalledAppsPage.qml
new file mode 100644
index 0000000..a18cd3f
--- /dev/null
+++ b/rockwork/qml/InstalledAppsPage.qml
@@ -0,0 +1,201 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Popups 1.3
+import RockWork 1.0
+
+Page {
+ id: root
+ title: showWatchApps ? (showWatchFaces ? i18n.tr("Apps & Watchfaces") : i18n.tr("Apps")) : i18n.tr("Watchfaces")
+
+ property var pebble: null
+ property bool showWatchApps: false
+ property bool showWatchFaces: false
+
+ head {
+ actions: [
+ Action {
+ iconName: "add"
+ onTriggered: pageStack.push(Qt.resolvedUrl("AppStorePage.qml"), {pebble: root.pebble, showWatchApps: root.showWatchApps, showWatchFaces: root.showWatchFaces})
+ }
+ ]
+ }
+
+ function configureApp(uuid) {
+ // The health app is special :/
+ if (uuid == "{36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c}") {
+ var popup = PopupUtils.open(Qt.resolvedUrl("HealthSettingsDialog.qml"), root, {healthParams: pebble.healthParams});
+ popup.accepted.connect(function() {
+ pebble.healthParams = popup.healthParams
+ })
+ } else {
+ pebble.requestConfigurationURL(uuid);
+ }
+ }
+
+ Item {
+ anchors.fill: parent
+ ListView {
+ id: listView
+ anchors.fill: parent
+ model: root.showWatchApps ? root.pebble.installedApps : root.pebble.installedWatchfaces
+ clip: true
+ property real realContentY: contentY + originY
+
+ delegate: InstalledAppDelegate {
+ id: delegate
+ uuid: model.uuid
+ name: model.name
+ iconSource: model.icon
+ vendor: model.vendor
+ visible: dndArea.draggedIndex !== index
+ hasGrip: index > 0
+ isSystemApp: model.isSystemApp
+ hasSettings: model.hasSettings
+
+ onDeleteApp: {
+ pebble.removeApp(model.uuid)
+ }
+ onConfigureApp: {
+ root.configureApp(model.uuid)
+ }
+ onClicked: {
+ PopupUtils.open(dialogComponent, root, {app: listView.model.get(index)})
+ }
+ }
+ }
+ MouseArea {
+ id: dndArea
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ right: parent.right
+ }
+ drag.axis: Drag.YAxis
+ propagateComposedEvents: true
+ width: units.gu(5)
+
+ property int startY: 0
+ property int draggedIndex: -1
+
+
+ onPressAndHold: {
+ startY = mouseY;
+ draggedIndex = Math.floor((listView.realContentY + mouseY) / fakeDragItem.height)
+ if (draggedIndex == 0) {
+ print("cannot drag settings app");
+ return;
+ }
+
+ var draggedItem = listView.model.get(draggedIndex);
+ fakeDragItem.uuid = draggedItem.uuid;
+ fakeDragItem.name = draggedItem.name;
+ fakeDragItem.vendor = draggedItem.vendor;
+ fakeDragItem.iconSource = draggedItem.icon;
+ fakeDragItem.isSystemApp = draggedItem.isSystemApp;
+ fakeDragItem.y = (fakeDragItem.height * draggedIndex) - listView.realContentY
+ drag.target = fakeDragItem;
+ }
+
+ onMouseYChanged: {
+ var newIndex = Math.floor((listView.realContentY + mouseY) / fakeDragItem.height)
+
+ if (newIndex > draggedIndex) {
+ newIndex = draggedIndex + 1;
+ } else if (newIndex < draggedIndex) {
+ newIndex = draggedIndex - 1;
+ } else {
+ return;
+ }
+
+ if (newIndex >= 1 && newIndex < listView.count) {
+ listView.model.move(draggedIndex, newIndex);
+ draggedIndex = newIndex;
+ }
+ }
+
+ onReleased: {
+ if (draggedIndex > -1) {
+ listView.model.commitMove();
+ draggedIndex = -1;
+ drag.target = null;
+ }
+ }
+ }
+ }
+
+
+
+ InstalledAppDelegate {
+ id: fakeDragItem
+ visible: dndArea.draggedIndex != -1
+
+ }
+
+ Component {
+ id: dialogComponent
+ Dialog {
+ id: dialog
+ property var app: null
+
+ RowLayout {
+ SystemAppIcon {
+ height: titleCol.height
+ width: height
+ isSystemApp: app.isSystemApp
+ uuid: app.uuid
+ iconSource: app.icon
+ }
+
+ ColumnLayout {
+ id: titleCol
+ Layout.fillWidth: true
+
+ Label {
+ Layout.fillWidth: true
+ text: app.name
+ fontSize: "large"
+ }
+ Label {
+ Layout.fillWidth: true
+ text: app.vendor
+ }
+ }
+ }
+
+ Button {
+ text: i18n.tr("Launch")
+ color: UbuntuColors.green
+ onClicked: {
+ pebble.launchApp(app.uuid);
+ PopupUtils.close(dialog);
+ }
+ }
+
+ Button {
+ text: i18n.tr("Configure")
+ color: UbuntuColors.blue
+ visible: app.hasSettings
+ onClicked: {
+ root.configureApp(app.uuid);
+ PopupUtils.close(dialog);
+ }
+ }
+
+ Button {
+ text: i18n.tr("Delete")
+ color: UbuntuColors.red
+ visible: !app.isSystemApp
+ onClicked: {
+ pebble.removeApp(app.uuid);
+ PopupUtils.close(dialog);
+ }
+ }
+
+ Button {
+ text: i18n.tr("Close")
+ onClicked: PopupUtils.close(dialog)
+ }
+ }
+ }
+}
diff --git a/rockwork/qml/Main.qml b/rockwork/qml/Main.qml
new file mode 100644
index 0000000..2bdece3
--- /dev/null
+++ b/rockwork/qml/Main.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import RockWork 1.0
+
+/*!
+ \brief MainView with a Label and Button elements.
+*/
+
+MainView {
+ applicationName: "rockwork.mzanetti"
+
+ width: units.gu(40)
+ height: units.gu(70)
+
+ ServiceController {
+ id: serviceController
+ serviceName: "rockworkd"
+ Component.onCompleted: {
+ if (!serviceController.serviceFileInstalled) {
+ print("Service file not installed. Installing now.")
+ serviceController.installServiceFile();
+ }
+ if (!serviceController.serviceRunning) {
+ print("Service not running. Starting now.")
+ serviceController.startService();
+ }
+ if (pebbles.version !== version) {
+ print("Service file version (", version, ") is not equal running service version (", pebbles.version, "). Restarting service.")
+ serviceController.restartService();
+ }
+ }
+ }
+
+ Pebbles {
+ id: pebbles
+ onCountChanged: loadStack()
+ }
+
+ function loadStack() {
+ pageStack.clear()
+ if (pebbles.count == 1) {
+ pageStack.push(Qt.resolvedUrl("MainMenuPage.qml"), {pebble: pebbles.get(0)})
+ } else {
+ pageStack.push(Qt.resolvedUrl("PebblesPage.qml"))
+ }
+ }
+
+ PageStack {
+ id: pageStack
+ Component.onCompleted: loadStack();
+ }
+}
diff --git a/rockwork/qml/MainMenuPage.qml b/rockwork/qml/MainMenuPage.qml
new file mode 100644
index 0000000..32c7b96
--- /dev/null
+++ b/rockwork/qml/MainMenuPage.qml
@@ -0,0 +1,317 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+
+Page {
+ id: root
+ title: pebble.name
+
+ property var pebble: null
+
+ head {
+ actions: [
+ Action {
+ iconName: "info"
+ text: i18n.tr("About")
+ onTriggered: {
+ pageStack.push(Qt.resolvedUrl("InfoPage.qml"))
+ }
+ },
+ Action {
+ iconName: "ubuntu-sdk-symbolic"
+ text: i18n.tr("Developer tools")
+ onTriggered: {
+ pageStack.push(Qt.resolvedUrl("DeveloperToolsPage.qml"), {pebble: root.pebble})
+ }
+ }
+ ]
+ }
+
+ //Creating the menu list this way to allow the text field to be translatable (http://askubuntu.com/a/476331)
+ ListModel {
+ id: mainMenuModel
+ dynamicRoles: true
+ }
+
+ Component.onCompleted: {
+ populateMainMenu();
+ }
+
+ Connections {
+ target: root.pebble
+ onFirmwareUpgradeAvailableChanged: {
+ populateMainMenu();
+ }
+ }
+
+ function populateMainMenu() {
+ mainMenuModel.clear();
+
+ mainMenuModel.append({
+ icon: "stock_notification",
+ text: i18n.tr("Manage notifications"),
+ page: "NotificationsPage.qml",
+ color: "blue"
+ });
+
+ mainMenuModel.append({
+ icon: "stock_application",
+ text: i18n.tr("Manage Apps"),
+ page: "InstalledAppsPage.qml",
+ showWatchApps: true,
+ color: UbuntuColors.green
+ });
+
+ mainMenuModel.append({
+ icon: "clock-app-symbolic",
+ text: i18n.tr("Manage Watchfaces"),
+ page: "InstalledAppsPage.qml",
+ showWatchFaces: true,
+ color: "black"
+ });
+
+ mainMenuModel.append({
+ icon: "settings",
+ text: i18n.tr("Settings"),
+ page: "SettingsPage.qml",
+ showWatchFaces: true,
+ color: "gold"
+ });
+
+ if (root.pebble.firmwareUpgradeAvailable) {
+ mainMenuModel.append({
+ icon: "preferences-system-updates-symbolic",
+ text: i18n.tr("Firmware upgrade"),
+ page: "FirmwareUpgradePage.qml",
+ color: "red"
+ });
+ }
+
+ }
+
+ PebbleModels {
+ id: modelModel
+ }
+
+ GridLayout {
+ anchors.fill: parent
+ columns: parent.width > parent.height ? 2 : 1
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.maximumHeight: units.gu(30)
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ spacing: units.gu(1)
+
+ Item {
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.minimumWidth: watchImage.width
+ Image {
+ id: watchImage
+ width: implicitWidth * height / implicitHeight
+ height: parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ source: modelModel.get(root.pebble.model).image
+ fillMode: Image.PreserveAspectFit
+
+ Item {
+ id: watchFace
+ height: parent.height * (modelModel.get(root.pebble.model - 1).shape === "rectangle" ? .5 : .515)
+ width: height * (modelModel.get(root.pebble.model - 1).shape === "rectangle" ? .85 : 1)
+ anchors.centerIn: parent
+ anchors.horizontalCenterOffset: units.dp(1)
+ anchors.verticalCenterOffset: units.dp(modelModel.get(root.pebble.model - 1).shape === "rectangle" ? 0 : 1)
+
+ Image {
+ id: image
+ anchors.fill: parent
+ source: "file://" + root.pebble.screenshots.latestScreenshot
+ visible: false
+ }
+
+ Component.onCompleted: {
+ if (!root.pebble.screenshots.latestScreenshot) {
+ root.pebble.requestScreenshot();
+ }
+ }
+
+ Rectangle {
+ id: textItem
+ anchors.fill: parent
+ layer.enabled: true
+ radius: modelModel.get(root.pebble.model - 1).shape === "rectangle" ? units.gu(.5) : height / 2
+ // This item should be used as the 'mask'
+ layer.samplerName: "maskSource"
+ layer.effect: ShaderEffect {
+ property var colorSource: image;
+ fragmentShader: "
+ uniform lowp sampler2D colorSource;
+ uniform lowp sampler2D maskSource;
+ uniform lowp float qt_Opacity;
+ varying highp vec2 qt_TexCoord0;
+ void main() {
+ gl_FragColor =
+ texture2D(colorSource, qt_TexCoord0)
+ * texture2D(maskSource, qt_TexCoord0).a
+ * qt_Opacity;
+ }
+ "
+ }
+ }
+ }
+ }
+ }
+ ColumnLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ spacing: units.gu(2)
+ Rectangle {
+ height: units.gu(10)
+ width: height
+ radius: height / 2
+ color: root.pebble.connected ? UbuntuColors.green : UbuntuColors.red
+
+ Icon {
+ anchors.fill: parent
+ anchors.margins: units.gu(2)
+ color: "white"
+ name: root.pebble.connected ? "tick" : "dialog-error-symbolic"
+ }
+ }
+
+ Label {
+ text: root.pebble.connected ? i18n.tr("Connected") : i18n.tr("Disconnected")
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+
+
+ Column {
+ Layout.fillWidth: true
+ Layout.preferredHeight: childrenRect.height
+ spacing: menuRepeater.count > 0 ? 0 : units.gu(2)
+ Label {
+ text: i18n.tr("Your Pebble smartwatch is disconnected. Please make sure it is powered on, within range and it is paired properly in the Bluetooth System Settings.")
+ width: parent.width - units.gu(4)
+ anchors.horizontalCenter: parent.horizontalCenter
+ wrapMode: Text.WordWrap
+ visible: !root.pebble.connected
+ fontSize: "large"
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ text: i18n.tr("Open System Settings")
+ visible: !root.pebble.connected
+ onClicked: Qt.openUrlExternally("settings://system/bluetooth")
+ color: UbuntuColors.orange
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Label {
+ text: i18n.tr("Your Pebble smartwatch is in factory mode and needs to be initialized.")
+ width: parent.width - units.gu(4)
+ anchors.horizontalCenter: parent.horizontalCenter
+ wrapMode: Text.WordWrap
+ visible: root.pebble.connected && root.pebble.recovery && !root.pebble.upgradingFirmware
+ fontSize: "large"
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: i18n.tr("Initialize Pebble")
+ onClicked: root.pebble.performFirmwareUpgrade();
+ visible: root.pebble.connected && root.pebble.recovery && !root.pebble.upgradingFirmware
+ color: UbuntuColors.orange
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Rectangle {
+ id: upgradeIcon
+ height: units.gu(10)
+ width: height
+ radius: width / 2
+ color: UbuntuColors.orange
+ anchors.horizontalCenter: parent.horizontalCenter
+ Icon {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ name: "preferences-system-updates-symbolic"
+ color: "white"
+ }
+
+ RotationAnimation on rotation {
+ duration: 2000
+ loops: Animation.Infinite
+ from: 0
+ to: 360
+ running: upgradeIcon.visible
+ }
+ visible: root.pebble.connected && root.pebble.upgradingFirmware
+ }
+
+ Label {
+ text: i18n.tr("Upgrading...")
+ fontSize: "large"
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: root.pebble.connected && root.pebble.upgradingFirmware
+ }
+
+ Repeater {
+ id: menuRepeater
+ model: root.pebble.connected && !root.pebble.recovery && !root.pebble.upgradingFirmware ? mainMenuModel : null
+ delegate: ListItem {
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+
+ UbuntuShape {
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+ backgroundColor: model.color
+ Icon {
+ anchors.fill: parent
+ anchors.margins: units.gu(.5)
+ name: model.icon
+ color: "white"
+ }
+ }
+
+
+ Label {
+ text: model.text
+ Layout.fillWidth: true
+ }
+ }
+
+ onClicked: {
+ var options = {};
+ options["pebble"] = root.pebble
+ var modelItem = mainMenuModel.get(index)
+ options["showWatchApps"] = modelItem.showWatchApps
+ options["showWatchFaces"] = modelItem.showWatchFaces
+ pageStack.push(Qt.resolvedUrl(model.page), options)
+ }
+ }
+ }
+ }
+ }
+
+ Connections {
+ target: pebble
+ onOpenURL: {
+ if (url) {
+ pageStack.push(Qt.resolvedUrl("AppSettingsPage.qml"), {uuid: uuid, url: url, pebble: pebble})
+ }
+ }
+ }
+}
diff --git a/rockwork/qml/NotificationsPage.qml b/rockwork/qml/NotificationsPage.qml
new file mode 100644
index 0000000..9802b05
--- /dev/null
+++ b/rockwork/qml/NotificationsPage.qml
@@ -0,0 +1,88 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import RockWork 1.0
+
+Page {
+ id: root
+ title: i18n.tr("Notifications")
+
+ property var pebble: null
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.topMargin: units.gu(1)
+
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: infoLabel.height
+
+ Label {
+ id: infoLabel
+ anchors {
+ left: parent.left
+ right: parent.right
+ margins: units.gu(2)
+ }
+
+ wrapMode: Text.WordWrap
+ text: i18n.tr("Entries here will be added as notifications appear on the phone. Selected notifications will be shown on your Pebble smartwatch.")
+ }
+ }
+
+
+ ListView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ clip: true
+ model: root.pebble.notifications
+
+ delegate: ListItem {
+ ListItemLayout {
+ title.text: model.name
+
+ UbuntuShape {
+ SlotsLayout.position: SlotsLayout.Leading;
+ height: units.gu(5)
+ width: height
+ backgroundColor: {
+ // Add some hacks for known icons
+ switch (model.icon) {
+ case "calendar":
+ return UbuntuColors.orange;
+ case "settings":
+ return "grey";
+ case "dialog-question-symbolic":
+ return UbuntuColors.red;
+ case "alarm-clock":
+ return UbuntuColors.purple;
+ case "gpm-battery-050":
+ return UbuntuColors.green;
+ }
+ return "black"
+ }
+ source: Image {
+ height: parent.height
+ width: parent.width
+ source: model.icon.indexOf("/") === 0 ? "file://" + model.icon : ""
+ }
+ Icon {
+ anchors.fill: parent
+ anchors.margins: units.gu(.5)
+ name: model.icon.indexOf("/") !== 0 ? model.icon : ""
+ color: "white"
+ }
+ }
+
+ Switch {
+ checked: model.enabled
+ SlotsLayout.position: SlotsLayout.Trailing;
+ onClicked: {
+ root.pebble.setNotificationFilter(model.name, checked)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/rockwork/qml/PebbleModels.qml b/rockwork/qml/PebbleModels.qml
new file mode 100644
index 0000000..103064a
--- /dev/null
+++ b/rockwork/qml/PebbleModels.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.4
+
+ListModel {
+ id: modelModel
+ ListElement { image: 'artwork/tintin-black.png'; shape: "rectangle" } // Fallback for Unknown
+ ListElement { image: 'artwork/tintin-black.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-white.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-red.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-orange.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-grey.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/bianca-silver.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/bianca-black.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-blue.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-green.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/tintin-pink.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/snowy-white.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/snowy-black.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/snowy-red.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/bobby-silver.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/bobby-black.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/bobby-gold.png'; shape: "rectangle" }
+ ListElement { image: 'artwork/spalding-14mm-silver.png'; shape: "round" }
+ ListElement { image: 'artwork/spalding-14mm-black.png'; shape: "round" }
+ ListElement { image: 'artwork/spalding-20mm-silver.png'; shape: "round" }
+ ListElement { image: 'artwork/spalding-20mm-black.png'; shape: "round" }
+ ListElement { image: 'artwork/spalding-14mm-rose-gold.png'; shape: "round" }
+}
+
diff --git a/rockwork/qml/PebblesPage.qml b/rockwork/qml/PebblesPage.qml
new file mode 100644
index 0000000..a973b0a
--- /dev/null
+++ b/rockwork/qml/PebblesPage.qml
@@ -0,0 +1,69 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+
+Page {
+ title: i18n.tr("Manage Pebble Watches")
+
+ head {
+ actions: [
+ Action {
+ iconName: "settings"
+ onTriggered: {
+ onClicked: Qt.openUrlExternally("settings://system/bluetooth")
+ }
+ }
+ ]
+ }
+
+ ListView {
+ anchors.fill: parent
+ model: pebbles
+ delegate: ListItem {
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+
+ ColumnLayout {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ Label {
+ text: model.name
+ }
+
+ Label {
+ text: model.connected ? i18n.tr("Connected") : i18n.tr("Disconnected")
+ fontSize: "small"
+ }
+ }
+ }
+
+ onClicked: {
+ var p = pebbles.get(index);
+ print("opening pebble:", p.name, p.hardwarePlatform)
+ pageStack.push(Qt.resolvedUrl("MainMenuPage.qml"), {pebble: pebbles.get(index)})
+ }
+ }
+ }
+
+ Column {
+ anchors.centerIn: parent
+ width: parent.width - units.gu(4)
+ spacing: units.gu(4)
+ visible: pebbles.count === 0
+
+ Label {
+ text: i18n.tr("No Pebble smartwatches configured yet. Please connect your Pebble smartwatch using System Settings.")
+ fontSize: "large"
+ width: parent.width
+ wrapMode: Text.WordWrap
+ }
+
+ Button {
+ text: i18n.tr("Open System Settings")
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: Qt.openUrlExternally("settings://system/bluetooth")
+ }
+ }
+}
diff --git a/rockwork/qml/ScreenshotsPage.qml b/rockwork/qml/ScreenshotsPage.qml
new file mode 100644
index 0000000..fdbeb9a
--- /dev/null
+++ b/rockwork/qml/ScreenshotsPage.qml
@@ -0,0 +1,107 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Popups 1.3
+import Ubuntu.Content 1.3
+import RockWork 1.0
+
+Page {
+ id: root
+
+ title: i18n.tr("Screenshots")
+
+ property var pebble: null
+
+ head {
+ actions: [
+ Action {
+ iconName: "camera-app-symbolic"
+ onTriggered: root.pebble.requestScreenshot()
+ }
+ ]
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ spacing: units.gu(1)
+
+ GridView {
+ id: grid
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ clip: true
+
+ property int columns: 2
+
+ cellWidth: width / columns
+ cellHeight: cellWidth
+
+ model: root.pebble.screenshots
+
+ displaced: Transition {
+ UbuntuNumberAnimation { properties: "x,y" }
+ }
+
+ delegate: Item {
+ width: grid.cellWidth
+ height: grid.cellHeight
+ Image {
+ anchors.fill: parent
+ anchors.margins: units.gu(.5)
+ fillMode: Image.PreserveAspectFit
+ source: "file://" + model.filename
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ PopupUtils.open(dialogComponent, root, {filename: model.filename})
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: dialogComponent
+ Dialog {
+ id: dialog
+ title: i18n.tr("Screenshot options")
+
+ property string filename
+
+ Button {
+ text: i18n.tr("Share")
+ color: UbuntuColors.blue
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("ContentPeerPickerPage.qml"), {itemName: i18n.tr("Pebble screenshot"), handler: ContentHandler.Share, contentType: ContentType.Pictures, filename: filename })
+ PopupUtils.close(dialog)
+ }
+ }
+ Button {
+ text: i18n.tr("Save")
+ color: UbuntuColors.green
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("ContentPeerPickerPage.qml"), {itemName: i18n.tr("Pebble screenshot"),handler: ContentHandler.Destination, contentType: ContentType.Pictures, filename: filename })
+ PopupUtils.close(dialog)
+ }
+ }
+
+ Button {
+ text: i18n.tr("Delete")
+ color: UbuntuColors.red
+ onClicked: {
+ root.pebble.removeScreenshot(filename)
+ PopupUtils.close(dialog)
+ }
+ }
+ Button {
+ text: i18n.tr("Cancel")
+ onClicked: {
+ PopupUtils.close(dialog)
+ }
+ }
+ }
+ }
+}
+
diff --git a/rockwork/qml/SettingsPage.qml b/rockwork/qml/SettingsPage.qml
new file mode 100644
index 0000000..153aaf4
--- /dev/null
+++ b/rockwork/qml/SettingsPage.qml
@@ -0,0 +1,80 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import Ubuntu.Components 1.3
+import Ubuntu.Components.ListItems 1.3
+
+Page {
+ id: root
+ title: i18n.tr("Settings")
+
+ property var pebble: null
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: units.gu(1)
+ spacing: units.gu(1)
+
+ Label {
+ Layout.fillWidth: true
+ text: i18n.tr("Distance Units")
+ font.bold: true
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ CheckBox {
+ id: metricUnitsCheckbox
+ checked: !root.pebble.imperialUnits
+ onClicked: {
+ checked = true
+ root.pebble.imperialUnits = false;
+ imperialUnitsCheckBox.checked = false;
+ }
+ }
+ Label {
+ text: i18n.tr("Metric")
+ Layout.fillWidth: true
+ }
+ CheckBox {
+ id: imperialUnitsCheckBox
+ checked: root.pebble.imperialUnits
+ onClicked: {
+ checked = true
+ root.pebble.imperialUnits = true;
+ metricUnitsCheckbox.checked = false;
+ }
+ }
+ Label {
+ text: i18n.tr("Imperial")
+ Layout.fillWidth: true
+ }
+ }
+ ThinDivider {}
+
+ Label {
+ text: i18n.tr("Calendar")
+ Layout.fillWidth: true
+ font.bold: true
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Label {
+ text: i18n.tr("Sync calendar to timeline")
+ Layout.fillWidth: true
+ }
+ Switch {
+ checked: root.pebble.calendarSyncEnabled
+ onClicked: {
+ root.pebble.calendarSyncEnabled = checked;
+ }
+ }
+ }
+ ThinDivider {}
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+}
+
diff --git a/rockwork/qml/SystemAppIcon.qml b/rockwork/qml/SystemAppIcon.qml
new file mode 100644
index 0000000..88e37bc
--- /dev/null
+++ b/rockwork/qml/SystemAppIcon.qml
@@ -0,0 +1,67 @@
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Item {
+ id: root
+
+ property bool isSystemApp: false
+ property string uuid: ""
+ property string iconSource: ""
+
+ UbuntuShape {
+ anchors.fill: parent
+ visible: root.isSystemApp
+ backgroundColor: {
+ switch (root.uuid) {
+ case "{07e0d9cb-8957-4bf7-9d42-35bf47caadfe}":
+ return "gray";
+ case "{18e443ce-38fd-47c8-84d5-6d0c775fbe55}":
+ return "blue";
+ case "{36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c}":
+ return UbuntuColors.red;
+ case "{1f03293d-47af-4f28-b960-f2b02a6dd757}":
+ return "gold"
+ case "{b2cae818-10f8-46df-ad2b-98ad2254a3c1}":
+ return "darkviolet"
+ case "{67a32d95-ef69-46d4-a0b9-854cc62f97f9}":
+ return "green";
+ case "{8f3c8686-31a1-4f5f-91f5-01600c9bdc59}":
+ return "black"
+ }
+
+ return "";
+ }
+ }
+ Icon {
+ anchors.fill: parent
+ implicitHeight: height
+ anchors.margins: units.gu(1)
+ visible: root.isSystemApp
+ color: "white"
+ name: {
+ switch (root.uuid) {
+ case "{07e0d9cb-8957-4bf7-9d42-35bf47caadfe}":
+ return "settings";
+ case "{18e443ce-38fd-47c8-84d5-6d0c775fbe55}":
+ return "clock-app-symbolic";
+ case "{36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c}":
+ return "like";
+ case "{1f03293d-47af-4f28-b960-f2b02a6dd757}":
+ return "stock_music";
+ case "{b2cae818-10f8-46df-ad2b-98ad2254a3c1}":
+ return "stock_notification";
+ case "{67a32d95-ef69-46d4-a0b9-854cc62f97f9}":
+ return "stock_alarm-clock";
+ case "{8f3c8686-31a1-4f5f-91f5-01600c9bdc59}":
+ return "clock-app-symbolic";
+ }
+ return "";
+ }
+ }
+
+ Image {
+ source: root.isSystemApp ? "" : "file://" + root.iconSource;
+ anchors.fill: parent
+ visible: !root.isSystemApp
+ }
+}