如何在Ubuntu QML应用中使用Push Notification

我们知道目前Ubuntu手机平台有些类似iPhone平台,是一个单任务的操作系统,虽然系统本身具有多任务的功能。如果当前的应用被推到后台的话,应用将会被自动挂起,而不会被系统所运行。在这个时候如果我们的应用需要等待一个消息,比如就想微信之类的信息,我们就要使用Ubuntu平台所提供的Push Notification机制来实现我们的类似多任务的东西。当通知被收到后,我们就可以直接点击接受到的通知,应用又会被重新运行到前台。

关于Push notification,在我们的开发者网站上,有一篇文章(client)和一篇文章(server)详细介绍了它的机制。这里我不想讲太多的东西。有兴趣的同学们可以详读那篇文章。今天在这里,我来和大家分析一个具体的实例,以更好地了解如何在Ubuntu手机上实现这个功能。

在上述的图中可以看出来,整个系统的组成分两部分:客户端及服务器端。在服务器端,又分为一个PushSever (https://push.ubuntu.com)及一个App Server。App server是用来管理我们的用户的Nick Name及Token的。在它的里面,有一个数据库。

为了测试,开发者必须有一个Ubuntu One的账号。我们需要在手机的“系统设置”里的账号中创建这个账号。

当一个QML应用在使用:

import Ubuntu.PushNotifications 0.1PushClient {id: pushClientComponent.onCompleted: {notificationsChanged.connect(messageList.handle_notifications)error.connect(messageList.handle_error)}appId: "com.ubuntu.developer.push.hello_hello"}

当我们使用上面的API后,push server将向我们的客户端发送一个token。这个token依赖于手机自己的参数及上面所看到的“appId”。利用这个token,我们可以向我们的应用服务器注册,并存于应用服务器端中。当我们需要发送信息的时候,我们必须注册一个类似nickname的东西。这个nickname将和我们手机客户端的token绑定。每当另外一个nickname想像我们发送信息时,应用服务器端可以通过数据库的查询来得到我们的token,从而更进一步通过push server来向我们的客服端推送信息。如果我们的客户端想向其它的客户端发送信息,这其中的道理,也是和刚才一样。

目前,在我们的开发者网站并没有PushClient的具体的介绍。我们可以使用在文章“如何得到QML package的详细API接口”中的方法来了解这个API。

Push server是用来推送信息。它位于https://push.ubuntu.com。它只有一个endpoint:/notify。为了向一个用户发送推送信息。应用服务器可以向Push Sever发送一个含有“Content-type: application/json”的HTTP POST信息来推送我们的信息。下面是一个POST body的一个样板内容:

{"appid": "com.ubuntu.music_music","expire_on": "2014-10-08T14:48:00.000Z","token": "LeA4tRQG9hhEkuhngdouoA==","clear_pending": true,"replace_tag": "tagname","data": {"message": "foobar","notification": {"card": {"summary": "yes","body": "hello","popup": true,"persist": true}"sound": "buzz.mp3","tag": "foo","vibrate": {"duration": 200,"pattern": (200, 100),"repeat": 2}"emblem-counter": {"count": 12,"visible": true}}}}

appid:ID of the application that will receive the notification, as described in the client side documentation.

expire_on:Expiration date/time for this message, inISO8601 Extendend format

token:The token identifying the user+device to which the message is directed, as described in the client side documentation.

clear_pending:Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.

replace_tag:If there’s a pending notification with the same tag, delete it before queuing this new one.

data:A JSON object.

从上面的信息格式,我们可以看出来,token是非常重要的一个信息。有了它,我们就可以向我们需要的终端发送推送信息。

我们可以利用我们的SDK来创建一个简单的例程。下面简单介绍一下我们的主要的文件main.qml:

import QtQuick 2.0import Qt.labs.settings 1.0import Ubuntu.Components 0.1import Ubuntu.Components.ListItems 0.1 as ListItemimport Ubuntu.PushNotifications 0.1import "components"MainView {id: "mainView"// objectName for functional testing purposes (autopilot-qt5)objectName: "mainView"// Note! applicationName needs to match the "name" field of the click manifestapplicationName: "com.ubuntu.developer.ralsina.hello"automaticOrientation: trueuseDeprecatedToolbar: falsewidth: units.gu(100)height: units.gu(75)Settings {property alias nick: chatClient.nickproperty alias nickText: nickEdit.textproperty alias nickPlaceholder: nickEdit.placeholderTextproperty alias nickEnabled: nickEdit.enabled}states: [State {name: "no-push-token"when: (pushClient.token == "")PropertyChanges { target: nickEdit; readOnly: true}PropertyChanges { target: nickEdit; focus: true}PropertyChanges { target: messageEdit; enabled: false}PropertyChanges { target: loginButton; enabled: false}PropertyChanges { target: loginButton; text: "Login"}},State {name: "push-token-not-registered"when: ((pushClient.token != "") && (chatClient.registered == false))PropertyChanges { target: nickEdit; readOnly: false}PropertyChanges { target: nickEdit; text: ""}PropertyChanges { target: nickEdit; focus: true}PropertyChanges { target: messageEdit; enabled: false}PropertyChanges { target: loginButton; enabled: true}PropertyChanges { target: loginButton; text: "Login"}},State {name: "registered"when: ((pushClient.token != "") && (chatClient.registered == true))PropertyChanges { target: nickEdit; readOnly: true}PropertyChanges { target: nickEdit; text: "Your nick is " + chatClient.nick}PropertyChanges { target: messageEdit; focus: true}PropertyChanges { target: messageEdit; enabled: true}PropertyChanges { target: loginButton; enabled: true}PropertyChanges { target: loginButton; text: "Logout"}}]state: "no-push-token"ChatClient {id: chatClientonError: {messageList.handle_error(msg)}token: {var i = {"from" : "","to" : "","message" : "Token: " + pushClient.token}if ( pushClient.token )messagesModel.insert(0, i);console.log("token is changed!");return pushClient.token;}}PushClient {id: pushClientComponent.onCompleted: {notificationsChanged.connect(messageList.handle_notifications)error.connect(messageList.handle_error)onTokenChanged: {console.log("token: +" + pushClient.token );console.log("foooooo")}}appId: "com.ubuntu.developer.ralsina.hello_hello"}TextField {id: nickEditplaceholderText: "Your nickname"inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercaseanchors.left: parent.leftanchors.right: loginButton.leftanchors.top: parent.topanchors.leftMargin: units.gu(.5)anchors.rightMargin: units.gu(1)anchors.topMargin: units.gu(.5)onAccepted: { loginButton.clicked() }}Button {id: loginButtonanchors.top: nickEdit.topanchors.right: parent.rightanchors.rightMargin: units.gu(.5)onClicked: {if (chatClient.nick) { // logoutchatClient.nick = ""} else { // loginchatClient.nick = nickEdit.text}}}TextField {id: messageEditinputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercaseanchors.right: parent.rightanchors.left: parent.leftanchors.top: nickEdit.bottomanchors.topMargin: units.gu(1)anchors.rightMargin: units.gu(.5)anchors.leftMargin: units.gu(.5)placeholderText: "Your message"onAccepted: {console.log("sending " + text)var idx = text.indexOf(":")var nick_to = text.substring(0, idx).trim()var msg = text.substring(idx+1, 9999).trim()var i = {"from" : chatClient.nick,"to" : nick_to,"message" : msg}var o = {enabled: annoyingSwitch.checked,persist: persistSwitch.checked,popup: popupSwitch.checked,sound: soundSwitch.checked,vibrate: vibrateSwitch.checked,counter: counterSlider.value}chatClient.sendMessage(i, o)i["type"] = "sent"messagesModel.insert(0, i)text = ""}}ListModel {id: messagesModelListElement {from: ""to: ""type: "info"message: "Register by typing your nick and clicking Login."}ListElement {from: ""to: ""type: "info"message: "Send messages in the form \&;destination: hello\&;"}ListElement {from: ""to: ""type: "info"message: "Slide from the bottom to control notification behaviour."}}UbuntuShape {anchors.left: parent.leftanchors.right: parent.rightanchors.bottom: notificationSettings.bottomanchors.top: messageEdit.bottomanchors.topMargin: units.gu(1)ListView {id: messageListmodel: messagesModelanchors.fill: parentdelegate: Rectangle {MouseArea {anchors.fill: parentonClicked: {if (from != "") {messageEdit.text = from + ": "messageEdit.focus = true}}}height: label.height + units.gu(2)width: parent.widthRectangle {color: {"info": "#B5EBB9","received" : "#A2CFA5","sent" : "#FFF9C8","error" : "#FF4867"}[type]height: label.height + units.gu(1)anchors.fill: parentradius: 5anchors.margins: units.gu(.5)Text {id: labeltext: "<b>" + ((type=="sent")?to:from) + ":</b> " + messagewrapMode: Text.Wrapwidth: parent.width – units.gu(1)x: units.gu(.5)y: units.gu(.5)horizontalAlignment: (type=="sent")?Text.AlignRight:Text.AlignLeft}}}function handle_error(error) {messagesModel.insert(0, {"from" : "","to" : "","type" : "error","message" : "<b>ERROR: " + error + "</b>"})}function handle_notifications(list) {list.forEach(function(notification) {var item = JSON.parse(notification)item["type"] = "received"messagesModel.insert(0, item)})}}}Panel {id: notificationSettingsanchors {left: parent.leftright: parent.rightbottom: parent.bottom}height: item1.height * 9UbuntuShape {anchors.fill: parentcolor: Theme.palette.normal.overlayColumn {id: settingsColumnanchors.fill: parentListItem.Header {text: "<b>Notification Settings</b>"}ListItem.Standard {id: item1text: "Enable Notifications"control: Switch {id: annoyingSwitchchecked: true}}ListItem.Standard {text: "Enable Popup"enabled: annoyingSwitch.checkedcontrol: Switch {id: popupSwitchchecked: true}}ListItem.Standard {text: "Persistent"enabled: annoyingSwitch.checkedcontrol: Switch {id: persistSwitchchecked: true}}ListItem.Standard {text: "Make Sound"enabled: annoyingSwitch.checkedcontrol: Switch {id: soundSwitchchecked: true}}ListItem.Standard {text: "Vibrate"enabled: annoyingSwitch.checkedcontrol: Switch {id: vibrateSwitchchecked: true}}ListItem.Standard {text: "Counter Value"enabled: annoyingSwitch.checkedcontrol: Slider {id: counterSlidervalue: 42}}Button {text: "Set Counter Via Plugin"onClicked: { pushClient.count = counterSlider.value; }}Button {text: "Clear Persistent Notifications"onClicked: { pushClient.clearPersistent([]); }}}}}}

这里,在上面创建一个nickname的输入框及一个login按钮。紧接着,我们创建一个输入信息的对话框。再紧接着,我们创建了一个listview来显示状态,提示信息,或来往的信息。

放弃等于又一次可以选择的机会。

如何在Ubuntu QML应用中使用Push Notification

相关文章:

你感兴趣的文章:

标签云: