Overview

Firefox OS First Time User Experience – Import Contacts

When you power on a Firefox OS phone for the very first time, you get a first-run experience. You can configure the phone to set your language, time zone and privacy options; connect to your Wi-Fi and cellular data; and take a tour of the phone features. You can also import your contacts from the SIM card or from your accounts with Facebook, Gmail and Windows Live.

If you have a lot of contacts, you wouldn’t want to wait forever while your contacts are imported. To test the performance of the Contacts app with a high volume of contacts, I wrote an automated Python script to generate a file containing hundreds of virtual cards, complete with name, address, phone numbers and a mug shot. Writing this script took me down many rabbit holes along the way. I dived into the fascinating worlds of virtual business cards, United States census data, embedded databases and random image generators, all of which I am happy to share with you here. At the end of the journey, we’ll see the virtual cards imported into the Firefox OS phone and measure how long it takes to do under different conditions. I’ll also talk about how you can use the Python script for your own testing, manual or automated.

Contents

Virtual Business Cards

Formal introductions at business meetings almost invariably start with the exchange of small, white rectangular pieces of cardboard. Business cards at Mozilla are a lot snazzier than the usual black-on-white: they have rounded corners and are colored red on the reverse. Nevertheless, they are printed card stock. With the rising use of smartphones, many people prefer to exchange electronic business cards. The two most popular file formats for electronic business cards are vCard and hCard.

Version 2.1 of the vCard format is the most widely supported among email clients and contact apps on mobile operating systems. Firefox OS aims to support version 4.0 of the format.

Version 4.0 adds many new properties and parameters as compared to previous versions, and redefines how the values of many existing properties should be encoded. Since version 4.0 is relatively new, it is not as widely supported as version 2.1.

Here’s what a vCard looks like in version 2.1:

+-------------------------------------------------------------------------------+ BEGIN:VCARD VERSION:2.1 N:Adelson;Darin;;; FN:Darin Adelson; ORG:Ingram Micro TITLE:Manager TEL;WORK;VOICE:+1-536-592-7850 TEL;HOME;VOICE:+1-973-470-6705 PHOTO;ENCODING=BASE64;TYPE=PNG:iVBORw0K[...more base64 encoding of the image...] ADR;WORK:;;8752 Veterans Avenue;Trilla;IL;62469;USA ADR;HOME:;;4121 Bethel Loop;Gainesville;FL;32606;USA EMAIL;HOME:darin3567@example.com REV:20111014T140223Z END:VCARD +-------------------------------------------------------------------------------+

And here’s what a vCard looks like in version 4.0:

+-------------------------------------------------------------------------------+ BEGIN:VCARD VERSION:4.0 N:Ballestero;Jules;;; FN:Jules Ballestero; ORG:Aetna TITLE:Customer Service PHOTO:data:image/png;base64,eGYnSO8J[...more base64 encoding of the image...] TEL;TYPE=work,voice;VALUE=uri:tel:+1-528-102-7341 TEL;TYPE=work,voice;VALUE=url:tel:+1-865-147-6132 ADR;TYPE=work;LABEL="1765 Goodwin PlacenGermansville, PA 18053nUSA":;;1765 Goodwin Place;Germansville;PA;18053;USA ADR;TYPE=home;LABEL="3328 Crosby AvenuenEdgerton, MO 64444nUSA":;;3328 Crosby Avenue;Edgerton;MO;64444;USA EMAIL:jules623@example.com REV:20110423T231132Z END:VCARD +-------------------------------------------------------------------------------+

I began writing my Python script by defining a class that maps to a vCard in the vCard 2.1 file format. I chose vCard 2.1 over vCard 4.0 format because the former is widely supported. (I later went on to modify this script to support the vCard 4.0 format as well. You can find both the scripts in my Github project.)

#!/usr/bin/env python """ Generate a vCard in the vCard 2.1 file format """ __author__ = "Parul Mathur" import sqlite3 import os import sys import random from datetime import datetime, time, timedelta import StringIO # Construct a vCard in the vCard 2.1 file format # Reference: http://en.wikipedia.org/wiki/Vcard#vCard_2.1 class Vcard: version = "2.1" first_name = "" last_name = "" company = "" job_title = "" photo = "" tel = {"WORK": "", "HOME": ""} adr = {"WORK": Address(), "HOME": Address()} email = '' rev = '' def __str__(self): vcard = u"BEGIN:VCARDn" vcard += "VERSION:%sn" % self.version vcard += "N:{};{};;;n".format(self.last_name, self.first_name) vcard += "FN:{} {};n".format(self.first_name, self.last_name) vcard += "ORG:%sn" % self.company vcard += "TITLE:%sn" % self.job_title vcard += "TEL;WORK;VOICE:%sn" % self.tel['WORK'] vcard += "TEL;HOME;VOICE:%sn" % self.tel['HOME'] vcard += "PHOTO;ENCODING=BASE64;TYPE=PNG:%sn" % self.photo vcard += "ADR;WORK:;;%sn" % self.adr['WORK'] vcard += "ADR;HOME:;;%sn" % self.adr['HOME'] vcard += "EMAIL;HOME:%sn" % self.email vcard += "REV:%sn" % self.rev vcard += "END:VCARD" return vcard

To generate hundreds of electronic business cards, the script would need mock data like names, addresses, cities, states, and zip codes. The United States Census Bureau collects just this sort of data on a routine basis and provides it free for use on its website.

Census Data

UNIVAC I: The first electronic computer designed for civilian use was commissioned by the United States Census Bureau in the late 1940s. Courtesy U.S. Census Bureau.

The first demographic and economic survey of the United States took place in 1790 when the nation’s total population was just 3.9 million, about a tenth of what it is today. Only six questions were asked of each individual and the survey was done with pen and paper. A hundred years later, electric tabulating machines were put into use to compile census data. In the late 1940’s, the Census Bureau commissioned the first electronic computer designed for civilian use. Known as UNIVAC 1, the machine was able to tabulate 4,000 items per minute, double the amount that electro-mechanical tabulating machines could process. For its latest survey in 2010, the United States Census Bureau sent out questionnaires to 145 million addresses and used optical character scanners to process the returned questionnaires.

The United States Census Bureau publishes statistics from all its surveys on its website and even has an API for developers. For the purposes of my own script, I chose to download lists of first names and last names gathered in the 1990 census.

Since the United States Census Bureau no longer maintains a current set of zip codes, cities and states, I was forced to search elsewhere for it. From other websites, I also helped myself to lists of street names, Fortune 100 companies and job titles.

Now that I had all the raw data ready to create electronic business cards, I needed a structured way to store them.

Embedded Databases

Traditionally, databases have been separate from the applications that access them. Today, databases that are embedded into the same process as the client application have become popular. SQLite is a widely used, self-contained, cross-platform embedded database with a compact footprint. Many programming languages provide libraries that interface with it. SQLite is also a “zero-configuration” database engine – there is nothing to install, setup, configure, initialize, manage, or troubleshoot. In 2008, Mozilla released the Firefox 3 browser with SQLite as a place to store bookmarks and browsing history.

I decided to pump all the data I had into a SQLite database and manage it with SQLite Manager, a Firefox browser add-on. I converted all the data into CSV files and used SQLite Manager’s Import feature to create database tables.

(Left) SQLite Manager Import Wizard. (Right) Data imported into the tables.

To hook up this SQLite database to the Python script, I installed the sqlite3 python module. In the script, I created a Connection object that represents the database and then called the execute() method on the Cursor object to fetch data through SQL commands.

# Query from a sqlite3 database containing data # sourced from the United States Census Bureau class RandomVcard: def __init__(self, con): self.con = con def get(self): vcard = Vcard() cur = con.cursor() cur.execute("SELECT LastNames FROM LastNames ORDER BY RANDOM() LIMIT 1;"); vcard.last_name = cur.fetchone()[0] cur.execute("SELECT MaleFirstNames FROM MaleFirstNames ORDER BY RANDOM() LIMIT 1;"); vcard.first_name = cur.fetchone()[0] cur.execute("SELECT Company FROM Companies ORDER BY RANDOM() LIMIT 1;"); vcard.company = cur.fetchone()[0] cur.execute("SELECT JobTitle FROM JobTitles ORDER BY RANDOM() LIMIT 1;"); vcard.job_title = cur.fetchone()[0] for k, v in vcard.adr.iteritems(): vcard.adr[k].street_number = random.randint(10,9999) cur.execute("SELECT StreetName FROM StreetNames ORDER BY RANDOM() LIMIT 1;"); vcard.adr[k].street_name = cur.fetchone()[0] cur.execute("SELECT * FROM ZipCityState ORDER BY RANDOM() LIMIT 1;"); r = cur.fetchone() vcard.adr[k].city = r[1] vcard.adr[k].state = r[2] vcard.adr[k].zipcode = r[0] vcard.adr[k].country = "USA"; for k, v in vcard.tel.iteritems(): vcard.tel[k] = "+1-{}-{}-{}".format(random.randint(210,999), random.randint(100,999), random.randint(1000,9999)) vcard.email = "{}{}@example.com".format(vcard.first_name.lower(), random.randint(10,9999)) t = datetime.now() - timedelta(seconds=random.randint(1000000,99999999)) vcard.rev = t.strftime("%Y%m%dT%H%I%SZ") return vcard # Program execution starts here if __name__ == "__main__": #Connect to sqlite3 database where contact data is stored con = sqlite3.connect('contacts.db') #Generate random vCards con.close()

I’ve shared the SQLite database on Github. You can use it with any programming language that provides an API for SQLite.

Random Image Generator

The SQLite database took care of all my textual data, so I turned my attention to the photo attached to the electronic business card. Every major programming language offers an image manipulation library that allows you to generate random images on demand. I wrote a function in the Python script that would create an image of size 100×100 with broad, cheerful stripes. The randomizing part of the code would change the breadth and color of the stripes each time the function was called.

# Generate a random user profile photograph of size 100x100 pixels def get_photo(self, width, height): img = Image.new( 'RGBA', (width,height)) pixels = img.load() for i in range(img.size[0]): # for every pixel: for j in range(img.size[1]): red = random.randint(0,255) green = random.randint(0,255) blue = random.randint(0,255) pixels[j,i] = (red, green, blue) # set the colour # Randomize the background with fancy stripes block_x = random.randint(5,49) block_y = random.randint(10,90) temp_col = i for x in range(block_x): for y in range(((-1)*block_y),block_y): if temp_col - y >= 0: if temp_col - y >= img.size[1]: pixel_y = (temp_col - y) % img.size[1] else: pixel_y = temp_col - y if temp_col - y < img.size[1]: if temp_col - y < 0: pixel_y = random.randint(0, img.size[1]-1) else: pixel_y = temp_col - y if j + y < img.size[0]: if j + y < 0: pixel_x = random.randint(0, img.size[0]-1) else: pixel_x = j + y if j + y >= 0: if j + y >= img.size[0]: pixel_x = img.size[0] - 1 else: pixel_x = j + y pixels[pixel_x,pixel_y] = (red, green, blue) # set the colour j = j + 1 temp_col = temp_col + 1 output = StringIO.StringIO() img.save(output, "PNG") img_base64 = output.getvalue().encode("base64") output.close() img_base64 = img_base64.replace("n","") return img_base64

I wrote some more code to merge the colorful background with an icon that represents a user. This is the final result:

+ = (Left) Randomly generated image. (Center) User icon. (Right) Icon merged with background image.

# Merge the background with a user profile icon background = Image.open("user.png") background.convert('RGBA') img.paste(background, (0, 0), background) output = StringIO.StringIO() img.save(output, "PNG") img_base64 = output.getvalue().encode("base64") output.close() img_base64 = img_base64.replace("n","") return img_base64

Run this code over and over again to see how randomization produces a different image each time.

Randomization produces a different image each time

Using the vCard Generator script

I put the finishing touches to the Python script by reading an argument from the command line that stood for how many vCards the script should generate at a time.

#!/usr/bin/env python """ Generate a vCard in the vCard 2.1 file format """ __author__ = "Parul Mathur" import sqlite3 import os import sys import random from datetime import datetime, time, timedelta from PIL import Image, ImageDraw, ImageOps import base64 import StringIO # Construct a vCard in the vCard 2.1 file format # Query from a sqlite3 database # Generate a random user profile photograph of size 100x100 pixels # Read the command line parameters # argv[1]: Number of vCards to generate # Redirect output to a file with filename extension .vcf # Sample command line: # python vcard21_maker.py 10 > contacts.vcf if __name__ == "__main__": max_vcards = 1 if len(sys.argv) > 1: max_vcards = long(sys.argv[1]) #Connect to sqlite3 database where contact data is stored con = sqlite3.connect('contacts.db') rvc = RandomVcard(con); while max_vcards > 0: #Generate a randon vCard vcard = rvc.get(); #Write the vCard to the command line print vcard max_vcards = max_vcards - 1 con.close()

The script outputs on the command line, so you can redirect its output to a file that is ready to transfer to your phone. To run the Python script, pass it the number of vCards needed on the command line.

$ python vcard21_maker.py 10 > contacts.vcf

As run above, the output file would have 10 vCards one after another in a single file with no line spacing between successive vCards. Each contact’s information is about 12 KB in file size.

I’ve shared the Python script on Github, where you can also find a version that supports vCard 4.0. If you want to skip running the script, you can grab a .vcf file containing 250 contacts which will play well with import from SIM Cards. There’s also another .vcf file containing 500 contacts.

Importing vCards into your Firefox OS phone

You can import vCards into your Firefox OS phone through your Gmail account or your Outlook account.

Firefox OS Contacts app. (#1 from Left) Start screen when address book is empty. Click on the Settings button in the top right corner. (#2 from Left) Click on Gmail in the Settings screen. (#3 from Left) Wait for the Gmail login page to load. Enter your credentials and click on Login.(#4 from Left) Contacts app attempts to obtain your contacts list from your Gmail account.

Firefox OS Contacts app. (#1 from Left) Contacts app enumerates the contacts in your Gmail account. Click on Select All and then Import. (#2 from Left) Contacts app starts importing your contacts. (#3 from Left) Contacts successfully imported into the phone’s address book. (#4 from Left) An individual contact in the address book.

You can also import vCards into your Firefox OS phone by copying a .vcf file onto an Android phone’s SDCard. Use the Copy2SIM app to export the contacts to a SIM Card, after which you pop the SIM Card into your Firefox OS phone. Keep in mind that due to storage constraints, the SIM Card may choose to truncate each contact’s name to the first 16 characters and store only one phone number per contact. If the contact has multiple phone numbers in the .vcf file, the SIM Card may break it up into multiple entries. Finally, the SIM Card may store no more than 250 contacts total.

In the future, you may be able to import vCards into your Firefox OS phone through the SD Card. That feature is being tracked in Bugzilla IDs 849729 and 848553.

To volume test the import contacts feature, I first imported 250 contacts via SIM Card, WIFi and cellular network ‐ each time measuring the time it took to do so. Then I repeated the process with 500 contacts. Before every import, I deleted all the contacts so that the import process always started with an empty address book.

Results of volume testing the import feature of the Firefox OS Contacts App

Number of contacts Import from SIM Card Import from Gmail over WiFi* Import from Gmail over Cellular Network** Notes: Firefox OS build version 1.1 identifier 20130403070204 was used for this test.

* Comcast High-Speed Home Internet Service in Mountain View, CA

** AT&T EDGE (2G) Cellular Network in Mountain View, CA

*** SIM Card does not accommodate more than 250 contacts. 250 ~ 2 minutes ~ 3 minutes ~ 10 minutes 500 N/A *** ~ 9 minutes ~ 14 minutes

Get Involved

Want to get involved with testing Firefox OS? Mozilla would love to have you as a contributor!

To become familiar with Firefox OS, get the Firefox OS Simulator, which is an add-on for your Firefox browser.

Check out the Marionette script written by the Web QA team for the import contacts feature discussed here. There are more automation scripts covering the functionality of the Contacts app in the project’s Github repository.

Say hi to the friendly Firefox OS test team on the #appsqa IRC channel, where you can participate in all discussions relating to Firefox OS quality assurance.

Help bring the next billion smartphone users online with Firefox OS!

Thanks to Matthew Brandt, Stephen Donner and Anthony Chung for reading drafts of this blog post and suggesting improvements.