How difficult is it?

It is easy to develop an add-on. You only need:

  • JSON Manifest
  • CSS or Javascript
  • Set of icons
  • NO UPDATE.WEBAPP MANIFEST

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

What I tried to do?

Firefox OS has an NFC API.

But it doesn't have a background service to interact with other NFC devices.

This means that you need to open a specific Webapp to use NFC.

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

Implementation

  1. We are going to add an Add-on to the System Webapp
  2. This Add-on will use the NFC API.

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

Use cases

  • Access control
  • Mobile payments
  • Loyalty programs
  • In-store marketing
  • Location based services
  • Targeted marketing
  • Information Exchange
  • Social networks...

@CodingFree

Lets start!

Firstly, we have to define our manifest.json file:

It will be a Chrome manifest, quite different from the manifests used for Firefox OS Webapps.

Manifest File Format: Field summary.

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

Fields

Supported fields:

  • name
  • version
  • description
  • content_scripts <== Important!
  • permissions <== ignore it
  • web_accessible_resources
  • background
  • browser_action
  • default_locale

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

Not supported:

  • browser_action
  • automation
  • commands
  • event_rules
  • plugins
  • sandbox
  • storage
  • key
  • more...

@CodingFree

{
    "manifest_version": 2, // WebApp Manifest = 1 (deprecated), WebExtension = 2
    "version": "1.0", //My own version 
    "name": "NFC Sleep",
    "description": "Turn off your WiFi and volume using a NFC Tag.",
    "author": "Adrian Crespo",
    "role": "addon", // Mandatory.
    "type": "certified", // No effect! It's deliberately wrong,I wanted to point out that is has no effect!
    "content_scripts": [
        {
            "matches": [
                "app://system.gaiamobile.org/index.html" // My target will be the System App.
            ],
            "js": [
                "scripts/nfc.js" // My script
            ]
        }
    ],
    "icons": {
        "1200": "/style/icons/1200.png", // Icons!

        "128": "/style/icons/128.png", // Required by the Marketplace
        "60": "/style/icons/60.png", // Standard size
        "32": "/style/icons/32.png" // Why not?
    }
}

Create the Zip archive

Do you remember...?    

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

  //... code ...
        "content_scripts": [{
          "css": [],
              "js": ["scripts/nfc.js"]
      }],
  //... code ...

We have to zip the same

tree structure, or it won't

work!

(it's the same for the icons)

Javascript

Our target will be the System App, because we specified it in the "matches" field.

It supports regular expressions:

 

// More code
  "content_scripts": [
    {
        "matches": [
            "app://system.gaiamobile.org/index.html"
        ],
        "js": [
            "scripts/nfc.js"
        ]
    }
  ],

// More code
["http://www.mozilla.com/*"].

For any URL it would be like this:

["<all_urls>"]

More examples: match patterns.

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

Considerations

First, notice that the wrapping function statement starts with (function... as opposed to just function....

(function foo(){ .. }) as an expression means the identifier foo is found only in the scope where the ... indicates, not in the outer scope (and this is safer and cleaner).

Extracted from: You Don't Know Javascript!

(because it is really awesome)

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

Considerations

We are going to use the Notifications API (standard):

To create the notification:

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

var notification = new window.Notification( ... )
  • Notification.title
  • Notification.body
  • Notification.icon
  • Notification.permission
  • Notification.requestPermission()

@CodingFree

Considerations

And we are going to use the NFC API:

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

if(window.navigator.mozNfc)...
nfc.ontagfound(...);
ndefObject.ndefRecords(...);
ndefRecords.length;
ndefObject.ndefRecords;
ndefRecords[i].payload.wrappedJSObject;
  • Firstly, test if mozNfc is supported.
  • If true, lets wait to find a NFC tag.
  • When found, get how many records are there and retrieve them.
  • And then, read the payload as a wrappedJSObject. <== ¿?¿?¿?

The payload will be the orders for the System App.

@CodingFree

Considerations

And we are going to use the NFC API:

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

We are using wrappedJSObject because it is a nasty fix :(

The XPCOM model didn't allow me to read the payload directly, so I used that to "see" the object hidden by the wrapper.

 

It is fixed, but... if you find it, you will know how to fix it :)

@CodingFree

Considerations

At last, we will use the Settings API to modify the volume of the mobile phone:

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

It's easy to use: you need to use a lock to change the settings (to avoid race conditions), and then use set() to change the values.

We could disable the WiFi: 'wifi.enabled': false

window.navigator.mozSettings;
settings.createLock();
result = lock.set({...});
result.onsuccess;
result.onerror;

@CodingFree

(function () {
    this._started = false;
    this.tag = null;
    start();

    function notify(titleid, body, bodyid, onClick) {
        console.log("A notification would be send: " + titleid);

        var canSend = false;


        if (Notification.permission === "granted") {
            // If it's okay let's create a notification
            canSend = true;
        }

        // Otherwise, we need to ask the user for permission
        if (Notification.permission !== 'denied') {
            Notification.requestPermission(function (permission) {
                // If the user accepts, let's create a notification
                if (permission === "granted") {
                    canSend = true;
                }
            });
        }

        if(canSend) {
            var notification = new window.Notification(titleid, {
                body: body,
                icon: '/style/icons/32.png'
            });
            notification.onclick = function () {
                notification.close();
                if (onClick) {
                    new MozActivity({
                        name: "view",
                        data: {
                            type: "url",
                            url: body
                        }
                    });
                }
            };
        }
    }

    function changeSettings(){
        var settings = window.navigator.mozSettings;
        var lock = settings.createLock();
        var result = lock.set({
                'wifi.enabled': false,
                'bluetooth.enabled': false,
                'powersave.enabled': true,
                'ril.data.enabled': false,
                'audio.volume.content': 0,
                'audio.volume.notification': 0

        });     

        result.onsuccess = function () {
          console.log("NFC-Sleep: The setting has been changed");
          notify("NFC-Sleep", "The setting has been changed");
        }

        result.onerror = function () {
          console.log("NFC-Sleep: An error occure, the setting remain unchanged");
          notify("NFC-Sleep", "An error occure, the setting remain unchanged");
        }
    }

    function manageNDEFRecords(ndefRecords) {
        console.log("NFC-Sleep: managing NDEF records.");
        var ndefLen = ndefRecords ? ndefRecords.length : 0;
        if(ndefLen != 0){
            var outputString = '';
            for (i = 0; i < ndefLen; i++) {
                payload = dumpUint8Array(ndefRecords[i].payload.wrappedJSObject);
            }
            console.log("NFC-Sleep: "+payload);
            if (payload.localeCompare("NFC-Pass1") === 0){
                changeSettings();
            }

        }
      }

    function handleTagFound(ndefObject){
        console.log("NFC-Sleep: Tag found");
        var ndefRecords = ndefObject.ndefRecords;
        var ndefLen = ndefRecords ? ndefRecords.length : 0;

         // if no NDEF Records are contained, bail out.
        if (!ndefLen) {
          console.log("NFC-Sleep: The tag is blank")
          return true;
        }

        console.log("NFC-Sleep: The tag is okay, lets read it");
        manageNDEFRecords(ndefObject.ndefRecords);
        return false;
    }

    function dumpUint8Array(array) {
      if (!array) {
        return 'null';
      }
      var str = '';
      var i;
      var arrayLen = array ? array.length : 0;
      for (i = 0; i < arrayLen; i++) {
        str += '' + hex2a(array[i].toString(16));
      }
      return str + '';
    }

    function hex2a(hexx) {
        var hex = hexx.toString();//force conversion
        var str = '';
        for (var i = 0; i < hex.length; i += 2)
            str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
        return str;
    }

    function stop() {
        if (!this._started) {
            throw 'Instance was never start()\'ed but stop() is called.';
        }
        this._started = false;
        window.removeEventListener('mozChromeEvent', this.handleEvent);
    }

    function start() {
        console.log("NFC Sleep Running LOL");
        if (this._started) {
            throw 'Instance should not be start()\'ed twice.';
        }
        var nfc = window.navigator.mozNfc;
        if (!nfc) {
            console.log('Go home NFC, you are not available. Please, disable this addon.');
            return;
        } else {
            console.log('NFC Sleep enabled.');
            this._started = true;
            nfc.ontagfound = handleTagFound.bind(this);
        }
    }

}());

Upload and publish it!

Do you remember the ZIP file?

Just go to this webpage and upload it:

https://marketplace.firefox.com/content/addon/submit/

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

It will be public as soon as it is reviewed by the Marketplace Reviewers Team.

@CodingFree

Thanks!

Thank you for attending the presenta

tion

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

Feel free to ask me any question:

@CodingFree

@CodingFree

Appendix

 Fork these slides: https://slides.com/firefoxos/fxos-nfc/

@CodingFree

Firefox OS Addons - NFC

By firefoxos

Firefox OS Addons - NFC

Developing Firefox OS Addons: implementing a background service for NFC.

  • 1,752