Merge lp:~ralsina/ubuntu-push/bring-in-examples into lp:ubuntu-push

Proposed by Roberto Alsina
Status: Superseded
Proposed branch: lp:~ralsina/ubuntu-push/bring-in-examples
Merge into: lp:ubuntu-push
Diff against target: 1614 lines (+932/-498)
20 files modified
debian/ubuntu-push-client.conf (+4/-0)
docs/_common.txt (+186/-0)
docs/_description.txt (+46/-0)
docs/example-client/Makefile (+22/-0)
docs/example-client/README (+28/-0)
docs/example-client/components/ChatClient.qml (+99/-0)
docs/example-client/hello.desktop (+8/-0)
docs/example-client/hello.json (+7/-0)
docs/example-client/helloHelper (+7/-0)
docs/example-client/helloHelper-apparmor.json (+6/-0)
docs/example-client/helloHelper.json (+3/-0)
docs/example-client/main.qml (+266/-0)
docs/example-client/manifest.json (+19/-0)
docs/example-client/push-example.qmlproject (+52/-0)
docs/example-client/tests/autopilot/push-example/__init__.py (+72/-0)
docs/example-client/tests/autopilot/push-example/test_main.py (+25/-0)
docs/example-client/tests/autopilot/run (+12/-0)
docs/example-client/tests/unit/tst_hellocomponent.qml (+50/-0)
docs/highlevel.txt (+11/-251)
docs/lowlevel.txt (+9/-247)
To merge this branch: bzr merge lp:~ralsina/ubuntu-push/bring-in-examples
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+233540@code.launchpad.net

This proposal has been superseded by a proposal from 2014-09-05.

Commit message

Deduplicate text, and bring in the example code into the project.

Description of the change

Deduplicate text, and bring in the example code into the project.

A second branch will add a bunch of links to them in the document.

To post a comment you must log in.
329. By Roberto Alsina

added example app server

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/ubuntu-push-client.conf'
2--- debian/ubuntu-push-client.conf 2014-08-06 14:08:29 +0000
3+++ debian/ubuntu-push-client.conf 2014-09-05 14:53:45 +0000
4@@ -3,6 +3,10 @@
5 start on started unity8
6 stop on stopping unity8
7
8+# set the media role for sounds to notifications' role
9+env PULSE_PROP="media.role=alert"
10+export PULSE_PROP
11+
12 exec /usr/lib/ubuntu-push-client/ubuntu-push-client
13 respawn
14
15
16=== added file 'docs/_common.txt'
17--- docs/_common.txt 1970-01-01 00:00:00 +0000
18+++ docs/_common.txt 2014-09-05 14:53:45 +0000
19@@ -0,0 +1,186 @@
20+Application Helpers
21+-------------------
22+
23+The payload delivered to push-client will be passed onto a helper program that can modify it as needed before passing it onto
24+the postal service (see `Helper Output Format <#helper-output-format>`__).
25+
26+The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed
27+version is placed in ``outfile``.
28+
29+This is the simplest possible useful helper, which simply passes the message through unchanged::
30+
31+ #!/usr/bin/python3
32+
33+ import sys
34+ f1, f2 = sys.argv[1:3]
35+ open(f2, "w").write(open(f1).read())
36+
37+Helpers need to be added to the click package manifest::
38+
39+ {
40+ "name": "com.ubuntu.developer.ralsina.hello",
41+ "description": "description of hello",
42+ "framework": "ubuntu-sdk-14.10-qml-dev2",
43+ "architecture": "all",
44+ "title": "hello",
45+ "hooks": {
46+ "hello": {
47+ "apparmor": "hello.json",
48+ "desktop": "hello.desktop"
49+ },
50+ "helloHelper": {
51+ "apparmor": "helloHelper-apparmor.json",
52+ "push-helper": "helloHelper.json"
53+ }
54+ },
55+ "version": "0.2",
56+ "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>"
57+ }
58+
59+Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook.
60+
61+helloHelper-apparmor.json must contain **only** the push-notification-client policy group::
62+
63+ {
64+ "policy_groups": [
65+ "push-notification-client"
66+ ],
67+ "policy_version": 1.2
68+ }
69+
70+And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally
71+an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version).
72+If the app_id is not specified, the helper will be used for all apps in the package::
73+
74+ {
75+ "exec": "helloHelper",
76+ "app_id": "com.ubuntu.developer.ralsina.hello_hello"
77+ }
78+
79+.. note:: For deb packages, helpers should be installed into /usr/lib/ubuntu-push-client/legacy-helpers/ as part of the package.
80+
81+Helper Output Format
82+--------------------
83+
84+Helpers output has two parts, the postal message (in the "message" key) and a notification to be presented to the user (in the "notification" key).
85+
86+.. note:: This format **will** change with future versions of the SDK and it **may** be incompatible.
87+
88+Here's a simple example::
89+
90+ {
91+ "message": "foobar",
92+ "notification": {
93+ "tag": "foo",
94+ "card": {
95+ "summary": "yes",
96+ "body": "hello",
97+ "popup": true,
98+ "persist": true,
99+ "timestamp": 1407160197
100+ }
101+ "sound": "buzz.mp3",
102+ "vibrate": {
103+ "pattern": [200, 100],
104+ "repeat": 2
105+ }
106+ "emblem-counter": {
107+ "count": 12,
108+ "visible": true
109+ }
110+ }
111+ }
112+
113+The notification can contain a **tag** field, which can later be used by the `persistent notification management API. <#persistent-notification-management>`__
114+
115+:message: (optional) A JSON object that is passed as-is to the application via PopAll.
116+:notification: (optional) Describes the user-facing notifications triggered by this push message.
117+
118+The notification can contain a **card**. A card describes a specific notification to be given to the user,
119+and has the following fields:
120+
121+:summary: (required) a title. The card will not be presented if this is missing.
122+:body: longer text, defaults to empty.
123+:actions: If empty (the default), a bubble notification is non-clickable.
124+ If you add a URL, then bubble notifications are clickable and launch that URL. One use for this is using a URL like
125+ ``appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version`` which will switch to the app or launch
126+ it if it's not running. See `URLDispatcher <https://wiki.ubuntu.com/URLDispatcher>`__ for more information.
127+
128+:icon: An icon relating to the event being notified. Defaults to empty (no icon);
129+ a secondary icon relating to the application will be shown as well, regardless of this field.
130+:timestamp: Seconds since the unix epoch, only used for persist (for now). If zero or unset, defaults to current timestamp.
131+:persist: Whether to show in notification centre; defaults to false
132+:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.
133+
134+.. note:: Keep in mind that the precise way in which each field is presented to the user depends on factors such as
135+ whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user
136+ has on their device.
137+
138+The notification can contain a **sound** field. This is either a boolean (play a predetermined sound) or the path to a sound file. The user can disable it, so don't rely on it exclusively.
139+Defaults to empty (no sound). The path is relative, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)
140+standard xdg dirs.
141+
142+The notification can contain a **vibrate** field, causing haptic feedback, which can be either a boolean (if true, vibrate a predetermined way) or an object that has the following content:
143+
144+:pattern: a list of integers describing a vibration pattern (duration of alternating vibration/no vibration times, in milliseconds).
145+:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).
146+
147+The notification can contain a **emblem-counter** field, with the following content:
148+
149+:count: a number to be displayed over the application's icon in the launcher.
150+:visible: set to true to show the counter, or false to hide it.
151+
152+.. note:: Unlike other notifications, emblem-counter needs to be cleaned by the app itself.
153+ Please see `the persistent notification management section. <#persistent-notification-management>`__
154+
155+.. FIXME crosslink to hello example app on each method
156+
157+Security
158+~~~~~~~~
159+
160+To use the push API, applications need to request permission in their security profile, using something like this::
161+
162+ {
163+ "policy_groups": [
164+ "networking",
165+ "push-notification-client"
166+ ],
167+ "policy_version": 1.2
168+ }
169+
170+
171+Ubuntu Push Server API
172+----------------------
173+
174+The Ubuntu Push server is located at https://push.ubuntu.com and has a single endpoint: ``/notify``.
175+To notify a user, your application has to do a POST with ``Content-type: application/json``.
176+
177+.. note:: The contents of the data field are arbitrary. They should be enough for your helper to build
178+ a notification using it, and decide whether it should be displayed or not. Keep in mind
179+ that this will be processed by more than one version of the helper, because the user may be using
180+ an older version of your app.
181+
182+Here is an example of the POST body using all the fields::
183+
184+ {
185+ "appid": "com.ubuntu.music_music",
186+ "expire_on": "2014-10-08T14:48:00.000Z",
187+ "token": "LeA4tRQG9hhEkuhngdouoA==",
188+ "clear_pending": true,
189+ "replace_tag": "tagname",
190+ "data": {
191+ "id": 43578,
192+ "timestamp": 1409583746,
193+ "serial": 1254,
194+ "sender": "Joe",
195+ "snippet": "Hi there!"
196+ }
197+ }
198+
199+
200+:appid: ID of the application that will receive the notification, as described in the client side documentation.
201+:expire_on: Expiration date/time for this message, in `ISO8601 Extendend format <http://en.wikipedia.org/wiki/ISO_8601>`__
202+:token: The token identifying the user+device to which the message is directed, as described in the client side documentation.
203+:clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.
204+:replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one.
205+:data: A JSON object.
206
207=== added file 'docs/_description.txt'
208--- docs/_description.txt 1970-01-01 00:00:00 +0000
209+++ docs/_description.txt 2014-09-05 14:53:45 +0000
210@@ -0,0 +1,46 @@
211+Let's describe the push system by way of an example.
212+
213+Alice has written a chat application called Chatter. Using it, Bob can send messages to Carol and viceversa. Alice has a
214+web application for it, so the way it works now is that Bob connects to the service, posts a message, and when Carol
215+connects, she gets it. If Carol leaves the browser window open, it beeps when messages arrive.
216+
217+Now Alice wants to create an Ubuntu Touch app for Chatter, so she implements the same architecture using a client that
218+does the same thing as the web browser. Sadly, since applications on Ubuntu Touch don't run continuously, messages are
219+only delivered when Carol opens the app, and the user experience suffers.
220+
221+Using the Ubuntu Push Server, this problem is alleviated: the Chatter server will deliver the messages to the Ubuntu
222+Push Server, which in turn will send it in an efficient manner to the Ubuntu Push Client running in Bob and Carol's
223+devices. The user sees a notification (all without starting the app) and then can launch it if he's interested in
224+reading messages at that point.
225+
226+Since the app is not started and messages are delivered oportunistically, this is both battery and bandwidth-efficient.
227+
228+.. figure:: push.svg
229+
230+The Ubuntu Push system provides:
231+
232+* A push server which receives **push messages** from the app servers, queues them and delivers them efficiently
233+ to the devices.
234+* A push client which receives those messages, queues messages to the app and displays notifications to the user
235+
236+The full lifecycle of a push message is:
237+
238+* Created in a application-specific server
239+* Sent to the Ubuntu Push server, targeted at a user or user+device pair
240+* Delivered to one or more Ubuntu devices
241+* Passed through the application helper for processing
242+* Notification displayed to the user (via different mechanisms)
243+* Application Message queued for the app's use
244+
245+If the user interacts with the notification, the application is launched and should check its queue for messages
246+it has to process.
247+
248+For the app developer, there are several components needed:
249+
250+* A server that sends the **push messages** to the Ubuntu Push server
251+* Support in the client app for registering with the Ubuntu Push client
252+* Support in the client app to react to **notifications** displayed to the user and process **application messages**
253+* A helper program with application-specific knowledge that transforms **push messages** as needed.
254+
255+In the following sections, we'll see how to implement all the client side parts. For the application server, see the
256+`Ubuntu Push Server API section <#ubuntu-push-server-api>`__
257
258=== added directory 'docs/example-client'
259=== added file 'docs/example-client/.excludes'
260=== added file 'docs/example-client/Makefile'
261--- docs/example-client/Makefile 1970-01-01 00:00:00 +0000
262+++ docs/example-client/Makefile 2014-09-05 14:53:45 +0000
263@@ -0,0 +1,22 @@
264+# More information: https://wiki.ubuntu.com/Touch/Testing
265+#
266+# Notes for autopilot tests:
267+# -----------------------------------------------------------
268+# In order to run autopilot tests:
269+# sudo apt-add-repository ppa:autopilot/ppa
270+# sudo apt-get update
271+# sudo apt-get install python-autopilot autopilot-qt
272+#############################################################
273+
274+all:
275+
276+autopilot:
277+ chmod +x tests/autopilot/run
278+ tests/autopilot/run
279+
280+check:
281+ qmltestrunner -input tests/unit
282+
283+run:
284+ /usr/bin/qmlscene $@ push-example.qml
285+
286
287=== added file 'docs/example-client/README'
288--- docs/example-client/README 1970-01-01 00:00:00 +0000
289+++ docs/example-client/README 2014-09-05 14:53:45 +0000
290@@ -0,0 +1,28 @@
291+Example App for the QML notifications API. This is an example application
292+showing how to use push notifications in Ubuntu Touch devices.
293+
294+= Running on Ubuntu Touch =
295+
296+Since push is currently only meant for Ubuntu Touch devices, this is meant
297+to be used in the emulator or on a real device.
298+
299+* Open the example project in Ubuntu-SDK
300+* Build a click file
301+* Run in the emulator or device
302+
303+= Running on the desktop =
304+
305+This is more complicated but may be convenient while experimenting:
306+
307+* Install qtdeclarative5-ubuntu-push-notifications-plugin
308+* Install ubuntu-push-client
309+* Run ubuntu-push-client in trivial helper mode:
310+
311+ UBUNTU_PUSH_USE_TRIVIAL_HELPER=1 ./ubuntu-push-client
312+
313+* Build click package
314+* Install in your desktop:
315+
316+ sudo click install --all-users com.ubuntu.developer.push.ubuntu-push-example_0.1_all.click
317+
318+* Run example app from the SDK using the "Desktop" kit
319
320=== added directory 'docs/example-client/components'
321=== added file 'docs/example-client/components/ChatClient.qml'
322--- docs/example-client/components/ChatClient.qml 1970-01-01 00:00:00 +0000
323+++ docs/example-client/components/ChatClient.qml 2014-09-05 14:53:45 +0000
324@@ -0,0 +1,99 @@
325+import QtQuick 2.0
326+import Ubuntu.Components 0.1
327+
328+Item {
329+ property string nick
330+ property string token
331+ property bool registered: false
332+ signal error (string msg)
333+ onNickChanged: {
334+ if (nick) {
335+ register()
336+ } else {
337+ registered = false
338+ }
339+ }
340+ onTokenChanged: {register()}
341+ function register() {
342+ console.log("registering ", nick, token);
343+ if (nick && token) {
344+ var req = new XMLHttpRequest();
345+ req.open("post", "http://direct.ralsina.me:8001/register", true);
346+ req.setRequestHeader("Content-type", "application/json");
347+ req.onreadystatechange = function() {//Call a function when the state changes.
348+ if(req.readyState == 4) {
349+ if (req.status == 200) {
350+ registered = true;
351+ } else {
352+ error(JSON.parse(req.responseText)["error"]);
353+ }
354+ }
355+ }
356+ req.send(JSON.stringify({
357+ "nick" : nick.toLowerCase(),
358+ "token": token
359+ }))
360+ }
361+ }
362+
363+ /* options is of the form:
364+ {
365+ enabled: false,
366+ persist: false,
367+ popup: false,
368+ sound: "buzz.mp3",
369+ vibrate: false,
370+ counter: 5
371+ }
372+ */
373+ function sendMessage(message, options) {
374+ var to_nick = message["to"]
375+ var data = {
376+ "from_nick": nick.toLowerCase(),
377+ "from_token": token,
378+ "nick": to_nick.toLowerCase(),
379+ "data": {
380+ "message": message,
381+ "notification": {}
382+ }
383+ }
384+ if (options["enabled"]) {
385+ data["data"]["notification"] = {
386+ "card": {
387+ "summary": nick + " says: " + message["message"],
388+ "body": "",
389+ "popup": options["popup"],
390+ "persist": options["persist"],
391+ "actions": ["appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version"]
392+ }
393+ }
394+ if (options["sound"]) {
395+ data["data"]["notification"]["sound"] = options["sound"]
396+ }
397+ if (options["vibrate"]) {
398+ data["data"]["notification"]["vibrate"] = {
399+ "duration": 200
400+ }
401+ }
402+ if (options["counter"]) {
403+ data["data"]["notification"]["emblem-counter"] = {
404+ "count": Math.floor(options["counter"]),
405+ "visible": true
406+ }
407+ }
408+ }
409+ var req = new XMLHttpRequest();
410+ req.open("post", "http://direct.ralsina.me:8001/message", true);
411+ req.setRequestHeader("Content-type", "application/json");
412+ req.onreadystatechange = function() {//Call a function when the state changes.
413+ if(req.readyState == 4) {
414+ if (req.status == 200) {
415+ registered = true;
416+ } else {
417+ error(JSON.parse(req.responseText)["error"]);
418+ }
419+ }
420+ }
421+ req.send(JSON.stringify(data))
422+ }
423+}
424
425=== added file 'docs/example-client/hello.desktop'
426--- docs/example-client/hello.desktop 1970-01-01 00:00:00 +0000
427+++ docs/example-client/hello.desktop 2014-09-05 14:53:45 +0000
428@@ -0,0 +1,8 @@
429+[Desktop Entry]
430+Name=hello
431+Exec=qmlscene $@ main.qml
432+Icon=push-example.png
433+Terminal=false
434+Type=Application
435+X-Ubuntu-Touch=true
436+
437
438=== added file 'docs/example-client/hello.json'
439--- docs/example-client/hello.json 1970-01-01 00:00:00 +0000
440+++ docs/example-client/hello.json 2014-09-05 14:53:45 +0000
441@@ -0,0 +1,7 @@
442+{
443+ "policy_groups": [
444+ "networking",
445+ "push-notification-client"
446+ ],
447+ "policy_version": 1.2
448+}
449\ No newline at end of file
450
451=== added file 'docs/example-client/helloHelper'
452--- docs/example-client/helloHelper 1970-01-01 00:00:00 +0000
453+++ docs/example-client/helloHelper 2014-09-05 14:53:45 +0000
454@@ -0,0 +1,7 @@
455+#!/usr/bin/python3
456+
457+import sys
458+
459+f1, f2 = sys.argv[1:3]
460+
461+open(f2, "w").write(open(f1).read())
462
463=== added file 'docs/example-client/helloHelper-apparmor.json'
464--- docs/example-client/helloHelper-apparmor.json 1970-01-01 00:00:00 +0000
465+++ docs/example-client/helloHelper-apparmor.json 2014-09-05 14:53:45 +0000
466@@ -0,0 +1,6 @@
467+{
468+ "policy_groups": [
469+ "push-notification-client"
470+ ],
471+ "policy_version": 1.2
472+}
473
474=== added file 'docs/example-client/helloHelper.json'
475--- docs/example-client/helloHelper.json 1970-01-01 00:00:00 +0000
476+++ docs/example-client/helloHelper.json 2014-09-05 14:53:45 +0000
477@@ -0,0 +1,3 @@
478+{
479+ "exec": "helloHelper"
480+}
481
482=== added file 'docs/example-client/main.qml'
483--- docs/example-client/main.qml 1970-01-01 00:00:00 +0000
484+++ docs/example-client/main.qml 2014-09-05 14:53:45 +0000
485@@ -0,0 +1,266 @@
486+import QtQuick 2.0
487+import Qt.labs.settings 1.0
488+import Ubuntu.Components 0.1
489+import Ubuntu.Components.ListItems 0.1 as ListItem
490+import Ubuntu.PushNotifications 0.1
491+import "components"
492+
493+MainView {
494+ id: "mainView"
495+ // objectName for functional testing purposes (autopilot-qt5)
496+ objectName: "mainView"
497+
498+ // Note! applicationName needs to match the "name" field of the click manifest
499+ applicationName: "com.ubuntu.developer.ralsina.hello"
500+
501+ automaticOrientation: true
502+ useDeprecatedToolbar: false
503+
504+ width: units.gu(100)
505+ height: units.gu(75)
506+
507+ Settings {
508+ property alias nick: chatClient.nick
509+ property alias nickText: nickEdit.text
510+ property alias nickPlaceholder: nickEdit.placeholderText
511+ property alias nickEnabled: nickEdit.enabled
512+ }
513+
514+ ChatClient {
515+ id: chatClient
516+ onRegisteredChanged: {nickEdit.registered()}
517+ onError: {messageList.handle_error(msg)}
518+ token: pushClient.token
519+ }
520+
521+ PushClient {
522+ id: pushClient
523+ Component.onCompleted: {
524+ notificationsChanged.connect(messageList.handle_notifications)
525+ error.connect(messageList.handle_error)
526+ }
527+ appId: "com.ubuntu.developer.ralsina.hello_hello"
528+ }
529+
530+ TextField {
531+ id: nickEdit
532+ focus: true
533+ placeholderText: "Your nickname"
534+ anchors.left: parent.left
535+ anchors.right: loginButton.left
536+ anchors.top: parent.top
537+ anchors.leftMargin: units.gu(.5)
538+ anchors.rightMargin: units.gu(1)
539+ anchors.topMargin: units.gu(.5)
540+ function registered() {
541+ readOnly = true
542+ text = "Your nick is " + chatClient.nick
543+ messageEdit.focus = true
544+ messageEdit.enabled = true
545+ loginButton.text = "Logout"
546+ }
547+ onAccepted: { loginButton.clicked() }
548+ }
549+
550+ Button {
551+ id: loginButton
552+ text: chatClient.rgistered? "Logout": "Login"
553+ anchors.top: nickEdit.top
554+ anchors.right: parent.right
555+ anchors.rightMargin: units.gu(.5)
556+ onClicked: {
557+ if (chatClient.nick) { // logout
558+ chatClient.nick = ""
559+ text = "Login"
560+ nickEdit.enabled = true
561+ nickEdit.readOnly = false
562+ nickEdit.text = ""
563+ nickEdit.focus = true
564+ messageEdit.enabled = false
565+ } else { // login
566+ chatClient.nick = nickEdit.text
567+ }
568+ }
569+ }
570+
571+ TextField {
572+ id: messageEdit
573+ anchors.right: parent.right
574+ anchors.left: parent.left
575+ anchors.top: nickEdit.bottom
576+ anchors.topMargin: units.gu(1)
577+ anchors.rightMargin: units.gu(.5)
578+ anchors.leftMargin: units.gu(.5)
579+ placeholderText: "Your message"
580+ enabled: false
581+ onAccepted: {
582+ console.log("sending " + text)
583+ var idx = text.indexOf(":")
584+ var nick_to = text.substring(0, idx).trim()
585+ var msg = text.substring(idx+1, 9999).trim()
586+ var i = {
587+ "from" : chatClient.nick,
588+ "to" : nick_to,
589+ "message" : msg
590+ }
591+ var o = {
592+ enabled: annoyingSwitch.checked,
593+ persist: persistSwitch.checked,
594+ popup: popupSwitch.checked,
595+ sound: soundSwitch.checked,
596+ vibrate: vibrateSwitch.checked,
597+ counter: counterSlider.value
598+ }
599+ chatClient.sendMessage(i, o)
600+ i["type"] = "sent"
601+ messagesModel.insert(0, i)
602+ text = ""
603+ }
604+ }
605+ ListModel {
606+ id: messagesModel
607+ ListElement {
608+ from: ""
609+ to: ""
610+ type: "info"
611+ message: "Register by typing your nick and clicking Login."
612+ }
613+ ListElement {
614+ from: ""
615+ to: ""
616+ type: "info"
617+ message: "Send messages in the form \"destination: hello\""
618+ }
619+ ListElement {
620+ from: ""
621+ to: ""
622+ type: "info"
623+ message: "Slide from the bottom to control notification behaviour."
624+ }
625+ }
626+
627+ UbuntuShape {
628+ anchors.left: parent.left
629+ anchors.right: parent.right
630+ anchors.bottom: notificationSettings.bottom
631+ anchors.top: messageEdit.bottom
632+ anchors.topMargin: units.gu(1)
633+ ListView {
634+ id: messageList
635+ model: messagesModel
636+ anchors.fill: parent
637+ delegate: Rectangle {
638+ MouseArea {
639+ anchors.fill: parent
640+ onClicked: {
641+ if (from != "") {
642+ messageEdit.text = from + ": "
643+ messageEdit.focus = true
644+ }
645+ }
646+ }
647+ height: label.height + units.gu(2)
648+ width: parent.width
649+ Rectangle {
650+ color: {
651+ "info": "#B5EBB9",
652+ "received" : "#A2CFA5",
653+ "sent" : "#FFF9C8",
654+ "error" : "#FF4867"}[type]
655+ height: label.height + units.gu(1)
656+ anchors.fill: parent
657+ radius: 5
658+ anchors.margins: units.gu(.5)
659+ Text {
660+ id: label
661+ text: "<b>" + ((type=="sent")?to:from) + ":</b> " + message
662+ wrapMode: Text.Wrap
663+ width: parent.width - units.gu(1)
664+ x: units.gu(.5)
665+ y: units.gu(.5)
666+ horizontalAlignment: (type=="sent")?Text.AlignRight:Text.AlignLeft
667+ }
668+ }
669+ }
670+
671+ function handle_error(error) {
672+ messagesModel.insert(0, {
673+ "from" : "",
674+ "to" : "",
675+ "type" : "error",
676+ "message" : "<b>ERROR: " + error + "</b>"
677+ })
678+ }
679+
680+ function handle_notifications(list) {
681+ list.forEach(function(notification) {
682+ var item = JSON.parse(notification)
683+ item["type"] = "received"
684+ messagesModel.insert(0, item)
685+ })
686+ }
687+ }
688+ }
689+ Panel {
690+ id: notificationSettings
691+ anchors {
692+ left: parent.left
693+ right: parent.right
694+ bottom: parent.bottom
695+ }
696+ height: item1.height * 7
697+ UbuntuShape {
698+ anchors.fill: parent
699+ color: Theme.palette.normal.overlay
700+ Column {
701+ id: settingsColumn
702+ anchors.fill: parent
703+ ListItem.Header {
704+ text: "<b>Notification Settings</b>"
705+ }
706+ ListItem.Standard {
707+ id: item1
708+ text: "Enable Notifications"
709+ control: Switch {
710+ id: annoyingSwitch
711+ }
712+ }
713+ ListItem.Standard {
714+ text: "Enable Popup"
715+ enabled: annoyingSwitch.checked
716+ control: Switch {
717+ id: popupSwitch
718+ }
719+ }
720+ ListItem.Standard {
721+ text: "Persistent"
722+ enabled: annoyingSwitch.checked
723+ control: Switch {
724+ id: persistSwitch
725+ }
726+ }
727+ ListItem.Standard {
728+ text: "Make Sound"
729+ enabled: annoyingSwitch.checked
730+ control: Switch {
731+ id: soundSwitch
732+ }
733+ }
734+ ListItem.Standard {
735+ text: "Vibrate"
736+ enabled: annoyingSwitch.checked
737+ control: Switch {
738+ id: vibrateSwitch
739+ }
740+ }
741+ ListItem.Standard {
742+ text: "Counter Value"
743+ enabled: annoyingSwitch.checked
744+ control: Slider {
745+ id: counterSlider
746+ }
747+ }
748+ }
749+ }
750+ }
751+}
752
753=== added file 'docs/example-client/manifest.json'
754--- docs/example-client/manifest.json 1970-01-01 00:00:00 +0000
755+++ docs/example-client/manifest.json 2014-09-05 14:53:45 +0000
756@@ -0,0 +1,19 @@
757+{
758+ "architecture": "all",
759+ "description": "Example app for Ubuntu push notifications.",
760+ "framework": "ubuntu-sdk-14.10-dev2",
761+ "hooks": {
762+ "hello": {
763+ "apparmor": "hello.json",
764+ "desktop": "hello.desktop"
765+ },
766+ "helloHelper": {
767+ "apparmor": "helloHelper-apparmor.json",
768+ "push-helper": "helloHelper.json"
769+ }
770+ },
771+ "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>",
772+ "name": "com.ubuntu.developer.ralsina.hello",
773+ "title": "ubuntu-push-example",
774+ "version": "0.4"
775+}
776
777=== added file 'docs/example-client/push-example.png'
778Binary files docs/example-client/push-example.png 1970-01-01 00:00:00 +0000 and docs/example-client/push-example.png 2014-09-05 14:53:45 +0000 differ
779=== added file 'docs/example-client/push-example.qmlproject'
780--- docs/example-client/push-example.qmlproject 1970-01-01 00:00:00 +0000
781+++ docs/example-client/push-example.qmlproject 2014-09-05 14:53:45 +0000
782@@ -0,0 +1,52 @@
783+import QmlProject 1.1
784+
785+Project {
786+ mainFile: "main.qml"
787+
788+ /* Include .qml, .js, and image files from current directory and subdirectories */
789+ QmlFiles {
790+ directory: "."
791+ }
792+ JavaScriptFiles {
793+ directory: "."
794+ }
795+ ImageFiles {
796+ directory: "."
797+ }
798+ Files {
799+ filter: "*.desktop"
800+ }
801+ Files {
802+ filter: "www/*.html"
803+ }
804+ Files {
805+ filter: "Makefile"
806+ }
807+ Files {
808+ directory: "www"
809+ filter: "*"
810+ }
811+ Files {
812+ directory: "www/img/"
813+ filter: "*"
814+ }
815+ Files {
816+ directory: "www/css/"
817+ filter: "*"
818+ }
819+ Files {
820+ directory: "www/js/"
821+ filter: "*"
822+ }
823+ Files {
824+ directory: "tests/"
825+ filter: "*"
826+ }
827+ Files {
828+ directory: "debian"
829+ filter: "*"
830+ }
831+ /* List of plugin directories passed to QML runtime */
832+ importPaths: [ "." ,"/usr/bin","/usr/lib/x86_64-linux-gnu/qt5/qml" ]
833+}
834+
835
836=== added directory 'docs/example-client/tests'
837=== added directory 'docs/example-client/tests/autopilot'
838=== added directory 'docs/example-client/tests/autopilot/push-example'
839=== added file 'docs/example-client/tests/autopilot/push-example/__init__.py'
840--- docs/example-client/tests/autopilot/push-example/__init__.py 1970-01-01 00:00:00 +0000
841+++ docs/example-client/tests/autopilot/push-example/__init__.py 2014-09-05 14:53:45 +0000
842@@ -0,0 +1,72 @@
843+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
844+
845+"""Ubuntu Touch App autopilot tests."""
846+
847+import os
848+import subprocess
849+
850+from autopilot import input, platform
851+from autopilot.matchers import Eventually
852+from testtools.matchers import Equals
853+from ubuntuuitoolkit import base, emulators
854+
855+
856+def _get_module_include_path():
857+ return os.path.join(get_path_to_source_root(), 'modules')
858+
859+
860+def get_path_to_source_root():
861+ return os.path.abspath(
862+ os.path.join(
863+ os.path.dirname(__file__), '..', '..', '..', '..'))
864+
865+
866+class ClickAppTestCase(base.UbuntuUIToolkitAppTestCase):
867+ """Common test case that provides several useful methods for the tests."""
868+
869+ package_id = '' # TODO
870+ app_name = 'push-example'
871+
872+ def setUp(self):
873+ super(ClickAppTestCase, self).setUp()
874+ self.pointing_device = input.Pointer(self.input_device_class.create())
875+ self.launch_application()
876+
877+ self.assertThat(self.main_view.visible, Eventually(Equals(True)))
878+
879+ def launch_application(self):
880+ if platform.model() == 'Desktop':
881+ self._launch_application_from_desktop()
882+ else:
883+ self._launch_application_from_phablet()
884+
885+ def _launch_application_from_desktop(self):
886+ app_qml_source_location = self._get_app_qml_source_path()
887+ if os.path.exists(app_qml_source_location):
888+ self.app = self.launch_test_application(
889+ base.get_qmlscene_launch_command(),
890+ '-I' + _get_module_include_path(),
891+ app_qml_source_location,
892+ app_type='qt',
893+ emulator_base=emulators.UbuntuUIToolkitEmulatorBase)
894+ else:
895+ raise NotImplementedError(
896+ "On desktop we can't install click packages yet, so we can "
897+ "only run from source.")
898+
899+ def _get_app_qml_source_path(self):
900+ qml_file_name = '{0}.qml'.format(self.app_name)
901+ return os.path.join(self._get_path_to_app_source(), qml_file_name)
902+
903+ def _get_path_to_app_source(self):
904+ return os.path.join(get_path_to_source_root(), self.app_name)
905+
906+ def _launch_application_from_phablet(self):
907+ # On phablet, we only run the tests against the installed click
908+ # package.
909+ self.app = self.launch_click_package(self.pacakge_id, self.app_name)
910+
911+ @property
912+ def main_view(self):
913+ return self.app.select_single(emulators.MainView)
914+
915
916=== added file 'docs/example-client/tests/autopilot/push-example/test_main.py'
917--- docs/example-client/tests/autopilot/push-example/test_main.py 1970-01-01 00:00:00 +0000
918+++ docs/example-client/tests/autopilot/push-example/test_main.py 2014-09-05 14:53:45 +0000
919@@ -0,0 +1,25 @@
920+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
921+
922+"""Tests for the Hello World"""
923+
924+import os
925+
926+from autopilot.matchers import Eventually
927+from testtools.matchers import Equals
928+
929+import push-example
930+
931+
932+class MainViewTestCase(push-example.ClickAppTestCase):
933+ """Generic tests for the Hello World"""
934+
935+ def test_initial_label(self):
936+ label = self.main_view.select_single(objectName='label')
937+ self.assertThat(label.text, Equals('Hello..'))
938+
939+ def test_click_button_should_update_label(self):
940+ button = self.main_view.select_single(objectName='button')
941+ self.pointing_device.click_object(button)
942+ label = self.main_view.select_single(objectName='label')
943+ self.assertThat(label.text, Eventually(Equals('..world!')))
944+
945
946=== added file 'docs/example-client/tests/autopilot/run'
947--- docs/example-client/tests/autopilot/run 1970-01-01 00:00:00 +0000
948+++ docs/example-client/tests/autopilot/run 2014-09-05 14:53:45 +0000
949@@ -0,0 +1,12 @@
950+#!/bin/bash
951+
952+if [[ -z `which autopilot` ]]; then
953+ echo "Autopilot is not installed. Skip"
954+ exit
955+fi
956+
957+SCRIPTPATH=`dirname $0`
958+pushd ${SCRIPTPATH}
959+autopilot run push-example
960+popd
961+
962
963=== added directory 'docs/example-client/tests/unit'
964=== added file 'docs/example-client/tests/unit/tst_hellocomponent.qml'
965--- docs/example-client/tests/unit/tst_hellocomponent.qml 1970-01-01 00:00:00 +0000
966+++ docs/example-client/tests/unit/tst_hellocomponent.qml 2014-09-05 14:53:45 +0000
967@@ -0,0 +1,50 @@
968+import QtQuick 2.0
969+import QtTest 1.0
970+import Ubuntu.Components 0.1
971+import "../../components"
972+
973+// See more details @ http://qt-project.org/doc/qt-5.0/qtquick/qml-testcase.html
974+
975+// Execute tests with:
976+// qmltestrunner
977+
978+Item {
979+ // The objects
980+ HelloComponent {
981+ id: objectUnderTest
982+ }
983+
984+ TestCase {
985+ name: "HelloComponent"
986+
987+ function init() {
988+ console.debug(">> init");
989+ compare("",objectUnderTest.text,"text was not empty on init");
990+ console.debug("<< init");
991+ }
992+
993+ function cleanup() {
994+ console.debug(">> cleanup");
995+ console.debug("<< cleanup");
996+ }
997+
998+ function initTestCase() {
999+ console.debug(">> initTestCase");
1000+ console.debug("<< initTestCase");
1001+ }
1002+
1003+ function cleanupTestCase() {
1004+ console.debug(">> cleanupTestCase");
1005+ console.debug("<< cleanupTestCase");
1006+ }
1007+
1008+ function test_canReadAndWriteText() {
1009+ var expected = "Hello World";
1010+
1011+ objectUnderTest.text = expected;
1012+
1013+ compare(expected,objectUnderTest.text,"expected did not equal result");
1014+ }
1015+ }
1016+}
1017+
1018
1019=== modified file 'docs/highlevel.txt'
1020--- docs/highlevel.txt 2014-08-08 09:09:39 +0000
1021+++ docs/highlevel.txt 2014-09-05 14:53:45 +0000
1022@@ -1,8 +1,10 @@
1023-Ubuntu Push Client Developer Guide
1024-==================================
1025+Ubuntu Push Client High Level Developer Guide
1026+============================================
1027
1028 :Version: 0.50+
1029
1030+.. contents::
1031+
1032 Introduction
1033 ------------
1034
1035@@ -11,52 +13,7 @@
1036
1037 ---------
1038
1039-Let's describe the push system by way of an example.
1040-
1041-Alice has written a chat application called Chatter. Using it, Bob can send messages to Carol and viceversa. Alice has a
1042-web application for it, so the way it works now is that Bob connects to the service, posts a message, and when Carol
1043-connects, she gets it. If Carol leaves the browser window open, it beeps when messages arrive.
1044-
1045-Now Alice wants to create an Ubuntu Touch app for Chatter, so she implements the same architecture using a client that
1046-does the same thing as the web browser. Sadly, since applications on Ubuntu Touch don't run continuously, messages are
1047-only delivered when Carol opens the app, and the user experience suffers.
1048-
1049-Using the Ubuntu Push Server, this problem is alleviated: the Chatter server will deliver the messages to the Ubuntu
1050-Push Server, which in turn will send it in an efficient manner to the Ubuntu Push Client running in Bob and Carol's
1051-devices. The user sees a notification (all without starting the app) and then can launch it if he's interested in
1052-reading messages at that point.
1053-
1054-Since the app is not started and messages are delivered oportunistically, this is both battery and bandwidth-efficient.
1055-
1056-.. figure:: push.svg
1057-
1058-The Ubuntu Push system provides:
1059-
1060-* A push server which receives **push messages** from the app servers, queues them and delivers them efficiently
1061- to the devices.
1062-* A push client which receives those messages, queues messages to the app and displays notifications to the user
1063-
1064-The full lifecycle of a push message is:
1065-
1066-* Created in a application-specific server
1067-* Sent to the Ubuntu Push server, targeted at a user or user+device pair
1068-* Delivered to one or more Ubuntu devices
1069-* Passed through the application helper for processing
1070-* Notification displayed to the user (via different mechanisms)
1071-* Application Message queued for the app's use
1072-
1073-If the user interacts with the notification, the application is launched and should check its queue for messages
1074-it has to process.
1075-
1076-For the app developer, there are several components needed:
1077-
1078-* A server that sends the **push messages** to the Ubuntu Push server
1079-* Support in the client app for registering with the Ubuntu Push client
1080-* Support in the client app to react to **notifications** displayed to the user and process **application messages**
1081-* A helper program with application-specific knowledge that transforms **push messages** as needed.
1082-
1083-In the following sections, we'll see how to implement all the client side parts. For the application server, see the
1084-`Ubuntu Push Server API section <#ubuntu-push-server-api>`__
1085+.. include:: _description.txt
1086
1087 The PushClient Component
1088 ------------------------
1089@@ -68,7 +25,7 @@
1090 PushClient {
1091 id: pushClient
1092 Component.onCompleted: {
1093- newNotifications.connect(messageList.handle_notifications)
1094+ notificationsChanged.connect(messageList.handle_notifications)
1095 error.connect(messageList.handle_error)
1096 }
1097 appId: "com.ubuntu.developer.push.hello_hello"
1098@@ -95,13 +52,13 @@
1099 ~~~~~~~~~~~~~~~~~~~~~~~
1100
1101 When a notification is received by the Push Client, it will be delivered to your application's push helper, and then
1102-placed in your application's mailbox. At that point, the PushClient will emit the ``newNotifications(QStringList)`` signal
1103+placed in your application's mailbox. At that point, the PushClient will emit the ``notificationsChanged(QStringList)`` signal
1104 containing your messages. You should probably connect to that signal and handle those messages.
1105
1106 Because of the application's lifecycle, there is no guarantee that it will be running when the signal is emitted. For that
1107 reason, apps should check for pending notifications whenever they are activated or started. To do that, use the
1108 ``getNotifications()`` slot. Triggering that slot will fetch notifications and trigger the
1109-``newNotifications(QStringList)`` signal.
1110+``notificationsChanged(QStringList)`` signal.
1111
1112 Error Handling
1113 ~~~~~~~~~~~~~~
1114@@ -111,8 +68,8 @@
1115 Persistent Notification Management
1116 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1117
1118-Some notifications are persistent, meaning they don't disappear automatically. For those notifications, there is an API that
1119-allows the app to manage them without having to know the underlying details of the platform.
1120+Some notifications are persistent, meaning that, after they are presented, they don't disappear automatically.
1121+This API allows the app to manage that type of notifications.
1122
1123 On each notification there's an optional ``tag`` field, used for this purpose.
1124
1125@@ -125,201 +82,4 @@
1126
1127 The ``count`` property sets the counter in the application's icon to the given value.
1128
1129-
1130-Application Helpers
1131--------------------
1132-
1133-The payload delivered to push-client will be passed onto a helper program that can modify it as needed before passing it onto
1134-the postal service (see `Helper Output Format <#helper-output-format>`__).
1135-
1136-The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed
1137-version is placed in ``outfile``.
1138-
1139-This is the simplest possible useful helper, which simply passes the message through unchanged::
1140-
1141- #!/usr/bin/python3
1142-
1143- import sys
1144- f1, f2 = sys.argv[1:3]
1145- open(f2, "w").write(open(f1).read())
1146-
1147-Helpers need to be added to the click package manifest::
1148-
1149- {
1150- "name": "com.ubuntu.developer.ralsina.hello",
1151- "description": "description of hello",
1152- "framework": "ubuntu-sdk-14.10-qml-dev2",
1153- "architecture": "all",
1154- "title": "hello",
1155- "hooks": {
1156- "hello": {
1157- "apparmor": "hello.json",
1158- "desktop": "hello.desktop"
1159- },
1160- "helloHelper": {
1161- "apparmor": "helloHelper-apparmor.json",
1162- "push-helper": "helloHelper.json"
1163- }
1164- },
1165- "version": "0.2",
1166- "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>"
1167- }
1168-
1169-Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook.
1170-
1171-helloHelper-apparmor.json must contain **only** the push-notification-client policy group::
1172-
1173- {
1174- "policy_groups": [
1175- "push-notification-client"
1176- ],
1177- "policy_version": 1.2
1178- }
1179-
1180-And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally
1181-an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version).
1182-If the app_id is not specified, the helper will be used for all apps in the package::
1183-
1184- {
1185- "exec": "helloHelper",
1186- "app_id": "com.ubuntu.developer.ralsina.hello_hello"
1187- }
1188-
1189-.. note:: For deb packages, helpers should be installed into /usr/lib/ubuntu-push-client/legacy-helpers/ as part of the package.
1190-
1191-Helper Output Format
1192---------------------
1193-
1194-Helpers output has two parts, the postal message (in the "message" key) and a notification to be presented to the user (in the "notification" key).
1195-
1196-Here's a simple example::
1197-
1198- {
1199- "message": "foobar",
1200- "notification": {
1201- "tag": "foo",
1202- "card": {
1203- "summary": "yes",
1204- "body": "hello",
1205- "popup": true,
1206- "persist": true,
1207- "timestamp": 1407160197
1208- }
1209- "sound": "buzz.mp3",
1210- "vibrate": {
1211- "pattern": [200, 100],
1212- "repeat": 2
1213- }
1214- "emblem-counter": {
1215- "count": 12,
1216- "visible": true
1217- }
1218- }
1219- }
1220-
1221-The notification can contain a **tag** field, which can later be used by the `persistent notification management API. <#persistent-notification-management>`__
1222-
1223-:message: (optional) A JSON object that is passed as-is to the application via PopAll.
1224-:notification: (optional) Describes the user-facing notifications triggered by this push message.
1225-
1226-The notification can contain a **card**. A card describes a specific notification to be given to the user,
1227-and has the following fields:
1228-
1229-:summary: (required) a title. The card will not be presented if this is missing.
1230-:body: longer text, defaults to empty.
1231-:actions: If empty (the default), a bubble notification is non-clickable.
1232- If you add a URL, then bubble notifications are clickable and launch that URL. One use for this is using a URL like
1233- ``appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version`` which will switch to the app or launch
1234- it if it's not running. See `URLDispatcher <https://wiki.ubuntu.com/URLDispatcher>`__ for more information.
1235-
1236-:icon: An icon relating to the event being notified. Defaults to empty (no icon);
1237- a secondary icon relating to the application will be shown as well, regardless of this field.
1238-:timestamp: Seconds since the unix epoch, only used for persist (for now). If zero or unset, defaults to current timestamp.
1239-:persist: Whether to show in notification centre; defaults to false
1240-:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.
1241-
1242-.. note:: Keep in mind that the precise way in which each field is presented to the user depends on factors such as
1243- whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user
1244- has on their device.
1245-
1246-The notification can contain a **sound** field. This is either a boolean (play a predetermined sound) or the path to a sound file. The user can disable it, so don't rely on it exclusively.
1247-Defaults to empty (no sound). The path is relative, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)
1248-standard xdg dirs.
1249-
1250-The notification can contain a **vibrate** field, causing haptic feedback, which can be either a boolean (if true, vibrate a predetermined way) or an object that has the following content:
1251-
1252-:pattern: a list of integers describing a vibration pattern (duration of alternating vibration/no vibration times, in milliseconds).
1253-:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).
1254-
1255-The notification can contain a **emblem-counter** field, with the following content:
1256-
1257-:count: a number to be displayed over the application's icon in the launcher.
1258-:visible: set to true to show the counter, or false to hide it.
1259-
1260-.. note:: Unlike other notifications, emblem-counter needs to be cleaned by the app itself.
1261- Please see `the persistent notification management section. <#persistent-notification-management>`__
1262-
1263-.. FIXME crosslink to hello example app on each method
1264-
1265-Security
1266-~~~~~~~~
1267-
1268-To use the push API, applications need to request permission in their security profile, using something like this::
1269-
1270- {
1271- "policy_groups": [
1272- "networking",
1273- "push-notification-client"
1274- ],
1275- "policy_version": 1.2
1276- }
1277-
1278-
1279-Ubuntu Push Server API
1280-----------------------
1281-
1282-The Ubuntu Push server is located at https://push.ubuntu.com and has a single endpoint: ``/notify``.
1283-To notify a user, your application has to do a POST with ``Content-type: application/json``.
1284-
1285-Here is an example of the POST body using all the fields::
1286-
1287- {
1288- "appid": "com.ubuntu.music_music",
1289- "expire_on": "2014-10-08T14:48:00.000Z",
1290- "token": "LeA4tRQG9hhEkuhngdouoA==",
1291- "clear_pending": true,
1292- "replace_tag": "tagname",
1293- "data": {
1294- "message": "foobar",
1295- "notification": {
1296- "card": {
1297- "summary": "yes",
1298- "body": "hello",
1299- "popup": true,
1300- "persist": true,
1301- "timestamp": 1407160197
1302- }
1303- "sound": "buzz.mp3",
1304- "tag": "foo",
1305- "vibrate": {
1306- "pattern": [200, 100],
1307- "repeat": 2
1308- }
1309- "emblem-counter": {
1310- "count": 12,
1311- "visible": true
1312- }
1313- }
1314- }
1315- }
1316-
1317-
1318-:appid: ID of the application that will receive the notification, as described in the client side documentation.
1319-:expire_on: Expiration date/time for this message, in `ISO8601 Extendend format <http://en.wikipedia.org/wiki/ISO_8601>`__
1320-:token: The token identifying the user+device to which the message is directed, as described in the client side documentation.
1321-:clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.
1322-:replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one.
1323-:data: A JSON object.
1324-
1325-In this example, data is `what a helper would output <#helper-output-format>`__ but that's not necessarily the case.
1326-The content of the data field will be passed to the helper application which **has** to produce output in that format.
1327+.. include:: _common.txt
1328\ No newline at end of file
1329
1330=== modified file 'docs/lowlevel.txt'
1331--- docs/lowlevel.txt 2014-08-08 09:09:39 +0000
1332+++ docs/lowlevel.txt 2014-09-05 14:53:45 +0000
1333@@ -1,8 +1,10 @@
1334-Ubuntu Push Client Developer Guide
1335-==================================
1336+Ubuntu Push Client Low Level Developer Guide
1337+============================================
1338
1339 :Version: 0.50+
1340
1341+.. contents::
1342+
1343 Introduction
1344 ------------
1345
1346@@ -14,52 +16,8 @@
1347
1348 ---------
1349
1350-Let's describe the push system by way of an example.
1351-
1352-Alice has written a chat application called Chatter. Using it, Bob can send messages to Carol and viceversa. Alice has a
1353-web application for it, so the way it works now is that Bob connects to the service, posts a message, and when Carol
1354-connects, she gets it. If Carol leaves the browser window open, it beeps when messages arrive.
1355-
1356-Now Alice wants to create an Ubuntu Touch app for Chatter, so she implements the same architecture using a client that
1357-does the same thing as the web browser. Sadly, since applications on Ubuntu Touch don't run continuously, messages are
1358-only delivered when Carol opens the app, and the user experience suffers.
1359-
1360-Using the Ubuntu Push Server, this problem is alleviated: the Chatter server will deliver the messages to the Ubuntu
1361-Push Server, which in turn will send it in an efficient manner to the Ubuntu Push Client running in Bob and Carol's
1362-devices. The user sees a notification (all without starting the app) and then can launch it if he's interested in
1363-reading messages at that point.
1364-
1365-Since the app is not started and messages are delivered oportunistically, this is both battery and bandwidth-efficient.
1366-
1367-.. figure:: push.svg
1368-
1369-The Ubuntu Push system provides:
1370-
1371-* A push server which receives **push messages** from the app servers, queues them and delivers them efficiently
1372- to the devices.
1373-* A push client which receives those messages, queues messages to the app and displays notifications to the user
1374-
1375-The full lifecycle of a push message is:
1376-
1377-* Created in a application-specific server
1378-* Sent to the Ubuntu Push server, targeted at a user or user+device pair
1379-* Delivered to one or more Ubuntu devices
1380-* Passed through the application helper for processing
1381-* Notification displayed to the user (via different mechanisms)
1382-* Application Message queued for the app's use
1383-
1384-If the user interacts with the notification, the application is launched and should check its queue for messages
1385-it has to process.
1386-
1387-For the app developer, there are several components needed:
1388-
1389-* A server that sends the **push messages** to the Ubuntu Push server
1390-* Support in the client app for registering with the Ubuntu Push client
1391-* Support in the client app to react to **notifications** displayed to the user and process **application messages**
1392-* A helper program with application-specific knowledge that transforms **push messages** as needed.
1393-
1394-In the following sections, we'll see how to implement all the client side parts. For the application server, see the
1395-`Ubuntu Push Server API section <#ubuntu-push-server-api>`__
1396+.. include:: _description.txt
1397+
1398
1399 The PushNotifications Service
1400 -----------------------------
1401@@ -202,8 +160,8 @@
1402 Persistent Notification Management
1403 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1404
1405-Some notifications are persistent, meaning they don't disappear automatically. For those notifications, there is an API that
1406-allows the app to manage them without having to know the underlying details of the platform.
1407+Some notifications are persistent, meaning that, after they are presented, they don't disappear automatically.
1408+This API allows the app to manage that type of notifications.
1409
1410 On each notification there's an optional ``tag`` field, used for this purpose.
1411
1412@@ -220,200 +178,4 @@
1413 Set the counter to the given values.
1414
1415
1416-Application Helpers
1417--------------------
1418-
1419-The payload delivered to push-client will be passed onto a helper program that can modify it as needed before passing it onto
1420-the postal service (see `Helper Output Format <#helper-output-format>`__).
1421-
1422-The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed
1423-version is placed in ``outfile``.
1424-
1425-This is the simplest possible useful helper, which simply passes the message through unchanged::
1426-
1427- #!/usr/bin/python3
1428-
1429- import sys
1430- f1, f2 = sys.argv[1:3]
1431- open(f2, "w").write(open(f1).read())
1432-
1433-Helpers need to be added to the click package manifest::
1434-
1435- {
1436- "name": "com.ubuntu.developer.ralsina.hello",
1437- "description": "description of hello",
1438- "framework": "ubuntu-sdk-14.10-qml-dev2",
1439- "architecture": "all",
1440- "title": "hello",
1441- "hooks": {
1442- "hello": {
1443- "apparmor": "hello.json",
1444- "desktop": "hello.desktop"
1445- },
1446- "helloHelper": {
1447- "apparmor": "helloHelper-apparmor.json",
1448- "push-helper": "helloHelper.json"
1449- }
1450- },
1451- "version": "0.2",
1452- "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>"
1453- }
1454-
1455-Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook.
1456-
1457-helloHelper-apparmor.json must contain **only** the push-notification-client policy group::
1458-
1459- {
1460- "policy_groups": [
1461- "push-notification-client"
1462- ],
1463- "policy_version": 1.2
1464- }
1465-
1466-And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally
1467-an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version).
1468-If the app_id is not specified, the helper will be used for all apps in the package::
1469-
1470- {
1471- "exec": "helloHelper",
1472- "app_id": "com.ubuntu.developer.ralsina.hello_hello"
1473- }
1474-
1475-.. note:: For deb packages, helpers should be installed into /usr/lib/ubuntu-push-client/legacy-helpers/ as part of the package.
1476-
1477-Helper Output Format
1478---------------------
1479-
1480-Helpers output has two parts, the postal message (in the "message" key) and a notification to be presented to the user (in the "notification" key).
1481-
1482-Here's a simple example::
1483-
1484- {
1485- "message": "foobar",
1486- "notification": {
1487- "tag": "foo",
1488- "card": {
1489- "summary": "yes",
1490- "body": "hello",
1491- "popup": true,
1492- "persist": true,
1493- "timestamp": 1407160197
1494- }
1495- "sound": "buzz.mp3",
1496- "vibrate": {
1497- "pattern": [200, 100],
1498- "repeat": 2
1499- }
1500- "emblem-counter": {
1501- "count": 12,
1502- "visible": true
1503- }
1504- }
1505- }
1506-
1507-The notification can contain a **tag** field, which can later be used by the `persistent notification management API. <#persistent-notification-management>`__
1508-
1509-:message: (optional) A JSON object that is passed as-is to the application via PopAll.
1510-:notification: (optional) Describes the user-facing notifications triggered by this push message.
1511-
1512-The notification can contain a **card**. A card describes a specific notification to be given to the user,
1513-and has the following fields:
1514-
1515-:summary: (required) a title. The card will not be presented if this is missing.
1516-:body: longer text, defaults to empty.
1517-:actions: If empty (the default), a bubble notification is non-clickable.
1518- If you add a URL, then bubble notifications are clickable and launch that URL. One use for this is using a URL like
1519- ``appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version`` which will switch to the app or launch
1520- it if it's not running. See `URLDispatcher <https://wiki.ubuntu.com/URLDispatcher>`__ for more information.
1521-
1522-:icon: An icon relating to the event being notified. Defaults to empty (no icon);
1523- a secondary icon relating to the application will be shown as well, regardless of this field.
1524-:timestamp: Seconds since the unix epoch, only used for persist (for now). If zero or unset, defaults to current timestamp.
1525-:persist: Whether to show in notification centre; defaults to false
1526-:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.
1527-
1528-.. note:: Keep in mind that the precise way in which each field is presented to the user depends on factors such as
1529- whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user
1530- has on their device.
1531-
1532-The notification can contain a **sound** field. This is either a boolean (play a predetermined sound) or the path to a sound file. The user can disable it, so don't rely on it exclusively.
1533-Defaults to empty (no sound). The path is relative, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)
1534-standard xdg dirs.
1535-
1536-The notification can contain a **vibrate** field, causing haptic feedback, which can be either a boolean (if true, vibrate a predetermined way) or an object that has the following content:
1537-
1538-:pattern: a list of integers describing a vibration pattern (duration of alternating vibration/no vibration times, in milliseconds).
1539-:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).
1540-
1541-The notification can contain a **emblem-counter** field, with the following content:
1542-
1543-:count: a number to be displayed over the application's icon in the launcher.
1544-:visible: set to true to show the counter, or false to hide it.
1545-
1546-.. note:: Unlike other notifications, emblem-counter needs to be cleaned by the app itself.
1547- Please see `the persistent notification management section. <#persistent-notification-management>`__
1548-
1549-.. FIXME crosslink to hello example app on each method
1550-
1551-Security
1552-~~~~~~~~
1553-
1554-To use the push API, applications need to request permission in their security profile, using something like this::
1555-
1556- {
1557- "policy_groups": [
1558- "networking",
1559- "push-notification-client"
1560- ],
1561- "policy_version": 1.2
1562- }
1563-
1564-
1565-Ubuntu Push Server API
1566-----------------------
1567-
1568-The Ubuntu Push server is located at https://push.ubuntu.com and has a single endpoint: ``/notify``.
1569-To notify a user, your application has to do a POST with ``Content-type: application/json``.
1570-
1571-Here is an example of the POST body using all the fields::
1572-
1573- {
1574- "appid": "com.ubuntu.music_music",
1575- "expire_on": "2014-10-08T14:48:00.000Z",
1576- "token": "LeA4tRQG9hhEkuhngdouoA==",
1577- "clear_pending": true,
1578- "replace_tag": "tagname",
1579- "data": {
1580- "message": "foobar",
1581- "notification": {
1582- "card": {
1583- "summary": "yes",
1584- "body": "hello",
1585- "popup": true,
1586- "persist": true,
1587- "timestamp": 1407160197
1588- }
1589- "sound": "buzz.mp3",
1590- "tag": "foo",
1591- "vibrate": {
1592- "pattern": [200, 100],
1593- "repeat": 2
1594- }
1595- "emblem-counter": {
1596- "count": 12,
1597- "visible": true
1598- }
1599- }
1600- }
1601- }
1602-
1603-
1604-:appid: ID of the application that will receive the notification, as described in the client side documentation.
1605-:expire_on: Expiration date/time for this message, in `ISO8601 Extendend format <http://en.wikipedia.org/wiki/ISO_8601>`__
1606-:token: The token identifying the user+device to which the message is directed, as described in the client side documentation.
1607-:clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.
1608-:replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one.
1609-:data: A JSON object.
1610-
1611-In this example, data is `what a helper would output <#helper-output-format>`__ but that's not necessarily the case.
1612-The content of the data field will be passed to the helper application which **has** to produce output in that format.
1613+.. include:: _common.txt
1614\ No newline at end of file

Subscribers

People subscribed via source and target branches