Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Updating phone contacts from the Web

Open web apps can benefit from integrating with the address books of devices they are installed on, especially on mobile devices in which contacts can be combined usefully with other data such as geolocation for personalised user experiences. This article explains how to build a real-world Firefox OS app that makes use of the API involved in managing phone contacts: the Contacts API.

Contacts API

The Contacts API is part of the WebAPI, and provides a simple interface to manage contacts stored in a device's system address book.

Typical use cases for the Contacts API includes:

  • Address book apps
  • Contact exchange apps
  • Social network apps
  • Instant messaging apps

The current Contacts API is not on a current W3C standards track, but it is supported on the Firefox OS platform, in privileged and certified apps. The previous W3C effort to create a Contacts API was developed on top of a Web-intents approach, but the working draft has been shelved. In the future, the Contacts API should be standardized and adopted across a wider range of platforms.

Demo: Geo-contacts

Geo-contacts is a geolocation app for Firefox OS that shows your contacts on a map.

Geo-contacts was built upon Geo, a web application that provides basic geolocation features. New features have been added in order to show how to use the Contacts API. Geo-contacts can:

  • Access the address book to show all the device's contacts on a map.
  • Find a specific contact in the address book and show it on a map.
  • Add an address to a contact if it is not already included in the address book.

You can try this demo by finding the source code on GitHub and running the app in the Firefox OS Simulator.

Note: This article focuses on contact management: if you are interested in how the geolocation part works, or on the map service used, please refer to our Plotting yourself on the map article.

Manifest

The Contacts API works only on Firefox OS privileged or certified apps. If you don’t know what it means, you can read our Firefox OS quickstart tutorial.

Geo-contacts contains an app manifest defining (amongst other things) that the app is privileged and has permission contacts with access readwrite, meaning that the application can add, read, or modify contacts from the address book on the device and read contacts from the SIM.

{
  "name": "Geo",
  "description": "Geolocation web application",
  "type": "privileged",
  "launch_path": "/index.html",
  "permissions": {
    "geolocation": {
      "description": "Required for getting current position"
    },
    "contacts": {
      "description": "Required for plotting contacts on the map",
      "access": "readwrite"
    }
  },
  "developer": {
    "name": "Francesco Iovine",
    "url": "https://www.francesco.iovine.name"
  },
  "default_locale": "en"
}

Initialising the app and feature detection

ContactManager is the Javascript Object that uses the Contacts API through window.navigator.mozContacts and isContactsApiSupported is the method that checks whether mozContacts is defined in window.navigator:

    /*
     * isContactsApiSupported
     * Checks whether the Contacts API is supported
     * @returns {Boolean}
     */
    isContactsApiSupported: function() {

        if (window.navigator.mozContacts) {
            return true;
        }

        return false;
    }

Showing all your contacts on the map

When the "show all contacts" button is clicked, the SearchBox's showAllContacts() method is invoked, which accesses the address book and shows all the device's contacts on the map.

SearchBox.showAllContacts calls the SearchBox.search() method for each contact retrieved from the address book. The SearchBox.search method shows the contact on the map. If you are curious about how the search feature work, please refer to this MDN information on performing searches. In Geo-contacts the search feature has been extended to provide the ability to add an address to the address book, as explained later on (see Adding a street address to a contact).

    /*
     * showAllContacts
     * Show all contacts on the map
     */
    showAllContacts: function() {
        console.log('SearchBox.showAllContacts()');

        var self = this;

        ContactManager.getAllContacts(function(contact) { // success callback
            if (contact.adr && contact.adr.length > 0) { // if contact has an address
                self.search(contact);
            }
        });
    },

ContactManager.getAllContacts uses the mozContacts.getAll function to retrieve all contacts from the address book. The contact passed as a parameter in the success callback MozContact, that provides, among other attributes, a name, a photo and an adr: to understand the demo code it's important to know that the name is an array of type String, the photo is a an array of type Blob object (each blob represents a picture), and mozContact.adr returns an Array of AddressField objects.

    /*
     * getAllContacts
     * Retrieves all contacts from the address book
     * @param {Function} successCallback
     * @param {Function} errorCallback
     */
    getAllContacts: function(successCallback, errorCallback) {
        console.log('ContactManager.getAllContacts()');

        if (this.isContactsApiSupported() === false) {
            errorCallback();
            return;
        }

        var allContacts = window.navigator.mozContacts.getAll({
            // no options
        });

        allContacts.onsuccess = function(event) {
            if (this.result) {
                successCallback(this.result);
                this.continue();
            }
        };

        allContacts.onerror = function() {
            console.log('getAllContacts error');
            errorCallback();
        };
    },

Finding and showing a contact on the map

Geo-contacts can find a contact in the address book and show it on a map. The following screenshots show how the search feature works.

When the “Show contact” button is clicked the SearchBox.searchContact() method is called, which looks for the phone contact specified in the "Contact name" input and shows the search result on the map.

    /*
     * searchContact
     * Submit the query to the address book and show the search results on the map
     * @param {String} query
     */
    searchContact: function(query) {
        console.log('SearchBox.searchContact(query)');
        console.log(query);

        var self = this;

        /* Perform the search if a query is specified */
        if (query) {
            ContactManager.findContact({
                filterBy: ['name'],
                filterValue: query,
                filterOp: 'contains'
            }, function(contactsFound) { // success callback
                if (contactsFound.length > 0) {
                    var contact = contactsFound[0];

                    self.contactSearchInput.value = contact.name[0];

                    if (contact.adr && contact.adr.length > 0) { // if contact has an address
                        self.search(contact);
                    }
                    else {
                        alert('No address specified for ' + contact.name[0] + ". Insert a address and click Search to add it to the contact");
                    }
                }
                else {
                    alert(query + ' not found in address book');
                }
            }, function() { // error callback

            });
        }
        else {
            alert("Please insert a value");
        }
    },

ContactManager.findContact() uses the mozContacts.find function to find a contact in the address book:

    /*
     * findContact
     * Finds a contact in the address book
     * @param {Object} filter
     * @param {Function} successCallback
     * @param {Function} errorCallback
     */
    findContact: function(filter, successCallback, errorCallback) {
        console.log('ContactManager.findContact(filter)');
        console.log(filter);

        if (this.isContactsApiSupported() === false) {
            errorCallback();
            return;
        }

        var request = window.navigator.mozContacts.find(filter);

        request.onsuccess = function() {
            console.log(this.result.length + ' contacts found.');
            successCallback(this.result);
        };

        request.onerror = function() {
            console.log('findContact error');
            errorCallback();
        };
    },

When displaying the marker on the map, ContactManager.contactAddressToString is called to display the description provided inside a marker popup in the appropriate place.

markerDescription = contact.name[0] + '<br/>' + ContactManager.contactAddressToString(contact);

In this case the popup shows only the name and the address of the contact, but it can also provide a link to their phone number (using the tel: protocol handler to allow the contact to be called by activating the link).

ContactManager.contactAddressToString converts the mozContact.adr attribute into a printable string:

    /*
     * contactAddressToString
     * Makes a contact address printable as a string
     * @param {type} contact
     * @returns {String}˙
     */
    contactAddressToString: function(contact) {
        console.log('ContactManager.contactAddressToString(contact)');
        console.log(contact);

        /* Build a query search string */
        var contactAddress = contact.adr[0];
        var addressString = '';

        console.log(contactAddress);

        addressString += contactAddress.streetAddress ? (contactAddress.streetAddress + ' ') : '';
        addressString += contactAddress.locality ? (contactAddress.locality + ' ') : '';
        addressString += contactAddress.region ? (contactAddress.region + ' ') : '';
        addressString += contactAddress.postalCode ? (contactAddress.postalCode + ' ') : '';
        addressString += contactAddress.countryName ? (contactAddress.countryName + ' ') : '';

        return addressString;
    },

Adding a street address to a contact

Geo-contacts can add an address to a contact if it is not already provided by the address book. This is done using the ContactManager.addAddressToContact method — it uses the mozContacts.save function to add address info to a contact:

    /*
     * addAddressToContact
     * Adds address info to a contact
     * @param {String} address
     * @param {mozContact} contact
     * @param {Function} successCallback
     * @param {Function} errorCallback
     */
    addAddressToContact: function(address, contact, successCallback, errorCallback) {
        console.log('ContactManager.addAddressToContact(address, contact, successCallBack, errorCallback)');
        console.log(address);
        console.log(contact);

        if (this.isContactsApiSupported() === false) {
            return;
        }

        contact.adr = [{
                streetAddress: address
            }];

        var saving = window.navigator.mozContacts.save(contact);

        saving.onsuccess = function() {
            console.log('the address was inserted correctly');
            successCallback();
        };

        saving.onerror = function(error) {
            console.log("error in inserting address");
            console.log(error);
            errorCallback();
        };
    },

The mozContacts.save function is used both to update and add a contact, as explained in the next section.

Adding contacts

When the application starts, some contacts are added by default for demo purposes. These contacts contain a name, a nickname, a photo and an address. In order to get a blob from a JPEG an Ajax call is used: on success the ContactManager.addContact method is called:

    JS.getBlobFromImagePath("img/franciov.jpeg", function(blob) { // when the images is retrieved correctly
        ContactManager.addContact({
            name: ["Francesco Iovine"],
            givenName: ["Francesco"],
            familyName: ["Iovine"],
            nickname: ["franciov"],
            photo: [blob],
            adr: [{
                    locality: "Rome",
                    countryName: "Italy"
                }]
        },
        function() { // on success
            console.log('Contact added successfully');
        }, function() { // on error
            console.log('Error adding contact to the address book');
        });
    });

JS.getBlobFromImagePath implements the Ajax call as follows:

    getBlobFromImagePath: function(imagePath, successCallback, errorCallback) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", imagePath, true);
        xhr.responseType = "arraybuffer";

        xhr.onload = function(e) {
            var arrayBufferView = new Uint8Array(this.response);
            var blob = new Blob([arrayBufferView], {type: "image/jpeg"});
            
            successCallback(blob);
        };

        xhr.send();
    }

Once the photo has been converted to a Blob object, the ContactManager.addContact method is called. It uses the mozContacts.save function to add a contact to the address book if it doesn’t already exist:

    /*
     * addContact
     * Adds a contact to the address book
     * @param {mozContact} contact
     * @param {Function} successCallBack
     * @param {Function} errorCallback
     */
    addContact: function(contact, successCallBack, errorCallback) {
        console.log('ContactManager.addContact(contact, successCallBack, errorCallback)');
        console.log(contact);

        if (this.isContactsApiSupported() === false) {
            errorCallback();
            return;
        }

        var self = this;

        ContactManager.findContact({
            filterBy: ['name'],
            filterValue: contact.name,
            filterOp: 'equals'

        }, function(contactsFound) { // success callback

            if (contactsFound.length === 0) {

                /* Add contact */
                var saving = window.navigator.mozContacts.save(new mozContact(contact));

                saving.onsuccess = function() {
                    console.log('new contact saved');
                    successCallBack();
                };

                saving.onerror = function(error) {
                    console.log(error);
                    errorCallback();
                };
            }
            else {
                console.log(contact.name + ' already exists');
            }

        }, function() { // error callback
            errorCallback();
            return;
        });
    }

Here is how the default contacts appear in the Firefox OS address book:


 

Keep in touch with mozContacts! :-)
 

Document Tags and Contributors

 Contributors to this page: chrisdavidmills, franciov
 Last updated by: chrisdavidmills,