I had the privilege to attend Chrome Apps Accelerated MTV hackathon. The goal set out for my self was to create a bluetooth scanner via chrome.bluetooth api.
First I started off by reviewing the Chrome app developer guide. From a previous post, a patch for 7228 was needed at the time.
Second was too communicate with the js defined context which Chrome Apps execute in. Some setup ceremony is needed such as a manifest file and background window. Creating the manifest file was easy, mostly choosing permissions and adding the entry Javascript background entry.
{ | |
"manifest_version": 2, | |
"name": "Bluetooth sample", | |
"version": "1", | |
"minimum_chrome_version": "23", | |
"icons": {"128": "dart_icon.png"}, | |
"permissions": [ | |
"unlimitedStorage", | |
"storage", | |
"notifications", | |
"bluetooth" | |
], | |
"app": { | |
"background": { | |
"scripts": ["main.js"] | |
} | |
} | |
} |
For dart we will need to create the background javascript that loads our dart application.
/** | |
* Listens for the app launching then creates the window | |
* | |
* @see http://developer.chrome.com/trunk/apps/app.runtime.html | |
* @see http://developer.chrome.com/trunk/apps/app.window.html | |
*/ | |
chrome.app.runtime.onLaunched.addListener(function() { | |
chrome.app.window.create('bluetooth_example.html', | |
{id: 'bluetooth_example', width: 800, height: 800}); | |
}); |
I stumbled with the communications between dart generated javascript and js-interop for some time. The first part of the stumbling blocks was CSP (Content Security Policy) and how dart2js apps are loaded. Thanks to Vijay Menon and Renato Mangini, I was able to create successful call and callback from dart to javascript. This was non trivial since it required knowledge of dart.js and loading of dart_interop.js/js.dart.
The diff file below removes the reference to localStorage and replaces it with a map.
diff --git a/web/dart.js b/web/dart.js | |
index 1c8b333..d69e50e 100644 | |
--- a/web/dart.js | |
+++ b/web/dart.js | |
@@ -157,13 +157,20 @@ function ReceivePortSync() { | |
return result; | |
} | |
+window._localStorage = {}; | |
+ | |
window.registerPort = function(name, port) { | |
var stringified = JSON.stringify(serialize(port)); | |
var attrName = 'dart-port:' + name; | |
document.documentElement.setAttribute(attrName, stringified); | |
- // TODO(vsm): Phase out usage of localStorage. We're leaving it in | |
- // temporarily for backwards compatibility. | |
- window.localStorage[attrName] = stringified; | |
+ // TODO(vsm): Phase out usage of localStorage and delete the | |
+ // below. We're leaving it in temporarily for backwards | |
+ // compatibility. | |
+ try { | |
+ window._localStorage[attrName] = stringified; | |
+ } catch (e) { | |
+ // Swallow errors (e.g., Chrome apps disallow this access). | |
+ } | |
}; | |
window.lookupPort = function(name) { | |
@@ -172,7 +179,7 @@ function ReceivePortSync() { | |
// TODO(vsm): Phase out usage of localStorage. We're leaving it in | |
// temporarily for backwards compatibility. | |
if (!stringified) { | |
- stringified = window.localStorage[attrName]; | |
+ stringified = window._localStorage[attrName]; | |
} | |
return deserialize(JSON.parse(stringified)); | |
}; |
dart_interop.js and js.dart needed to be copied into the root folder of the project. Not sure exactly why this is, very possible that only dart_interop.js is needed since were compiling to js.
04:47:27-adam@Adams-MacBook-Air:~/dart/bluetooth_example/web | |
$ ls -l | |
total 1304 | |
-rw-r--r-- 1 adam staff 376 Dec 10 10:29 bluetooth_example.css | |
-rw-r--r-- 1 adam staff 1253 Dec 10 14:18 bluetooth_example.dart | |
-rw-r--r-- 1 adam staff 437928 Dec 10 14:19 bluetooth_example.dart.js | |
-rw-r--r-- 1 adam staff 6825 Dec 10 14:19 bluetooth_example.dart.js.deps | |
-rw-r--r-- 1 adam staff 102617 Dec 10 14:19 bluetooth_example.dart.js.map | |
-rw-r--r-- 1 adam staff 410 Dec 10 13:18 bluetooth_example.html | |
-rwxr-xr-x 1 adam staff 139 Dec 10 10:30 compile.sh | |
-rw-r--r-- 1 adam staff 8815 Dec 10 13:43 dart.js | |
-rw-r--r-- 1 adam staff 7294 Dec 10 10:31 dart_icon.png | |
-rw-r--r-- 1 adam staff 17105 Dec 10 11:29 dart_interop.js | |
-rw-r--r-- 1 adam staff 42233 Dec 10 11:29 js.dart | |
-rw-r--r-- 1 adam staff 364 Dec 10 10:32 main.js | |
-rw-r--r-- 1 adam staff 320 Dec 10 10:36 manifest.json | |
lrwxr-xr-x 1 adam staff 43 Dec 10 11:28 packages -> /Users/adam/dart/bluetooth_example/packages |
The first sanity check of being able to communicate with dart in a Chrome App was printing out the permissions from chrome.permissions.getAll
import 'dart:html'; | |
import 'package:js/js.dart' as js; | |
void main() { | |
js.scoped(() { | |
void permissionsCallback(var result) { | |
print("permissions = $result"); | |
for (int i = 0; i < result.permissions.length; i++) { | |
print(result.permissions[i]); | |
} | |
}; | |
var chrome = js.context.chrome; | |
print("chrome = ${chrome.runtime.id}"); | |
var permissionsCallbackHandler = new js.Callback.many(permissionsCallback); | |
js.context.permissionsCallbackHandler = permissionsCallbackHandler; | |
chrome.permissions.getAll(js.context.permissionsCallbackHandler); | |
}); | |
print("finished main"); | |
} |
Google searches lead me to the wrong api docs for accessing the bluetooth device. Managed to land on chrome.experimental.bluetooth from extensions api, which is different from the Chrome Apps chrome.* api. Bug was filed about how easy it was to land on the wrong api pages.
Now that I’ve got the right context too loaded, calling the chrome.bluetooth api produced errors with bluetooth support on MacOSX. doh! Bluetooth is currently only supported on ChromeOS and Windows. This was not mentioned in any of the docs and bug was filed on that.
1 2 |
|
At this point I was glad to be able to make calls on chrome.* api, in a following post I will go over a more complete sample from start to finish. Feel free to browse code and project structure bluetooth_example_chrome_app. Please note this is not a fully working sample and has bugs!
By the end of the hackathon I decided my best bet was to package something that did not rely on the chrome.* api so heavily. An ASCII camera capture app was created and demoed. The application accesses the client’s video input device and converts images capture to ascii images based on a ascii art formula. “videoCapture” permissions are required for accessing video input, this was set in the manifest file. The ASCII camera capture app code is available on the following branch chrome_app_example.