Address Book Management
PUBLISHED
Sample application features
The ExtendedAddressBook sample application demonstrates how to use the Tizen platform’s Contact, Messaging and Application APIs. It also makes use of filters (AttributeFilter and CompositeFilter) provided by the Tizen Device API.
After startup, the application displays a list of all the contacts stored within the phone’s address book, sorted by the ‘First Name’ attribute. Options are provided to select the filter type (“First Name”, “Last Name” or “Phone”) and enter a string in the search bar. If a string is entered in the search bar, the application will filter the list of contacts displayed, based on the search string and the selected filter types. Every element in the list is registered to handle the ‘swiperight’ and ‘swipeleft’ events. A ‘Swiperight’ event launches an application service for the Phone application, with the selected number as parameter. A ‘Swipeleft’ event opens a message page, where the user can create and send a message to one or more recipients.
The application layout is defined in the index.html file, behavior in main.js and style in style.css.
The sample application uses jQuery Mobile 1.2.0 framework, includes jQuery 1.8.0 library and was tested on Tizen SDK 2.1.0.
Sample Application screen shots
Contact
The tizen.contact object contains information, such as phone numbers, e-mail addresses, etc. and provides API functionality to read, create, remove, and update contacts in specific address books. Address books can be obtained using the getAddressBooks() method, which returns an array of AddressBook objects.
To use any of the Contact functionality, you must declare the necessary privileges in the config.xml file. In this case:
- Open the config.xml file.
- Choose the Privileges tab.
- Add privilege: http://tizen.org/privilege/contact.read
You can also do it manually:
- Open the config.xml file.
- Choose the Source tab.
- Add the following <privilege> tag to the file:
<tizen:privilege name="http://tizen.org/privilege/contact.read"/>
AddressBook object
Before you can add, update or delete a contact, you need to get an instance of the AddressBook object. Sample application gets default address book using the following method:
gAddressbook = tizen.contact.getDefaultAddressBook();
Once you have retrieved the default Address Book, you can fetch the contacts.
Fetch a contact
The fetchAllContacts() function retrieves all the contacts using the AddressBook object's find() method. You need to provide the find() method with a success callback, and optionally an error callback, filter and sort mode:
gAddressbook.find(onContactFindSuccess, onError, phoneFilter, sortingMode);
In this sample application tizen.SortMode and tizen.AttributeFilter objects are used.
tizen.SortMode is a common interface used for sorting queried data.
tizen.AttributeFilter is one of the tizen.AbstractFilter’s subtypes and represents a filter based on an object attribute.
- If the filter is passed containing valid values, only those contacts in the address book that match the filter criteria will be returned in the successCallback.
- If no filter is passed, or the filter contains any invalid values, or if the filter is null or undefined, then the full list of contact items is returned in the successCallback.
- If no contacts are available in the address book or no contact matches the filter criteria, the successCallback will be invoked with an empty array.
In this sample application, only the contacts which contain a phone number are required, and they should be displayed in alphabetical order, so our variables (phoneFilter and sortingMode) are defined as follows:
var sortingMode = new tizen.SortMode('name.firstName', 'ASC'); var phoneFilter = new tizen.AttributeFilter('phoneNumbers.number','CONTAINS', '');
When the contact items meeting these criteria are found, the onContactFindSuccess callback is invoked. This method contains the retrieved list of contacts, which can be added to <ul> element in index.html:
<ul data-role="listview" id="list" data-divider-theme="d" class="ui-listview"> </ul>
Every item in the list is created in this way (addressBookMgr.js):
var str = ''; str += '<li class="ui-li-has-multiline" data-item-type="contact" data-id="' + contacts[i].id + '">' + ((contacts[i].name === null) ? "" : (value = contacts[i].name.firstName) ? value : "") + ' ' + ((contacts[i].name === null) ? "" : (value = contacts[i].name.lastName) ? value : "") + ' ' + '<span class="ui-li-text-sub">' + ((value = contacts[i].phoneNumbers[0]) ? value.number : "") + '</span></li>';
Using filters
The fetchAllContacts() method returns all the contacts that contain phone numbers. In this sample application, one of the core features is the possibility to filter contacts based on other options selected by the user. For this purpose the sample application uses the fetchContacts() method.
The user can select from the following filter options:
Available filters
These buttons toggle a bit in a binary number (initial value: 000). Selecting the FirstName button changes first bit to 1 (001), deselecting – to 0. LastName – changes second bit, Phone – third bit. The decimal value of the resulting number is saved as the variable filter. Depending on the value of filter, a suitable filterMode (described later) is set.
The fetchContacts() method defines four basic filters: firstNameFilter, lastNameFilter, phoneFilter, and includePhone. The first three are used to check if a contact attribute value matches the string entered by the user in the search bar. The last one is used to check if the contact item contains a phone number.
var firstNameFilter = new tizen.AttributeFilter('name.firstName','CONTAINS', attrName); var lastNameFilter = new tizen.AttributeFilter('name.lastName','CONTAINS', attrName); var phoneFilter = new tizen.AttributeFilter('phoneNumbers.number','CONTAINS', attrName); var includePhone = new tizen.AttributeFilter('phoneNumbers.number','CONTAINS', '');
An AttributeFilter is a filter that can be used to search for items based on an object’s attribute. If no third parameter is defined, the filter is used to match all objects that have the attribute defined (the same as the "EXISTS" flag), otherwise, it will be used to find objects whose attribute matches the given value. The second parameter is FilterMatchFlag. All available values for this attribute:
enum FilterMatchFlag { "EXACTLY", "FULLSTRING", "CONTAINS", "STARTSWITH", "ENDSWITH", "EXISTS" };
By default, it is set to "EXACTLY".
Depending on the options chosen by the user, the sample application uses different combinations of filters.
id = filter; switch (id) { case 0: var filterMode = includePhone; break; case 1: var filterMode = new tizen.CompositeFilter("INTERSECTION", [firstNameFilter, includePhone ]); break; case 2: var filterMode = new tizen.CompositeFilter("INTERSECTION", [lastNameFilter, includePhone ]); break; case 3: { var tmp = new tizen.CompositeFilter("UNION", [firstNameFilter, lastNameFilter ]); var filterMode = new tizen.CompositeFilter("INTERSECTION", [tmp, includePhone ]); } break; case 4: var filterMode = phoneFilter; break; case 5: { var tmp = new tizen.CompositeFilter("UNION", [firstNameFilter, phoneFilter ]); var filterMode = new tizen.CompositeFilter("INTERSECTION", [tmp, includePhone ]); } break; case 6: { var tmp = new tizen.CompositeFilter("UNION", [lastNameFilter, phoneFilter ]); var filterMode = new tizen.CompositeFilter("INTERSECTION", [tmp, includePhone ]); } break; case 7: { var tmp = new tizen.CompositeFilter("UNION", [firstNameFilter, lastNameFilter, phoneFilter ]); var filterMode = new tizen.CompositeFilter("INTERSECTION", [tmp, includePhone ]); } break; default: break; }
A CompositeFilter represents a combination of filters. There are 2 types of this filter:
The union - used to filter objects that match any of the filter conditions it includes, this is equivalent to a logical OR.
var tmp = new tizen.CompositeFilter("UNION", [firstNameFilter, phoneFilter ]);
The intersection - used to filter objects that match all the filter conditions it includes, this is equivalent to a logical AND.
var filterMode = new tizen.CompositeFilter("INTERSECTION", [tmp, includePhone ]);
The second parameter contains the list of filters to use in the composite filter.
The sample application uses a union filter to combine the options selected by user, and an intersection filter to ensure that every contact displayed contains a phone number.
Both, CompositeFilter and AttributeFilter, are subtypes of AbstractFilter, which is a common interface used by different types of object filters. You should never use this base interface directly.
Messaging
The messaging API provides access to features such as retrieving available message services and sending SMS messages, which is an important part of this sample application. To use any of the Messaging features, you must declare the necessary privileges in the config.xml file:
- Open the config.xml file.
- Choose the Privileges tab.
- Add privileges: http://tizen.org/privilege/messaging.read and http://tizen.org/privilege/messaging.write
The structure of the message page is described in the index.html file. The main elements are: phone number input, message textarea and “Send” button.
<input type="text" id="formPhone" /> <textarea rows="10" cols="20" id="formBody"></textarea> <a href="" id="send" data-role="button" data-inline="true"> Send </a>
Additionally, a <div> tag is added for logging information about the message sending status and results of other operations:
<div id="logDiv"></div>
Retrieving message service
Clicking on the “Send” button triggers the sendSms() method.
$("#send").unbind().click(function(event) { sendSms(); });
The main purpose of the sendSms() method is to obtain the SMS service. This is done using the tizen.messaging.getMessageServices() method:
tizen.messaging.getMessageServices("messaging.sms", serviceList, onError);
The first argument specifies the type of the service to be retrieved, the second is a callback function that is called when the SMS service is retrieved and the third is an optional callback function that is called if an error occurs.
Creating the message
If tizen.messaging.getMessageServices() succeeds, the success callback function (serviceList) is invoked and an array of MessageService objects (services) are returned, which you can use to create and send messages.
In this method the value of the formBody element is read:
var serviceList = function(services) { var body = $("#formBody").val();
Because, you don’t want the user to be able to send an empty message, if the message area is empty the relevant popup message is displayed:
tlib.view.showPopup("Message cannot be empty.");
tlib.view is a module responsible for showing custom jQueryMobile UI elements on. It includes the showPopup() method, which displays a popup message.
The next step is to create a message to be sent. For SMS services the first parameter (MessageServiceTag) is "messaging.sms" and the second is a new MessageInit object. MessageInit allows you to specify many optional message attributes when the message is created, but this sample application uses only two of these attributes:
- DOMString plainBody;
The plain text representation of the message body.
- DOMString[] to;
The destination addresses (or phone numbers) of a message.
msg = new tizen.Message("messaging.sms", { plainBody : body, to : [ phone ] });
The phone number in the sample application is returned by getPhoneNumber() method.
var getPhoneNumber = function(p) { var phoneNr = ""; var contact = gAddressbook.get(receivers[p]); if (typeof(contact.phoneNumbers[0]) !== 'undefined') { if (contact.phoneNumbers[0]['number'][0] != '+') { phoneNr += "+48"; } phoneNr += (contact.phoneNumbers[0]['number']); } return phoneNr; }
The gAddressbook.get(receivers[p]) returns the contact with the given identifier (in this case identifier = receivers[p]). receivers[] is an array of contact ids.
var receivers = [];
After a message has been successfully created, the sample application can send it. To do this, the sendMessage() method is called with the previously created Message object (msg) as a parameter. You can additionally register two callback functions, which are triggered after the message sending has either succeeded or failed.
services[0].sendMessage(msg, messageSent, messageFailed); /** * Success Callback for function sendMessage() */ var messageSent = function() { logMsg("The SMS has been sent"); } /** * Error Callback for function sendMessage() */ var messageFailed = function(error) { logMsg("The SMS could not be sent: " + error.message); }
You can use a simple logging method to display messages in the previously defined ‘logDiv’ tag:
var logMsg = function(message) { var line = $('<p><p>'); line.html(message); $("#logDiv").append(line); }
Swipe Events
Every element in the list is has registered two events: ‘swiperight’ and ‘swipeleft’.
After attaching a handler to 'swiperight', each time the event is triggered the following callback function is invoked:
elem.unbind('swiperight').bind('swiperight', function(e) { /** * On "swiperight" event call to selected person */ receivers.length = []; receivers[0] = $(this).data("id"); callTo(); });
This clears the array of contact ids and invokes the callTo() method,which is described later.
In a similar way, a handler is attached for the 'swipeleft' event for all elements in the list of contacts. Each time the event is triggered the callback function is invoked:
$(str).unbind('swipeleft').bind('swipeleft', function(e) { /** * On "swipeleft" event send message to selected person */ receivers.length = []; receivers[0] = $(this).data("id"); setMsgPage(); $.mobile.changePage("#msgpage"); });
This clears the array of contact ids, sets the first element as the selected person id, calls the setMsgPage() method and changes to the message page.
The setMsgPage() method sets the properties of the message page.
var setMsgPage = function() { var i, number = ""; if (!page) { page = 1; } else { receivers.length = 0; for (i = 0; i < tmpReceivers.length; i++) { receivers[i] = tmpReceivers[i]; } } for (i = 0; i < receivers.length; i++) { if (getPhoneNumber(i)) { number += getPhoneNumber(i); number += ", "; } else { // If the contact doesn't have proper phone number // contact.phoneNumbers[0] is undefined tlib.view.showPopup("phoneNumbers[0] for receiver (id " + receivers[i] + ") is undefined!"); } } $("#formPhone").attr("value", number); $("#formBody").attr("value", ""); $("#logDiv").empty(); };
As can be seen in the setMsgPage() method, the message can be sent to more than one recipient.
Multiple recipients
One of the message attributes is the phone number. This sample application allows user to add more than one message recipient. To do this, a second page with a list of contacts is displayed for user selection.
The structure of this page is described in the index.html file. The main elements are: ‘Add/Change’ button, search input and list of all contacts.
<a href="" id="add-change" data-role="button" data-icon="plus" data-iconpos="left">Add/Change</a> <!-- search --> <input type="search" name="search-receiver" id="search-receiver" value=""> <ul class="listview-add-more" data-role="listview" id="listAddRec" data-divider-theme="d"> </ul> <!-- /search -->
Every item in the list is created as follows:
str += '<li class="ui-li-has-multiline" data-item-type="contact" data-id="' + contacts[i].id + '"><input type="checkbox" value="' + contacts[i].id + '" name="' + contacts[i].id + '" data-id="' + contacts[i].id + '" id="' + contacts[i].id + '" class="custom"'; if (isChecked(contacts[i].id)) { str += ' checked="checked"'; } str += '>' + '<label for="' + contacts[i].id + '" class="checklistLabel">' + ((contacts[i].name === null) ? "" : (value = contacts[i].name.firstName) ? value : "") + ' ' + ((contacts[i].name === null) ? "" : (value = contacts[i].name.lastName) ? value : "") + ' ' + ((value = contacts[i].phoneNumbers[0]) ? value.number : "") + '</label></li>';
The isChecked(index) method checks if the id of the contact is already in the list of message recipients, so the list ‘checked’ property can be set appropriately.
var isChecked = function(index) { for ( var i = 0; i < tmpReceivers.length; i++) { if (index == receivers[i]) { return true; } } return false; }
Selecting any of the elements from the list of contacts invokes the following:
("#listAddRec").delegate("input", "change", function() { var that = $(this); addReceiver(that); }); var addReceiver = function(that) { var i, checkedNr = that.data("id"); if (tmpReceivers.length > 10) { tlib.view.showPopup("10 contacts available only. Uncheck some contacts."); } if (that.prop("checked")) { var wsk = 0; for (i = 0; i < tmpReceivers.length; i++) { if (tmpReceivers[i] === checkedNr) { wsk = 1; } } (!wsk) ? tmpReceivers.push(checkedNr) : wsk = 0; } else { var idx = -1; for (i = 0; i < tmpReceivers.length; i++) { if (tmpReceivers[i] === checkedNr) { idx = i; } } tmpReceivers.splice(idx, 1); } }
Application
The Application API provides functionality for launching other applications. You can launch other applications by specifying their identifier, or by specifying what operation should be performed by the launched application. The operation is identified by a string. The table containing descriptions of standard operations can be found in the Documentation.
To use any of the application's features, you must declare the necessary privileges in the config.xml file. In this case the application.launch privilege is needed.
- Open the config.xml file.
- Choose the Privileges tab.
- Add privilege: http://tizen.org/privilege/application.launch
Launch a service
If you want your application to use common functionality provided by another application, you can request that the system launches the application that provides your required functionality. The functionality to launch a service is provided by the tizen.application.launchAppControl() method.
tizen.application.launchAppControl(service, null, function() { console.log("launchAppControl"); }, onError, null);
It has the following parameters:
- ApplicationService service,
The data structure describing the service details is constructed using:
service = new tizen.ApplicationControl('http://tizen.org/appcontrol/operation/call', number, null);
The ApplicationControl interface consists of an operation, URI, MIME type, and data (URI and MIME are optional parameters). It describes the action to be performed by another application. When the system gets the service launch request, it finds the application that provides the required service, and launches it.
This functionality is used in this sample application to launch the dialer application to make a phone call. The required operation parameter is 'http://tizen.org/appcontrol/operation/call' and there is no MIME type parameter.
- ApplicationId? id,
This is the identifier of the application to be launched. If the id is null or not specified, then the system tries to find the application to be launched for the requested service.
- optional SuccessCallback? successCallback,
Called when the invocation ends successfully.
- optional ErrorCallback? errorCallback,
Called when an error occurs.
- optional ApplicationServiceDataArrayReplyCallback? replyCallback
Called when the application gets results back from the launched application.
Exit application
The sample application can be terminated. To provide this feature, you should:
- add the Exit button to index.html:
<a href="" id="close" data-role="button" data-icon="delete" data-iconpos="notext">Close</a>
- bind the exit method with the exit button
$("#close").unbind().click(function(event) { var app = tizen.application.getCurrentApplication(); app.exit(); });
Summary
Using the APIs described in this article you can develop applications with the following features: sending and receiving SMS messages, launching other applications and services, making a phone call to a selected person, adding/deleting/modifying contacts and many others.