Merge lp:~abreu-alexandre/unity-webapps-qml/application-api into lp:unity-webapps-qml

Proposed by Alexandre Abreu
Status: Merged
Merged at revision: 102
Proposed branch: lp:~abreu-alexandre/unity-webapps-qml/application-api
Merge into: lp:unity-webapps-qml
Diff against target: 1421 lines (+1185/-9)
20 files modified
examples/api-bindings/alarm/www/index.html (+4/-0)
examples/api-bindings/content-hub-exporter/www/index.html (+4/-0)
examples/api-bindings/content-hub/www/index.html (+4/-0)
examples/api-bindings/online-accounts/www/index.html (+4/-0)
examples/api-bindings/runtime-api/main.qml.in (+32/-0)
examples/api-bindings/runtime-api/www/index.html (+22/-0)
examples/api-bindings/runtime-api/www/js/app.js (+57/-0)
src/Ubuntu/UnityWebApps/UnityWebApps.pro (+22/-7)
src/Ubuntu/UnityWebApps/UnityWebApps.qml (+3/-0)
src/Ubuntu/UnityWebApps/bindings/runtime-api/backend/runtime-api.js (+171/-0)
src/Ubuntu/UnityWebApps/bindings/runtime-api/client/runtime-api.js (+256/-0)
src/Ubuntu/UnityWebApps/plugin/application-api.cpp (+287/-0)
src/Ubuntu/UnityWebApps/plugin/application-api.h (+75/-0)
src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.cpp (+139/-0)
src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.h (+62/-0)
src/Ubuntu/UnityWebApps/plugin/plugin.pro (+6/-2)
src/Ubuntu/UnityWebApps/plugin/qml-plugin.cpp (+15/-0)
src/Ubuntu/UnityWebApps/unity-webapps-api.js.in (+2/-0)
tests/unit/test_plugin/tst_plugin.cpp (+19/-0)
tests/unit/test_plugin/tst_plugin.h (+1/-0)
To merge this branch: bzr merge lp:~abreu-alexandre/unity-webapps-qml/application-api
Reviewer Review Type Date Requested Status
Alberto Mardegan (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+212727@code.launchpad.net

Commit message

Add application api

Description of the change

Add application api

To post a comment you must log in.
Revision history for this message
Alberto Mardegan (mardy) wrote :

I tested it and it's mostly working. A few comments:

getPlatformInfos: I think it's better to call it "getPlatformInfo" without the final "s". Anyway, this just returns the name of the QPA plugin, so I'd rather rename the method to "getPlatformName" or keep the current name and make it return a dictionary, currently having only one field "name" which contains the platform name. But I'd take the second approach only if you really plan to add more info there in the future.

The example expects the onActivated() and onDeactivated() signal to carry a parameter, but they don't.

All the methods are asynchronous and take a callback; even the applicationName() function is asynchronous, even if the result is immediately available. I'm not familiar with HTML5 development so this might be perfectly fine, I just point this out because coming from the C++ world this sounds rather strange (and a bit inconvenient).

Naming: it's better to use the same convention for all the getters: either start their name always with a "get", or never. Most of the methods you added start with "get", but "applicationName" doesn't.

getInputMethod(): it's not very clear what this function does; maybe rename it to "getInputMethodName"?

nameFromScreenOrientation(): you are only use two possible values, "Landscape" and "Portrait", while Qt supports 4. I guess that most apps don't care about distinguishing between the extra modes, but maybe some do (to be sure that the mic is on a specific side, for instance)?

Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

> I tested it and it's mostly working. A few comments:
>
> getPlatformInfos: I think it's better to call it "getPlatformInfo" without the
> final "s". Anyway, this just returns the name of the QPA plugin, so I'd rather
> rename the method to "getPlatformName" or keep the current name and make it
> return a dictionary, currently having only one field "name" which contains the
> platform name. But I'd take the second approach only if you really plan to add
> more info there in the future.

done,

> The example expects the onActivated() and onDeactivated() signal to carry a
> parameter, but they don't.

done,

> All the methods are asynchronous and take a callback; even the
> applicationName() function is asynchronous, even if the result is immediately
> available. I'm not familiar with HTML5 development so this might be perfectly
> fine, I just point this out because coming from the C++ world this sounds
> rather strange (and a bit inconvenient).

right, agree, took another approach,

> Naming: it's better to use the same convention for all the getters: either
> start their name always with a "get", or never. Most of the methods you added
> start with "get", but "applicationName" doesn't.

done,

> getInputMethod(): it's not very clear what this function does; maybe rename it
> to "getInputMethodName"?

done,

> nameFromScreenOrientation(): you are only use two possible values, "Landscape"
> and "Portrait", while Qt supports 4. I guess that most apps don't care about
> distinguishing between the extra modes, but maybe some do (to be sure that the
> mic is on a specific side, for instance)?

right, agree, done

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
102. By Alexandre Abreu

merge trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alberto Mardegan (mardy) wrote :

I think there's a problem with how the getters are implemented. Let's take getScreenOrientation() for example: when the client calls this method, it will retrieve the application name which was setup, which is correct. If the application registers a callback with onScreenOrientationChanged(), then this._screenOrientation will always get updated propertly, so getScreenOrientation() will always return the proper value; however, if the application doesn't call onScreenOrientationChanged(), this._screenOrientation will never be updated and getScreenOrientation() will always return the old value.

I'm not sure how expensive the switch betwenn client and backend is, so depending on that:

1) If the switch is not very expensive: don't cache any values in the client, always request the properties directly from the backend.

2) If the switch is very expensive, do as you are doing, but register an internal callback to the cached properties in the Application constructor. Something like:

  var self = this;
  this._name = content.name;
  this._proxy.call('onApplicationNameChanged', [function(name) {self._name = name; }]);

(In the latter case we might even want to avoid passing through the backend when the client calls onApplicationNameChanged(): we could make it store the function callback into an array (this._applicationNameListeners) and in the callback registered in the Application constructor we could then invoke all the callbacks in this._applicationNameListeners, after having updated "self._name = name")

Can you also make the getInputMethodName return the value immediately?

Other than that, it all looks good to me!

review: Needs Fixing
Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

> I think there's a problem with how the getters are implemented. Let's take
> getScreenOrientation() for example: when the client calls this method, it will
> retrieve the application name which was setup, which is correct. If the
> application registers a callback with onScreenOrientationChanged(), then
> this._screenOrientation will always get updated propertly, so
> getScreenOrientation() will always return the proper value; however, if the
> application doesn't call onScreenOrientationChanged(), this._screenOrientation
> will never be updated and getScreenOrientation() will always return the old
> value.
>
> I'm not sure how expensive the switch betwenn client and backend is, so
> depending on that:
>
> 1) If the switch is not very expensive: don't cache any values in the client,
> always request the properties directly from the backend.
>
> 2) If the switch is very expensive, do as you are doing, but register an
> internal callback to the cached properties in the Application constructor.
> Something like:
>
> var self = this;
> this._name = content.name;
> this._proxy.call('onApplicationNameChanged', [function(name) {self._name =
> name; }]);
>
> (In the latter case we might even want to avoid passing through the backend
> when the client calls onApplicationNameChanged(): we could make it store the
> function callback into an array (this._applicationNameListeners) and in the
> callback registered in the Application constructor we could then invoke all
> the callbacks in this._applicationNameListeners, after having updated
> "self._name = name")
>
>
> Can you also make the getInputMethodName return the value immediately?

all done

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/api-bindings/alarm/www/index.html'
2--- examples/api-bindings/alarm/www/index.html 2014-01-22 20:38:52 +0000
3+++ examples/api-bindings/alarm/www/index.html 2014-03-26 15:53:02 +0000
4@@ -1,5 +1,9 @@
5+<!DOCTYPE html>
6 <html>
7
8+ <meta charset="utf-8" />
9+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10+
11 <head>
12 <title>Content Hub example</title>
13 <script src="./js/app.js"></script>
14
15=== modified file 'examples/api-bindings/content-hub-exporter/www/index.html'
16--- examples/api-bindings/content-hub-exporter/www/index.html 2014-01-31 01:44:10 +0000
17+++ examples/api-bindings/content-hub-exporter/www/index.html 2014-03-26 15:53:02 +0000
18@@ -1,5 +1,9 @@
19+<!DOCTYPE html>
20 <html>
21
22+ <meta charset="utf-8" />
23+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
24+
25 <head>
26 <title>Content Hub Exporter example</title>
27 <script src="./js/app.js"></script>
28
29=== modified file 'examples/api-bindings/content-hub/www/index.html'
30--- examples/api-bindings/content-hub/www/index.html 2014-02-08 14:48:32 +0000
31+++ examples/api-bindings/content-hub/www/index.html 2014-03-26 15:53:02 +0000
32@@ -1,5 +1,9 @@
33+<!DOCTYPE html>
34 <html>
35
36+ <meta charset="utf-8" />
37+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
38+
39 <head>
40 <title>Content Hub example</title>
41 <script src="./js/app.js"></script>
42
43=== modified file 'examples/api-bindings/online-accounts/www/index.html'
44--- examples/api-bindings/online-accounts/www/index.html 2014-02-26 13:21:37 +0000
45+++ examples/api-bindings/online-accounts/www/index.html 2014-03-26 15:53:02 +0000
46@@ -1,5 +1,9 @@
47+<!DOCTYPE html>
48 <html>
49
50+ <meta charset="utf-8" />
51+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
52+
53 <head>
54 <title>Content Hub example</title>
55 <script src="./js/app.js"></script>
56
57=== added directory 'examples/api-bindings/runtime-api'
58=== added file 'examples/api-bindings/runtime-api/main.qml.in'
59--- examples/api-bindings/runtime-api/main.qml.in 1970-01-01 00:00:00 +0000
60+++ examples/api-bindings/runtime-api/main.qml.in 2014-03-26 15:53:02 +0000
61@@ -0,0 +1,32 @@
62+import QtQuick 2.0
63+import QtWebKit 3.0
64+import QtWebKit.experimental 1.0
65+import Ubuntu.Components 0.1
66+import Ubuntu.UnityWebApps 0.1
67+
68+MainView {
69+ id: root
70+ focus: true
71+ applicationName: \"runtime-api\"
72+
73+ width: units.gu(100)
74+ height: units.gu(100)
75+
76+ WebView {
77+ id: webview
78+ anchors.fill: parent
79+ url: \"file://$$OUT_PWD/runtime-api/www/index.html\"
80+
81+ experimental.preferences.navigatorQtObjectEnabled: true
82+ experimental.preferences.developerExtrasEnabled: true
83+
84+ function getUnityWebappsProxies() {
85+ return UnityWebAppsUtils.makeProxiesForQtWebViewBindee(webview);
86+ }
87+
88+ UnityWebApps {
89+ id: webapps
90+ bindee: webview
91+ }
92+ }
93+}
94
95=== added directory 'examples/api-bindings/runtime-api/www'
96=== added file 'examples/api-bindings/runtime-api/www/index.html'
97--- examples/api-bindings/runtime-api/www/index.html 1970-01-01 00:00:00 +0000
98+++ examples/api-bindings/runtime-api/www/index.html 2014-03-26 15:53:02 +0000
99@@ -0,0 +1,22 @@
100+<!DOCTYPE html>
101+<html>
102+
103+ <meta charset="utf-8" />
104+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
105+
106+ <head>
107+ <title>Runtime Api example</title>
108+ <script src="./js/app.js"></script>
109+ </head>
110+
111+ <body>
112+
113+ <div>
114+ Results:
115+ <div id="results">
116+ </div>
117+ </div>
118+
119+ </body>
120+
121+</html>
122
123=== added directory 'examples/api-bindings/runtime-api/www/js'
124=== added file 'examples/api-bindings/runtime-api/www/js/app.js'
125--- examples/api-bindings/runtime-api/www/js/app.js 1970-01-01 00:00:00 +0000
126+++ examples/api-bindings/runtime-api/www/js/app.js 2014-03-26 15:53:02 +0000
127@@ -0,0 +1,57 @@
128+window.onload = function() {
129+ function setResult(message) {
130+ var results = document.getElementById('results');
131+ results.innerHTML += message + '<br>';
132+ }
133+
134+ var last = 0;
135+ if (localStorage.getItem("lastkilled") !== null)
136+ last = localStorage.getItem("lastkilled");
137+
138+ setResult('last killed: ' + last);
139+
140+ var api = external.getUnityObject('1.0');
141+
142+ api.RuntimeApi.getApplication(function(application) {
143+ setResult('application name: ' + application.getApplicationName());
144+ setResult('application info: ' + JSON.stringify(application.getPlatformInfo()));
145+
146+ application.getInputMethodName(function(name) {
147+ if (name.length == 0)
148+ setResult('input method: no OSK available');
149+ else
150+ setResult('input method: ' + name);
151+ });
152+
153+ setResult('screen orientation: ' + application.getScreenOrientation());
154+
155+ application.onScreenOrientationChanged(function(name) {
156+ setResult('Event: orientation changed - ' + name);
157+ });
158+
159+ application.onAboutToQuit(function(killed) {
160+ localStorage.setItem("lastkilled", last + 1);
161+
162+ console.log('killed: ' + killed)
163+
164+ setResult('onAboutToQuit: ' + killed);
165+ });
166+
167+ application.onDeactivated(function() {
168+ setResult('Event: application deactivated');
169+ });
170+
171+ application.onActivated(function() {
172+ setResult('Event: application activated');
173+ });
174+
175+ application.onInputMethodVisibilityChanged(function(visibility) {
176+ setResult('Event: onInputMethodVisibilityChanged - ' + visibility);
177+ });
178+
179+ application.setupUriHandler(function(uris) {
180+ setResult('Event: received URI to open w/ UriHandler - ' + JSON.stringify(uris));
181+ });
182+
183+ });
184+};
185
186=== modified file 'src/Ubuntu/UnityWebApps/UnityWebApps.pro'
187--- src/Ubuntu/UnityWebApps/UnityWebApps.pro 2014-01-17 15:51:27 +0000
188+++ src/Ubuntu/UnityWebApps/UnityWebApps.pro 2014-03-26 15:53:02 +0000
189@@ -21,10 +21,13 @@
190 #
191 # deployment directives
192 #
193-JS_FILES = $$system(ls *.js) \
194- $${UNITY_API_JS_FILE} \
195+PLUGIN_JS_FILES = \
196+ $$system(ls *.js) \
197+ $${UNITY_API_JS_FILE}
198+
199+CLIENT_JS_FILES = \
200 $$system(ls ./common/*/*.js) \
201- $$system(ls ./bindings/*/*/*.js)
202+ $$system(ls ./bindings/*/client/*.js)
203
204 QML_FILES = $$system(ls *.qml)
205
206@@ -32,7 +35,9 @@
207 QMAKE_SUBSTITUTES += qmldir.in
208
209 OTHER_FILES += $$QML_FILES \
210- $$JS_FILES \
211+ $$PLUGIN_JS_FILES \
212+ $$CLIENT_JS_FILES \
213+ $$system(ls ./bindings/*/backend/*.js) \
214 qmldir.in \
215 unity-webapps-api.js.in \
216 $${UNITY_API_JS_FILE}
217@@ -44,9 +49,19 @@
218
219 qmldir_file.path = $$installPath
220 qmldir_file.files = $$QMLDIR_FILE
221+
222 qml_files.path = $$installPath
223 qml_files.files = $$QML_FILES
224+
225 js_files.path = $$installPath
226-js_files.files = $$JS_FILES
227-
228-INSTALLS += qmldir_file qml_files js_files
229+js_files.files = $$PLUGIN_JS_FILES
230+
231+runtime_api_binding_backend_js_files.path = $$installPath/bindings/runtime-api/backend/
232+runtime_api_binding_backend_js_files.files = ./bindings/runtime-api/backend/runtime-api.js
233+
234+INSTALLS += qmldir_file \
235+ qml_files \
236+ js_files \
237+ runtime_api_binding_backend_js_files \
238+
239+
240
241=== modified file 'src/Ubuntu/UnityWebApps/UnityWebApps.qml'
242--- src/Ubuntu/UnityWebApps/UnityWebApps.qml 2014-03-17 17:25:53 +0000
243+++ src/Ubuntu/UnityWebApps/UnityWebApps.qml 2014-03-26 15:53:02 +0000
244@@ -21,6 +21,7 @@
245 import "UnityWebApps.js" as UnityWebAppsJs
246 import "UnityWebAppsUtils.js" as UnityWebAppsJsUtils
247 import "UnityWebAppsBackendComponents.js" as UnityBackends
248+import "./bindings/runtime-api/backend/runtime-api.js" as RuntimeApiBackend
249
250
251 /*!
252@@ -649,6 +650,8 @@
253 return UnityBackends.createContentHubApi(UnityBackends.backendDelegate)
254 }),
255
256+ RuntimeApi: RuntimeApiBackend.createRuntimeApi(UnityBackends.backendDelegate),
257+
258 Launcher: {
259 setCount: function (count) {
260 if (!initialized)
261
262=== added directory 'src/Ubuntu/UnityWebApps/bindings/runtime-api'
263=== added directory 'src/Ubuntu/UnityWebApps/bindings/runtime-api/backend'
264=== added file 'src/Ubuntu/UnityWebApps/bindings/runtime-api/backend/runtime-api.js'
265--- src/Ubuntu/UnityWebApps/bindings/runtime-api/backend/runtime-api.js 1970-01-01 00:00:00 +0000
266+++ src/Ubuntu/UnityWebApps/bindings/runtime-api/backend/runtime-api.js 2014-03-26 15:53:02 +0000
267@@ -0,0 +1,171 @@
268+/*
269+ * Copyright 2014 Canonical Ltd.
270+ *
271+ * This file is part of unity-webapps-qml.
272+ *
273+ * unity-webapps-qml is free software; you can redistribute it and/or modify
274+ * it under the terms of the GNU General Public License as published by
275+ * the Free Software Foundation; version 3.
276+ *
277+ * unity-webapps-qml is distributed in the hope that it will be useful,
278+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
279+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
280+ * GNU General Public License for more details.
281+ *
282+ * You should have received a copy of the GNU General Public License
283+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
284+ */
285+
286+.import Ubuntu.UnityWebApps 0.1 as UnityWebAppsBridge
287+.import Ubuntu.Components 0.1 as ComponentsBridge
288+
289+
290+/**
291+ *
292+ * Runtime API backend binding
293+ *
294+ */
295+function createRuntimeApi(backendDelegate) {
296+ var PLUGIN_URI = 'Ubuntu.UnityWebApps';
297+ var VERSION = 0.1;
298+
299+ var applicationApiInstance = UnityWebAppsBridge.ApplicationApi;
300+
301+ function Application() {
302+ // no need to have a specific id since this class is mostly a passtrough one
303+ this._id = 0;
304+ };
305+ Application.prototype = {
306+
307+ // object methods
308+ serialize: function() {
309+ var self = this;
310+ return {
311+ type: 'object-proxy',
312+ apiid: 'RuntimeApi',
313+ objecttype: 'Application',
314+ objectid: this._id,
315+
316+ content: {
317+ name: applicationApiInstance.applicationName,
318+ platform: applicationApiInstance.applicationPlatform,
319+ writableLocation: applicationApiInstance.applicationDataPath,
320+ screenOrientation: applicationApiInstance.screenOrientation,
321+ }
322+ }
323+ },
324+
325+ getApplicationName: function(callback) {
326+ if (callback && typeof(callback) === 'function')
327+ callback(applicationApiInstance.applicationName);
328+ },
329+ onApplicationNameChanged: function(callback) {
330+ if (callback && typeof(callback) === 'function')
331+ applicationApiInstance.applicationNameChanged.connect(function() {
332+ callback(applicationApiInstance.applicationName);
333+ });
334+ },
335+
336+ getApplicationWritableLocation: function(callback) {
337+ if (callback && typeof(callback) === 'function')
338+ callback(applicationApiInstance.applicationDataPath);
339+ },
340+
341+ getPlatformInfo: function(callback) {
342+ if (callback && typeof(callback) === 'function') {
343+ var info = {};
344+ info.name = applicationApiInstance.applicationPlatform;
345+ callback(info);
346+ }
347+ },
348+
349+ setInputMethodVisible: function(visible, callback) {
350+ applicationApiInstance.setInputMethodVisible(visible);
351+ if (callback && typeof(callback) === 'function')
352+ callback();
353+ },
354+ getInputMethodName: function(callback) {
355+ if (callback && typeof(callback) === 'function')
356+ callback(applicationApiInstance.getInputMethodName());
357+ },
358+ onInputMethodVisibilityChanged: function(callback) {
359+ if (callback && typeof(callback) === 'function')
360+ Qt.inputMethod.onVisibleChanged.connect(function() {
361+ callback(Qt.inputMethod.visible)
362+ });
363+ },
364+
365+ onAboutToQuit: function(callback) {
366+ if (callback && typeof(callback) === 'function')
367+ applicationApiInstance.applicationAboutToQuit.connect(function(killed) {
368+ callback(killed);
369+ });
370+ },
371+
372+ setupUriHandler: function(callback) {
373+ if (callback && typeof(callback) === 'function')
374+ var urihandler = ComponentsBridge.UriHandler;
375+ urihandler.opened.connect(function(uris, data) {
376+ var translatedUris = []
377+ for (var idx in uris) {
378+ translatedUris.push(uris[idx])
379+ }
380+ callback(translatedUris);
381+ });
382+ },
383+
384+ onDeactivated: function(callback) {
385+ if (callback && typeof(callback) === 'function')
386+ applicationApiInstance.applicationDeactivated.connect(callback);
387+ },
388+
389+ onActivated: function(callback) {
390+ if (callback && typeof(callback) === 'function')
391+ applicationApiInstance.applicationActivated.connect(callback);
392+ },
393+
394+ getScreenOrientation: function(callback) {
395+ if (callback && typeof(callback) === 'function')
396+ callback(applicationApiInstance.screenOrientation);
397+ },
398+ onScreenOrientationChanged: function(callback) {
399+ if (callback && typeof(callback) === 'function')
400+ applicationApiInstance.applicationScreenOrientationChanged.connect(callback);
401+ },
402+ };
403+
404+ function _constructorFromName(className) {
405+ var constructorPerName = {
406+ "Application": Application,
407+ };
408+ return className in constructorPerName
409+ ? constructorPerName[className]
410+ : null;
411+ }
412+
413+ return {
414+ getApplication: function(callback) {
415+ var application = new Application();
416+ callback(application.serialize());
417+ },
418+
419+ // Internal
420+
421+ dispatchToObject: function(infos) {
422+ var args = infos.args;
423+ var callback = infos.callback;
424+ var method_name = infos.method_name;
425+ var objectid = infos.objectid;
426+ var class_name = infos.class_name;
427+
428+ if (callback)
429+ args.push(callback);
430+
431+ var Constructor = _constructorFromName(class_name);
432+
433+ var instance = new Constructor(objectid);
434+
435+ instance[method_name].apply(instance, args);
436+ }
437+ };
438+}
439
440=== added directory 'src/Ubuntu/UnityWebApps/bindings/runtime-api/client'
441=== added file 'src/Ubuntu/UnityWebApps/bindings/runtime-api/client/runtime-api.js'
442--- src/Ubuntu/UnityWebApps/bindings/runtime-api/client/runtime-api.js 1970-01-01 00:00:00 +0000
443+++ src/Ubuntu/UnityWebApps/bindings/runtime-api/client/runtime-api.js 2014-03-26 15:53:02 +0000
444@@ -0,0 +1,256 @@
445+/*
446+ * Copyright 2014 Canonical Ltd.
447+ *
448+ * This file is part of unity-webapps-qml.
449+ *
450+ * unity-webapps-qml is free software; you can redistribute it and/or modify
451+ * it under the terms of the GNU General Public License as published by
452+ * the Free Software Foundation; version 3.
453+ *
454+ * unity-webapps-qml is distributed in the hope that it will be useful,
455+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
456+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
457+ * GNU General Public License for more details.
458+ *
459+ * You should have received a copy of the GNU General Public License
460+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
461+ */
462+
463+
464+/**
465+ * RuntimeApi gives access to the application runtime information and management.
466+
467+ * @module RuntimeApi
468+ */
469+function createRuntimeApi(backendBridge) {
470+ var PLUGIN_URI = 'RuntimeApi';
471+
472+ function Application(id, content) {
473+ this._proxy = backendBridge.createRemoteObject(
474+ PLUGIN_URI, 'Application', id);
475+
476+ this._name = content.name;
477+ this._platform = content.platform;
478+ this._writableLocation = content.writableLocation;
479+ this._screenOrientation = content.screenOrientation;
480+ };
481+ Application.prototype = {
482+
483+ /**
484+ * Retrieves the application name.
485+ *
486+ * @method getApplicationName
487+ * @return {String} application name
488+ */
489+ getApplicationName: function() {
490+ return this._name;
491+ },
492+
493+ /**
494+ * Sets up a callback that is to be called when the application's name changed.
495+ *
496+ * @method onApplicationNameChanged
497+ * @param callback {Function(String)} Function to be called when the application's name has changed.
498+ */
499+ onApplicationNameChanged: function(callback) {
500+ var self = this;
501+ this._proxy.call('onApplicationNameChanged'
502+ , [function(name) {self._name = name; callback(name); }]);
503+ },
504+
505+ /**
506+ * Retrieves the fileystem location where the application is allowed to write its data in.
507+ *
508+ * @method getApplicationWritableLocation
509+ * @return {String} application writable location path
510+ */
511+ getApplicationWritableLocation: function() {
512+ return this._writableLocation;
513+ },
514+
515+ /**
516+ * Retrieves current platform information.
517+ *
518+ * @method getPlatformInfos
519+ * @return {Object} platform information as a dictionary with the following keys:
520+ * - name: the platform name
521+ */
522+ getPlatformInfo: function() {
523+ return this._platform;
524+ },
525+
526+ /**
527+ * Sets up a callback that is to be called when the application is about to quit.
528+ *
529+ * @method onAboutToQuit
530+ * @param callback {Function()} Function to be called when the application is about to quit.
531+ */
532+ onAboutToQuit: function(callback) {
533+ this._proxy.call('onAboutToQuit'
534+ , [callback]);
535+ },
536+
537+ /**
538+ * Sets up a callback that is to be called when the application has been deactivated (background).
539+ *
540+ * @method onDeactivated
541+ * @param callback {Function()} Function to be called when the application has been deactivated.
542+ */
543+ onDeactivated: function(callback) {
544+ this._proxy.call('onDeactivated'
545+ , [callback]);
546+ },
547+
548+ /**
549+ * Sets up a callback that is to be called when the application has been activated (from background).
550+ *
551+ * @method onActivated
552+ * @param callback {Function()} Function to be called when the application has been activated.
553+ */
554+ onActivated: function(callback) {
555+ this._proxy.call('onActivated'
556+ , [callback]);
557+ },
558+
559+ /**
560+ * Retrieves the current screen orientation.
561+ *
562+ * @method getScreenOrientation
563+ * @return {ScreenOrientation} current screen orientation.
564+ */
565+ getScreenOrientation: function() {
566+ return this._screenOrientation;
567+ },
568+
569+ /**
570+ * Sets up a callback that is to be called when the application's screen has changed its orientation.
571+ *
572+ * @method onScreenOrientationChanged
573+ * @param callback {Function(ScreenOrientation)} Function to be called when the application's screen orientation has changed.
574+ */
575+ onScreenOrientationChanged: function(callback) {
576+ var self = this;
577+ this._proxy.call('onScreenOrientationChanged'
578+ , [function(orientation) {self._screenOrientation = orientation; callback(orientation); }]);
579+ },
580+
581+ /**
582+ * Sets up a URI handler. The application can be sent URIs to open.
583+ *
584+ * @method setupUriHandler
585+ * @param callback {Function([String])} Function to be called with the current list of uris to open
586+ */
587+ setupUriHandler: function(callback) {
588+ this._proxy.call('setupUriHandler'
589+ , [callback]);
590+ },
591+
592+ /**
593+ * Retrieves the current input method's name. The name varies depending on the platform
594+ * e.g. maliit can be part of the name for a maliit based Virtual Keyboard (possibly mangled
595+ * with e.g. 'phablet'), when a keyboard is there the name can be empty, ...
596+ *
597+ * @method getInputMethodName
598+ * @param callback {Function(String)} Function to be called with the current input method name
599+ */
600+ getInputMethodName: function(callback) {
601+ this._proxy.call('getInputMethodName'
602+ , []
603+ , callback);
604+ },
605+
606+ /**
607+ * Sets up a callback that is to be called when the On Screen Keyboard visibility has changed.
608+ *
609+ * @method onInputMethodVisibilityChanged
610+ * @param callback {Function(Bool)} Function to be called when the On Screen Keyboard visibility has changed (received the visibility as an arg).
611+ */
612+ onInputMethodVisibilityChanged: function(callback) {
613+ this._proxy.call('onInputMethodVisibilityChanged'
614+ , [callback]);
615+ }
616+ };
617+
618+ function _constructorFromName(className) {
619+ var constructorPerName = {
620+ "Application": Application,
621+ };
622+ return className in constructorPerName
623+ ? constructorPerName[className]
624+ : null;
625+ };
626+
627+
628+/**
629+ * The RuntimeApi object
630+
631+ * @class RuntimeApi
632+ * @constructor
633+ * @example
634+
635+ var api = external.getUnityObject('1.0');
636+ api.RuntimeApi.getApplication(function(application) {
637+ console.log('Application name: ' + application.getApplicationName());
638+ });
639+ */
640+ return {
641+ /**
642+ Enumeration of the available types of ScreenOrientation.
643+
644+ Values:
645+
646+ Landscape: The application screen is in landscape mode
647+
648+ InvertedLandscape: The application screen is in inverted landscape mode
649+
650+ Portrait: The application screen is in portrait mode
651+
652+ InvertedPortrait: The application screen is in inverted portrait mode
653+
654+ Unknown: The application screen is in an unknown mode
655+
656+ @static
657+ @property ScreenOrientation {Object}
658+
659+ @example
660+
661+ var api = external.getUnityObject('1.0');
662+ var orientation = api.RuntimeApi.ScreenOrientation;
663+ // use orientation.Landscape or orientation.Portrait
664+ */
665+ ScreenOrientation: {
666+ Landscape: "Landscape",
667+
668+ InvertedLandscape: "InvertedLandscape",
669+
670+ Portrait: "Portrait",
671+
672+ InvertedPortrait: "InvertedPortrait",
673+
674+ Unknwon: "Unknown",
675+ },
676+
677+ /**
678+ * Creates an Application object.
679+ *
680+ * @method getApplication
681+ * @param callback {Function (Application)}
682+ */
683+ getApplication: function(callback) {
684+ backendBridge.call('RuntimeApi.getApplication'
685+ , []
686+ , callback);
687+ },
688+
689+ /**
690+ * @private
691+ *
692+ */
693+ createObjectWrapper: function(objectType, objectId, content) {
694+ var Constructor = _constructorFromName(objectType);
695+ return new Constructor(objectId, content);
696+ },
697+ };
698+};
699+
700+
701
702=== added file 'src/Ubuntu/UnityWebApps/plugin/application-api.cpp'
703--- src/Ubuntu/UnityWebApps/plugin/application-api.cpp 1970-01-01 00:00:00 +0000
704+++ src/Ubuntu/UnityWebApps/plugin/application-api.cpp 2014-03-26 15:53:02 +0000
705@@ -0,0 +1,287 @@
706+/*
707+ * Copyright 2014 Canonical Ltd.
708+ *
709+ * This file is part of unity-webapps-qml.
710+ *
711+ * unity-webapps-qml is free software; you can redistribute it and/or modify
712+ * it under the terms of the GNU General Public License as published by
713+ * the Free Software Foundation; version 3.
714+ *
715+ * unity-webapps-qml is distributed in the hope that it will be useful,
716+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
717+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
718+ * GNU General Public License for more details.
719+ *
720+ * You should have received a copy of the GNU General Public License
721+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
722+ */
723+
724+#include "application-api.h"
725+
726+#include "application-signal-to-qt-bridge.h"
727+
728+#include <QDebug>
729+#include <QGuiApplication>
730+#include <QScreen>
731+#include <QDir>
732+#include <QtCore/QCoreApplication>
733+#include <QtCore/QStandardPaths>
734+#include <signal.h>
735+
736+
737+namespace
738+{
739+
740+QString
741+nameFromScreenOrientation (Qt::ScreenOrientation orientation)
742+{
743+ switch (orientation)
744+ {
745+ case Qt::InvertedLandscapeOrientation:
746+ return QString("InvertedLandscape");
747+
748+ case Qt::LandscapeOrientation:
749+ return QString("Landscape");
750+
751+ case Qt::PortraitOrientation:
752+ return QString("Portrait");
753+
754+ case Qt::InvertedPortraitOrientation:
755+ return QString("InvertedPortrait");
756+
757+ case Qt::PrimaryOrientation:
758+ return QString("Primary");
759+
760+ default:
761+ break;
762+ }
763+
764+ return QString("Unknown");
765+}
766+
767+}
768+
769+class ApplicationApiEventListener : public QObject
770+{
771+ Q_OBJECT
772+
773+public:
774+ ApplicationApiEventListener(QObject * parent)
775+ : QObject(parent)
776+ {
777+ if (QGuiApplication::instance())
778+ QGuiApplication::instance()->installEventFilter(this);
779+ }
780+ ~ApplicationApiEventListener()
781+ {
782+ if (QGuiApplication::instance())
783+ QGuiApplication::instance()->removeEventFilter(this);
784+ }
785+
786+ bool eventFilter(QObject *obj,
787+ QEvent *event)
788+ {
789+ switch (event->type())
790+ {
791+ case QEvent::ApplicationActivate:
792+ Q_EMIT activated();
793+ break;
794+ case QEvent::ApplicationDeactivate:
795+ Q_EMIT deactivated();
796+ break;
797+ default:
798+ break;
799+ }
800+ return QObject::eventFilter(obj, event);
801+ }
802+
803+Q_SIGNALS:
804+
805+ void activated();
806+ void deactivated();
807+};
808+
809+
810+class ApplicationApiPrivate: public QObject
811+{
812+ Q_OBJECT
813+
814+public:
815+ ApplicationApiPrivate(QObject * parent)
816+ : QObject(parent),
817+ _applicationEventListener(new ApplicationApiEventListener(this)),
818+ _applicationSignalBridge(new ApplicationSignalToQtBridge(this))
819+ {}
820+ ~ApplicationApiPrivate()
821+ {
822+ delete _applicationEventListener;
823+ delete _applicationSignalBridge;
824+ }
825+
826+ ApplicationApiEventListener * _applicationEventListener;
827+ ApplicationSignalToQtBridge * _applicationSignalBridge;
828+};
829+
830+
831+ApplicationApi::ApplicationApi(QObject *parent) :
832+ QObject(parent),
833+ d_ptr(new ApplicationApiPrivate(this))
834+
835+{
836+ Q_D(ApplicationApi);
837+
838+ QObject::connect(QCoreApplication::instance(),
839+ &QCoreApplication::aboutToQuit,
840+ this,
841+ &ApplicationApi::aboutToQuit);
842+
843+ QObject::connect(d->_applicationEventListener,
844+ &ApplicationApiEventListener::activated,
845+ this,
846+ &ApplicationApi::activated);
847+
848+ QObject::connect(d->_applicationEventListener,
849+ &ApplicationApiEventListener::deactivated,
850+ this,
851+ &ApplicationApi::deactivated);
852+
853+ QObject::connect(d->_applicationSignalBridge,
854+ &ApplicationSignalToQtBridge::onSignalRaised,
855+ this,
856+ &ApplicationApi::signalReceived);
857+
858+ // We explictly handle the SIGTERM signal case that is being sent
859+ // on Touch to an application being killed by the platform.
860+ // Upstart sends this signal when the application is closed from app scope.
861+ d->_applicationSignalBridge->addSignalHandlerFor(SIGTERM);
862+
863+ QScreen * screen = QGuiApplication::primaryScreen();
864+ if (screen)
865+ {
866+ QObject::connect(screen,
867+ &QScreen::orientationChanged,
868+ this,
869+ &ApplicationApi::screenOrientationChanged);
870+ }
871+}
872+
873+ApplicationApi::~ApplicationApi()
874+{
875+ Q_D(ApplicationApi);
876+
877+ QObject::disconnect(QCoreApplication::instance(),
878+ &QCoreApplication::aboutToQuit,
879+ this,
880+ &ApplicationApi::aboutToQuit);
881+
882+ QObject::disconnect(d->_applicationEventListener,
883+ &ApplicationApiEventListener::activated,
884+ this,
885+ &ApplicationApi::activated);
886+
887+ QObject::disconnect(d->_applicationEventListener,
888+ &ApplicationApiEventListener::deactivated,
889+ this,
890+ &ApplicationApi::deactivated);
891+
892+ QObject::disconnect(d->_applicationSignalBridge,
893+ &ApplicationSignalToQtBridge::onSignalRaised,
894+ this,
895+ &ApplicationApi::signalReceived);
896+
897+ QScreen * screen = QGuiApplication::primaryScreen();
898+ if (screen)
899+ {
900+ QObject::disconnect(screen,
901+ &QScreen::orientationChanged,
902+ this,
903+ &ApplicationApi::screenOrientationChanged);
904+ }
905+
906+ delete d_ptr;
907+}
908+
909+QString ApplicationApi::getApplicationName() const
910+{
911+ if ( ! qgetenv("APP_ID").isEmpty())
912+ return qgetenv("APP_ID");
913+
914+ return QCoreApplication::applicationName();
915+}
916+
917+QString ApplicationApi::getApplicationDataPath() const
918+{
919+ QDir dataLocation(
920+ QStandardPaths::writableLocation(
921+ QStandardPaths::DataLocation));
922+
923+ if (!dataLocation.exists()) {
924+ QDir::root().mkpath(dataLocation.absolutePath());
925+ }
926+
927+ return dataLocation.absolutePath();
928+}
929+
930+QString ApplicationApi::getApplicationScreenOrientation() const
931+{
932+ QScreen * screen = QGuiApplication::primaryScreen();
933+
934+ return nameFromScreenOrientation(screen->primaryOrientation());
935+}
936+
937+QString ApplicationApi::getInputMethodName() const
938+{
939+ return QString(getenv("QT_IM_MODULE"));
940+}
941+
942+void ApplicationApi::setInputMethodVisible(bool visible)
943+{
944+ QGuiApplication::inputMethod()->setVisible(visible);
945+ if (visible)
946+ QGuiApplication::inputMethod()->show();
947+ else
948+ QGuiApplication::inputMethod()->hide();
949+}
950+
951+QString ApplicationApi::getApplicationPlatform() const
952+{
953+ return QGuiApplication::platformName();
954+}
955+
956+void ApplicationApi::aboutToQuit()
957+{
958+ Q_EMIT applicationAboutToQuit(false);
959+}
960+
961+void ApplicationApi::activated()
962+{
963+ Q_EMIT applicationActivated();
964+}
965+
966+void ApplicationApi::deactivated()
967+{
968+ Q_EMIT applicationDeactivated();
969+}
970+
971+void ApplicationApi::signalReceived(int type)
972+{
973+ if (type != SIGTERM && type != SIGINT)
974+ return;
975+
976+ bool killed = (type == SIGTERM);
977+
978+ Q_EMIT applicationAboutToQuit(killed);
979+
980+ QCoreApplication::quit();
981+}
982+
983+void ApplicationApi::screenOrientationChanged(Qt::ScreenOrientation orientation)
984+{
985+ Q_EMIT applicationScreenOrientationChanged(
986+ nameFromScreenOrientation(
987+ orientation));
988+}
989+
990+
991+#include "application-api.moc"
992+
993
994=== added file 'src/Ubuntu/UnityWebApps/plugin/application-api.h'
995--- src/Ubuntu/UnityWebApps/plugin/application-api.h 1970-01-01 00:00:00 +0000
996+++ src/Ubuntu/UnityWebApps/plugin/application-api.h 2014-03-26 15:53:02 +0000
997@@ -0,0 +1,75 @@
998+/*
999+ * Copyright 2014 Canonical Ltd.
1000+ *
1001+ * This file is part of unity-webapps-qml.
1002+ *
1003+ * unity-webapps-qml is free software; you can redistribute it and/or modify
1004+ * it under the terms of the GNU General Public License as published by
1005+ * the Free Software Foundation; version 3.
1006+ *
1007+ * unity-webapps-qml is distributed in the hope that it will be useful,
1008+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1009+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1010+ * GNU General Public License for more details.
1011+ *
1012+ * You should have received a copy of the GNU General Public License
1013+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1014+ */
1015+
1016+#ifndef UNITY_WEBAPPS_APPLICATIONAPI_H
1017+#define UNITY_WEBAPPS_APPLICATIONAPI_H
1018+
1019+#include <QObject>
1020+
1021+class ApplicationApiPrivate;
1022+
1023+class ApplicationApi : public QObject
1024+{
1025+ Q_OBJECT
1026+ Q_PROPERTY(QString applicationName READ getApplicationName NOTIFY applicationNameChanged)
1027+ Q_PROPERTY(QString screenOrientation READ getApplicationScreenOrientation NOTIFY applicationScreenOrientationChanged)
1028+ Q_PROPERTY(QString applicationDataPath READ getApplicationDataPath)
1029+ Q_PROPERTY(QString applicationPlatform READ getApplicationPlatform)
1030+
1031+
1032+public:
1033+ explicit ApplicationApi(QObject *parent = 0);
1034+ ~ApplicationApi();
1035+
1036+ QString getApplicationName() const;
1037+
1038+ QString getApplicationDataPath() const;
1039+
1040+ QString getApplicationPlatform() const;
1041+
1042+ QString getApplicationScreenOrientation() const;
1043+
1044+ Q_INVOKABLE QString getInputMethodName() const;
1045+ Q_INVOKABLE void setInputMethodVisible(bool);
1046+
1047+
1048+Q_SIGNALS:
1049+
1050+ void applicationNameChanged();
1051+ void applicationAboutToQuit(bool killed);
1052+ void applicationDeactivated();
1053+ void applicationActivated();
1054+ void applicationScreenOrientationChanged(QString);
1055+
1056+
1057+public Q_SLOTS:
1058+
1059+ void aboutToQuit();
1060+ void deactivated();
1061+ void activated();
1062+ void screenOrientationChanged(Qt::ScreenOrientation);
1063+ void signalReceived(int type);
1064+
1065+
1066+private:
1067+
1068+ ApplicationApiPrivate *d_ptr;
1069+ Q_DECLARE_PRIVATE(ApplicationApi)
1070+};
1071+
1072+#endif // UNITY_WEBAPPS_APPLICATIONAPI_H
1073
1074=== added file 'src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.cpp'
1075--- src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.cpp 1970-01-01 00:00:00 +0000
1076+++ src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.cpp 2014-03-26 15:53:02 +0000
1077@@ -0,0 +1,139 @@
1078+/*
1079+ * Copyright 2014 Canonical Ltd.
1080+ *
1081+ * This file is part of unity-webapps-qml.
1082+ *
1083+ * unity-webapps-qml is free software; you can redistribute it and/or modify
1084+ * it under the terms of the GNU General Public License as published by
1085+ * the Free Software Foundation; version 3.
1086+ *
1087+ * unity-webapps-qml is distributed in the hope that it will be useful,
1088+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1089+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1090+ * GNU General Public License for more details.
1091+ *
1092+ * You should have received a copy of the GNU General Public License
1093+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1094+ */
1095+
1096+#include "application-signal-to-qt-bridge.h"
1097+
1098+#include <QDebug>
1099+#include <QSocketNotifier>
1100+
1101+#include <sys/socket.h>
1102+#include <signal.h>
1103+#include <unistd.h>
1104+
1105+/**
1106+ * @brief The ApplicationSignalToQtBridgePrivate class
1107+ *
1108+ * It does the job described here:
1109+ *
1110+ * https://qt-project.org/doc/qt-5.0/qtdoc/unix-signals.html
1111+ *
1112+ * and bridges Unix signal handlers & qt objects in a proper
1113+ * way.
1114+ *
1115+ */
1116+
1117+class ApplicationSignalToQtBridgePrivate: public QObject
1118+{
1119+ Q_OBJECT
1120+
1121+public:
1122+ ApplicationSignalToQtBridgePrivate(QObject * parent)
1123+ : QObject(parent),
1124+ _signalSocketNotifier(NULL)
1125+ {}
1126+ ~ApplicationSignalToQtBridgePrivate()
1127+ {
1128+ delete _signalSocketNotifier;
1129+ }
1130+
1131+ QSocketNotifier * _signalSocketNotifier;
1132+};
1133+
1134+
1135+int ApplicationSignalToQtBridge::signalSocketPair[2];
1136+
1137+ApplicationSignalToQtBridge::ApplicationSignalToQtBridge(QObject *parent)
1138+ : QObject(parent)
1139+ , d_ptr(new ApplicationSignalToQtBridgePrivate(this))
1140+{}
1141+
1142+ApplicationSignalToQtBridge::~ApplicationSignalToQtBridge()
1143+{
1144+ delete d_ptr;
1145+}
1146+
1147+void ApplicationSignalToQtBridge::setupQtSignalListener()
1148+{
1149+ Q_D(ApplicationSignalToQtBridge);
1150+
1151+ if (0 != ::socketpair(AF_UNIX, SOCK_STREAM, 0, signalSocketPair))
1152+ {
1153+ qFatal("Couldn't create HUP socketpair");
1154+ }
1155+
1156+ d->_signalSocketNotifier =
1157+ new QSocketNotifier (signalSocketPair[1],
1158+ QSocketNotifier::Read,
1159+ this);
1160+
1161+ connect(d->_signalSocketNotifier,
1162+ SIGNAL(activated(int)),
1163+ this,
1164+ SLOT(handleSignal(int)));
1165+}
1166+
1167+bool ApplicationSignalToQtBridge::addSignalHandlerFor(int type)
1168+{
1169+ Q_D(ApplicationSignalToQtBridge);
1170+
1171+ if ( ! d->_signalSocketNotifier)
1172+ setupQtSignalListener();
1173+
1174+ struct sigaction sa;
1175+
1176+ sa.sa_handler = ApplicationSignalToQtBridge::signalHandler;
1177+ sigemptyset (&sa.sa_mask);
1178+ sa.sa_flags = 0;
1179+ sa.sa_flags |= SA_RESTART;
1180+
1181+ return sigaction (type, &sa, 0) > 0;
1182+}
1183+
1184+void ApplicationSignalToQtBridge::signalHandler(int type)
1185+{
1186+ if (0 != signalSocketPair[0])
1187+ {
1188+ size_t size =
1189+ ::write (signalSocketPair[0],
1190+ &type,
1191+ sizeof(type));
1192+ Q_UNUSED(size);
1193+ }
1194+}
1195+
1196+void ApplicationSignalToQtBridge::handleSignal(int socket)
1197+{
1198+ Q_D(ApplicationSignalToQtBridge);
1199+
1200+ Q_UNUSED(socket);
1201+
1202+ d->_signalSocketNotifier->setEnabled(false);
1203+
1204+ int type = 0;
1205+ size_t size =
1206+ ::read (signalSocketPair[1], &type, sizeof(type));
1207+ Q_UNUSED(size);
1208+
1209+ Q_EMIT onSignalRaised(type);
1210+
1211+ d->_signalSocketNotifier->setEnabled(true);
1212+}
1213+
1214+
1215+#include "application-signal-to-qt-bridge.moc"
1216+
1217
1218=== added file 'src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.h'
1219--- src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.h 1970-01-01 00:00:00 +0000
1220+++ src/Ubuntu/UnityWebApps/plugin/application-signal-to-qt-bridge.h 2014-03-26 15:53:02 +0000
1221@@ -0,0 +1,62 @@
1222+/*
1223+ * Copyright 2014 Canonical Ltd.
1224+ *
1225+ * This file is part of unity-webapps-qml.
1226+ *
1227+ * unity-webapps-qml is free software; you can redistribute it and/or modify
1228+ * it under the terms of the GNU General Public License as published by
1229+ * the Free Software Foundation; version 3.
1230+ *
1231+ * unity-webapps-qml is distributed in the hope that it will be useful,
1232+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1233+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1234+ * GNU General Public License for more details.
1235+ *
1236+ * You should have received a copy of the GNU General Public License
1237+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1238+ */
1239+
1240+#ifndef APPLICATIONSIGNALTOQTBRIDGE_H
1241+#define APPLICATIONSIGNALTOQTBRIDGE_H
1242+
1243+#include <QObject>
1244+
1245+
1246+class ApplicationSignalToQtBridgePrivate;
1247+
1248+class ApplicationSignalToQtBridge : public QObject
1249+{
1250+ Q_OBJECT
1251+
1252+public:
1253+
1254+ explicit ApplicationSignalToQtBridge (QObject *parent = 0);
1255+ ~ApplicationSignalToQtBridge ();
1256+
1257+ bool addSignalHandlerFor(int type);
1258+
1259+
1260+public Q_SLOTS:
1261+
1262+ void handleSignal(int type);
1263+
1264+
1265+Q_SIGNALS:
1266+
1267+ void onSignalRaised(int type);
1268+
1269+
1270+private:
1271+
1272+ void setupQtSignalListener();
1273+
1274+ // Unix signal handlers.
1275+ static void signalHandler(int type);
1276+
1277+ static int signalSocketPair[2];
1278+
1279+ ApplicationSignalToQtBridgePrivate *d_ptr;
1280+ Q_DECLARE_PRIVATE(ApplicationSignalToQtBridge)
1281+};
1282+
1283+#endif // APPLICATIONSIGNALTOQTBRIDGE_H
1284
1285=== modified file 'src/Ubuntu/UnityWebApps/plugin/plugin.pro'
1286--- src/Ubuntu/UnityWebApps/plugin/plugin.pro 2014-01-17 15:51:27 +0000
1287+++ src/Ubuntu/UnityWebApps/plugin/plugin.pro 2014-03-26 15:53:02 +0000
1288@@ -40,7 +40,9 @@
1289 unity-webapps-desktop-infos.cpp \
1290 unity-webapps-icon-utils.cpp \
1291 callback.cpp \
1292- abstract-item-model-adaptor.cpp
1293+ abstract-item-model-adaptor.cpp \
1294+ application-api.cpp \
1295+ application-signal-to-qt-bridge.cpp
1296
1297 HEADERS += \
1298 qml-plugin.h \
1299@@ -57,7 +59,9 @@
1300 unity-webapps-desktop-infos.h \
1301 unity-webapps-icon-utils.h \
1302 callback.h \
1303- abstract-item-model-adaptor.h
1304+ abstract-item-model-adaptor.h \
1305+ application-api.h \
1306+ application-signal-to-qt-bridge.h
1307
1308 DEFINES += \
1309 API_URI=\\\"$${API_URI}\\\"
1310
1311=== modified file 'src/Ubuntu/UnityWebApps/plugin/qml-plugin.cpp'
1312--- src/Ubuntu/UnityWebApps/plugin/qml-plugin.cpp 2014-01-17 15:51:27 +0000
1313+++ src/Ubuntu/UnityWebApps/plugin/qml-plugin.cpp 2014-03-26 15:53:02 +0000
1314@@ -17,6 +17,10 @@
1315 */
1316
1317 #include "qml-plugin.h"
1318+
1319+#include <QtQml/QQmlEngine>
1320+#include <QtQml/QQmlContext>
1321+
1322 #include "unity-webapps-api.h"
1323 #include "unity-webapps-api-notifications.h"
1324 #include "unity-webapps-api-messaging-menu.h"
1325@@ -26,11 +30,19 @@
1326 #include "unity-webapps-app-model.h"
1327 #include "unity-webapps-app-infos.h"
1328
1329+#include "application-api.h"
1330 #include "abstract-item-model-adaptor.h"
1331 #include "callback.h"
1332
1333 #include <qqml.h>
1334
1335+static QObject *createApplicationApi(QQmlEngine *engine, QJSEngine *scriptEngine)
1336+{
1337+ Q_UNUSED(engine);
1338+ Q_UNUSED(scriptEngine);
1339+
1340+ return new ApplicationApi();
1341+}
1342
1343 void WebappsQmlPlugin::registerTypes(const char *uri)
1344 {
1345@@ -49,5 +61,8 @@
1346
1347 // TODO bump version
1348 qmlRegisterType<AbstractItemModelAdaptor> (uri, 0, 1, "AbstractItemModelAdaptor");
1349+
1350+ //
1351+ qmlRegisterSingletonType<ApplicationApi>(uri, 0, 1, "ApplicationApi", createApplicationApi);
1352 }
1353
1354
1355=== modified file 'src/Ubuntu/UnityWebApps/unity-webapps-api.js.in'
1356--- src/Ubuntu/UnityWebApps/unity-webapps-api.js.in 2014-03-18 17:26:34 +0000
1357+++ src/Ubuntu/UnityWebApps/unity-webapps-api.js.in 2014-03-26 15:53:02 +0000
1358@@ -33,6 +33,7 @@
1359 //@include ./bindings/alarm-api/client/alarm-api.js
1360 //@include ./bindings/content-hub/client/content-hub.js
1361 //@include ./bindings/online-accounts/client/online-accounts.js
1362+ //@include ./bindings/runtime-api/client/runtime-api.js
1363 //@include ./common/js/unity-backend-messaging-proxy.js
1364 //@include ./common/js/unity-binding-proxy.js
1365 //@include ./common/js/unity-binding-bridge.js
1366@@ -280,6 +281,7 @@
1367 OnlineAccounts: createOnlineAccountsApi(backend),
1368 AlarmApi: createAlarmApi(backend),
1369 ContentHub: createContentHubApi(backend),
1370+ RuntimeApi: createRuntimeApi(backend),
1371 };
1372
1373 return api;
1374
1375=== modified file 'tests/unit/test_plugin/tst_plugin.cpp'
1376--- tests/unit/test_plugin/tst_plugin.cpp 2014-01-21 19:58:04 +0000
1377+++ tests/unit/test_plugin/tst_plugin.cpp 2014-03-26 15:53:02 +0000
1378@@ -25,6 +25,8 @@
1379 #include <QSignalSpy>
1380 #include <QJsonDocument>
1381 #include <QVariantMap>
1382+#include <QProcess>
1383+#include <QProcessEnvironment>
1384
1385 #include "plugin/unity-webapps-api.h"
1386 #include "plugin/abstract-item-model-adaptor.h"
1387@@ -172,5 +174,22 @@
1388 delete model;
1389 }
1390
1391+void PluginTest::testApplicationSignalHandler()
1392+{
1393+ QProcess testApp;
1394+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
1395+
1396+ env.insert("APP_ID", "application-api");
1397+
1398+ testApp.setProcessEnvironment(env);
1399+ testApp.start("qmlscene -I ../../../src ../../../examples/api-bindings/application-api/main.qml");
1400+ testApp.waitForStarted();
1401+
1402+ testApp.terminate();
1403+ testApp.waitForFinished();
1404+
1405+ QVERIFY(true);
1406+}
1407+
1408 #include "tst_plugin.moc"
1409
1410
1411=== modified file 'tests/unit/test_plugin/tst_plugin.h'
1412--- tests/unit/test_plugin/tst_plugin.h 2014-01-17 15:51:27 +0000
1413+++ tests/unit/test_plugin/tst_plugin.h 2014-03-26 15:53:02 +0000
1414@@ -35,6 +35,7 @@
1415 void testLoadPlugin();
1416 void testInit();
1417 void testAbstractItemModelAdaptor();
1418+ void testApplicationSignalHandler();
1419 };
1420
1421 #endif // TST_PLUGIN_H

Subscribers

People subscribed via source and target branches

to all changes: