On Github colinfrei / FirefoxOsAppDayZurichTalk
window.ondevicelight = function (event) { console.log("Ambient light: " + event.value + " lux"); };W3C Specification Ambient Light is a w3c working draft. It adds an event on the window object, which gets called with the lux value the ambient light sensor measured. This is really straightforward, but allows cool things like allowing you to change the style and the contrast of your site/app based on the ambient light.
var battery = navigator.battery; /* battery.level: Battery level between 0 and 1 battery.charging: true/false battery.chargingTime: Time left to fully charge, in seconds battery.dischargingTime: Time until fully discharged, in seconds */ console.log("Battery level: " + battery.level * 100 + "%"); navigator.battery.addEventListener( "levelchange", function() { // Update display with current battery level } );W3C Specification The Battery Status API is a W3C Editors Draft. It allows you to get information about the battery by using the navigator.battery object.
navigator.vibrate(1000); navigator.vibrate([200, 100, 200]);W3C Specification This one used to be called WebVibrator, that was changed to Vibration API after all the bad jokes had been made too many times. The Vibration API is simple as well, it allows you to call a function on the navigator object and either pass it a length it should vibrate (in milliseconds) or an array, where it'll switch between vibrating and not vibrating. So in the bottom example, it'd vibrate for 0.2 seconds, pause for 0.1 seconds and vibrate again for 0.2 seconds.
// Setup Push Notifications var reg = navigator.push.register(); reg.onsuccess = function(e) { var endpointUrl = e.target.result; // save endpointUrl to app server } navigator.mozSetMessageHandler('push', function(m) { var version = m.version; // do something based on version }); //Unregister navigator.push.unregister(endpointUrl);SimplePush on MDN Push notification require a bit more code, since you need a backend to it, but they're still fairly easy. You simplyregister a pushnotification, and the browser will return an endpointUrl, which is on a third-party server. You send that URL to your backend server, and whenever you want to send a push notification to the device, you can PUT to that endpointURL. That will cause the notification to be pushed to the device, which will broadcast a message, and that message can cause other things to happen, like you notifying the user. We'll see how messages are dealt with in more detail later on.
var notification = navigator.mozNotification; notification.createNotification( "Title", "Notification Text", iconUrl );
var appInstall = navigator.mozApps.install(manifestUrl); appInstall.onsuccess = function(data) { // App installed! }; appInstall.onerror = function() { console.log("Install failed: " + appInstall.error.name); };
var payment = navigator.mozPay(JsonWebToken); payment.onsuccess = function() { // ... // Profit! }Payments have a bunch of parts, having to do with authentication, identification, payment processing and so on. It's all specificed in a set of specifications under the name PaySwarm. I haven't played around with it much, but my understanding is that you wrap all the payment information into a so called Web Token, and the payment itself is simply a call to mozPay(), passing in that token and returning the result.
var share = new MozActivity({ name: "share", data: { type: "image/*", number: 1, blobs: [new Blob($('#imgToShare')[0], {type: "text/png"})] } }); share.onsuccess = function() { // Do stuff }; share.onerror = function () { // handle error };Let's take a closer look at the first scenario first. Basically all we have to do is instantiate a new object, and tell it what kind of activity we want to execute. As soon as that object is instantiated the user will see the interface to choose what he wants to do.
"pick": { "href": "./pick.html", "disposition": "inline", "filters": { "type": ["image/*","image/jpeg","image/png"] }, "returnValue": true }The alternative, if our app wants to offer functionality, needs two steps - we need to register the activity and handle it. Registering the app happens in the app manifest, where we add Objects that configure it.pick is the name of the activity in this case, and additionally we define the href, which is the page that should be opened when the activity is called. The disposition has two options, either inline or window returnValue is a boolean, stating if the call will return a value or not. Finally, filters specify what image types this activity should be offered for. This can be easy like in this example, but can also have things like ranges with a minimum and maximum, a pattern, and more.
navigator.mozSetMessageHandler('activity', function(request) { var options = request.source; if ("pick" === options.name) { // Do something with the image } });To handle the activity, what we need to do is Things to note: Activity pages have to abide to same origin policy as well Support for dynamically registering activities is coming but not ready yet
{ "name": "Cool App", "description": "This app lets you do really awesome stuff" }This is a very basic manifest, and includes the two required fields, a name for your app, and a short text describing what your app does.
{ "name": "Cool App", "description": "This app lets you do really awesome stuff", "icons": { "60": "/img/icon-60.png", "128": "/img/icon-128.png" }, "fullscreen": true, "orientations": ["portrait","landscape-secondary"], "chrome": { "navigation": true}, "developer": { "name": "Colin Frei", "url": "http://colinfrei.com" } }Firefox OS App Icon Guidelines Let's expand on that a bit: There's some more not really interesting data in here, including data about you, as well as an icon. You can add a bunch of different icons in different sizes if you want, and for Firefox OS apps there are some guidelines to what icons should look like. If you want to submit your app to the marketplace you'll need a 128x128 icon There's also a fullscreen flag, which is specific to FirefoxOS, and allows you to say if the app should be launched in fullscreen mode or not. Firefox Marketplace requires the developer name to be included.
"chrome": { "navigation": true}The 'chrome' attribute allows you to have a navigation bar appear automatically. Don't use this. Whenever possible, build a back button into your app directly.
{ "name": "Cool App", "description": "This app lets you do really awesome stuff", "default_locale": "en", "locales": { "de": { "description": "Beschreibung der App" } } }We can add translations for pretty much all this metadata. We specify the default_locale property, saying all this stuff is English, and then have a locales object, with an object for each language, where we can overwrite the fields above. This does not have anything to do with the app's language itself, only the metadata in this manifest file. You can overwrite almost all the fields in it though, with very few exceptions.
{ "name": "Cool App", "description": "This app lets you do really awesome stuff", "activities": { "pick": { "href": "./pick.html", "disposition": "inline", "filters": { "type": ["image/*","image/jpeg","image/png"] }, "returnValue": true } } }We talked about web activities previously, the configuration for these would go into the manifest file in the activities object.
{ "name": "Cool App", "description": "This app lets you do really awesome stuff", "messages": [ { "telephony-new-call": "/dialer/index.html#keyboard-view" }, { "notification": "/dialer/index.html#keyboard-view" }, { "alarm": "/facebook/fb_sync.html" } ] }I'm not the expert on this, but in general messages allow you to catch system events and react to them. The whole thing is a two step process - you register for the event here in the manifest and also have to register the actual callback function in the JavaScript code. This can be useful combined with the Alarm API as a 'local notification' system.
{ "name": "Cool App", "description": "This app lets you do really awesome stuff", "installs_allowed_from": [ "https://marketplace.firefox.com", "http://colinfrei.com" ] }
{ "name": "App Name", "description": "A description of your app", "type": "privileged", "permissions": { "desktop-notification": { "description": "Required to notify about stuff" } } }There are two interesting things here, the type, and the permissions. They're linked, but lets look at them one at a time.
Types are important - they basically decide what APIs an app has access to. There are three permission types for Firefox OS: web, privileged and certified. Web apps have the leat permission, but can be distributed in the easiest way. Privileged apps have access to some APIs that can be "misused", so the code of these apps is reviewed to make sure it doesn't do anything stupid. One example is the Contacts API, which allows you to read the content of your phonebook. Certified apps have access to all the APIs, so these are extremely limited. This includes things like being allowed to register to handle phone calls. These apps are reviewed for security, and digitally signed. Most apps that are certified are probably preloaded apps.
In addition to permission types there's what I call app types. These differentiate between apps that are hosted on the web and use appcache and stuff to work offline, and apps that are packaged as a zip file and installed directly on the phone. The important distinction here is between privileged as a permission type and packaged as an app type. I spent about half a day trying to figure out why I didn't have permission to use an API, because I thought packaged automatically meant privileged, which it doesn't.
{ "name": "App Name", "description": "A description of your app", "type": "privileged", "permissions": { "contacts": { "description": "Required to match users", "access": "readonly" } } }So again, the format for permissions is really easy, just add the permission and a description of what it's used for and you're done. Very few permissions require an additional level, this can be set using the access property. This can be readonly, readwrite, readcreate or createonly
{ "name": "App Name", "description": "A description of your app", "version": "2.0" }Updating an app happens differently depending on what kind of app we're dealing with. Web Apps can be updated like normal, using the AppCache updating mechanisms. Packaged apps can be updated by rebuilding the package and updating the version number in the manifest file.
Content Type:application/x-web-app-manifest+json
Same Origin
Absolute Paths
So now we've got our manifest put together! Anything else we need to know? Well one thing is that manifests need to be served with a content-type of 'application/x-web-app-manifest+json', so that they're recognized and parsed. Github does this automatically for files with a .webapp ending, you can output the content type easily on your own server as well. Then, the manifest file has to be hosted at the same origin as your app. Seems obvious too, but is probably worth mentioning. Also, paths to icons and so on should be absolute. There are manifest validators available, and there's also a service with an API that allows you to validate manifests if you happen to need something like that.Requirements: