Call and message history on Tizen

Handling call and message history on Tizen is important for all applications that create a summary of recently made calls or SMS messages sent. You may need such a summary if you want to present some statistics for the recently dialed/received calls or sent/received SMS messages.

LoveMeter illustrates how to obtain the call and message history on Tizen. The main functionality of this application is to present how many connections, both dialed and received, were made by the user with the detailed duration. The sample application also presents how many SMS messages were sent or received by the user. You can easily sort contacts by call duration, the number of SMS messages and the number of calls simply by clicking on a column. You can reverse the order by clicking on already selected column. You can dial a number from a list by clicking on each row.

This sample application uses the jQuery Mobile 1.2.0 framework and includes the jQuery 1.8.2 library and was tested on Tizen SDK 2.1.0.

LoveMeter screen shot – contacts sorted in descending order by number of calls.

Fig 1. LoveMeter screen shot – contacts sorted in descending order by number of calls.

LoveMeter screen shot – contacts sorted in descending order by call duration.

Fig 2. LoveMeter screen shot – contacts sorted in descending order by call duration.

How to obtain the call history?

To access the call history you have to add the required permissions to the config.xml file:

<tizen:privilege name="http://tizen.org/privilege/callhistory.read"/>

You can do that either by manually editing the XML file or by using the Privileges tab if you open the XML document in the Eclipse IDE with Tizen SDK.

The next thing you need to do is to create a valid AttribiuteFilter. For a detailed explanation about the tizen.AttribiuteFilter please refer Tizen documentation. The AttribiuteFilter is very similar in action to a SQL language query and you can read it almost in the same way as you read SQL. Each AttribiuteFilter accepts the following parameters:

  1. attributeName
  2. matchFlag
  3. matchValue

A properly defined AttribiuteFilter has to be passed to the tizen.callhistory.find method, along with two callback functions. The tizen.callhistory.find is an asynchronous function and it takes up to six parameters, while only the first one is mandatory. The code below also shows the tizen.SortMode interface. It sets the sorting attribute and the sorting order.

var filter = new tizen.AttributeFilter("type", "EXACTLY", "TEL"),
    sortMode = new tizen.SortMode("startTime", "DESC");

try {
    tizen.callhistory.find(onCallHistoryFindSuccess, onCallHistoryFindError, filter, sortMode);
} catch (err) {
    alert("Error: Find failed: " + err.code + ": " + err.message);
}

Please note that the meaning of the above mentioned tizen.AttributeFilter is similar to the following SQL query:

SELECT * FROM ? WHERE type=”TEL”

If we keep the SQL convention you can see that the FROM table is not defined. In order to define it we need to pass the tizen.AttributeFilter as a parameter to one of Tizen APIs. In our case it’s the tizen.callhistory API. This API provides the CallHistoryEntries objects in response. So we can update the SQL query that it fully reflects the previous JavaScript code snippet:

SELECT * FROM CallHistoryEntries WHERE type=”TEL” ORDER BY startTime DESC

Now we need to provide the success callback function for handling the CallHistoryEntries array. The onCallHistoryFindSuccess function can be found below:

function onCallHistoryFindSuccess(results) {
    if (results.length > 0) {
        for ( var i = 0; i < results.length; i++) {
            var conn = new connection({
                number : results[i].remoteParties[0].remoteParty,
                date : results[i].startTime,
                sms : 0,
                connection : 1,
                duration : results[i].duration
            });
            contacts.add(conn);
        }
        resetList();
    } else {
        noListItemsFound();
    }
}

This callback provides a simple data parsing mechanism from CallHistoryEntries array that is passed as result to the array of connection object instances (./js/connection.js). Please notice that the CallHistoryEntry object contains the following properties:

  1. remoteParties – this is an array with all the parties that participate in the call. In case of phone call this object represents a person who either called or received our call. The remoteParty property represents the exact phone number. In this application we assume a simple case, that there is only one remoteParty participating in a phone call and conference calls with many participants.
  2. startTime – represents a Date object for connection start time
  3. duration – represents the call duration
  4. direction – is a string that represents the call direction in order to determine if this was a received, dialed or missed call. In this application only outgoing and incoming connections have been taken into account.

You can find the connection constructor function (./js/connection.js) below:

var connection = function(options) {
    this.number = options.number;
    this.date = options.date;
    this.connection = options.connection;
    this.sms = options.sms;
    this.duration = options.duration;
};

You don’t need to know the exact implementations of resetList() and noListItemsFound() functions. They are used to create the list UI.

How to obtain the SMS message history?

We can obtain the SMS message history almost as easy as we can get the call history. In order to be able to use the tizen.messaging API you should include the right permissions in the config.xml file:

<tizen:privilege name="http://tizen.org/privilege/messaging.read" />

You should then use the tizen.messaging.getMessageServices method in order to obtain the MessageService object instance. There is a possibility to have many message services on one device. By default you should choose the first one.

tizen.messaging.getMessageServices("messaging.sms", serviceListCB);

This method accepts up to four parameters, from which only the first two are mandatory. The first parameter is a string that represents a message service type. You can choose from the following types:

enum MessageServiceTag {"messaging.sms", "messaging.mms", "messaging.email" };

We are interested only in the SMS service.
The second parameter is the success callback function. This function accepts only one parameter which is an array of MessageService objects:

function serviceListCB (services) {
    smsService = services[0];
    // Set the abstract filter
    var filter = new tizen.AttributeFilter("type", "EXACTLY", "messaging.sms");
    smsService.messageStorage.findMessages(filter, messageArrayCB, errorCallback);
}

Next we create an AttributeFilter in the same manner as we did previously. Please find the corresponding SQL query below:

SELECT * FROM ? WHERE type=”messaging.sms”

In order to perform the actual search operation you need to invoke the findMessages method from smsService.MessageStorage. The findMessages method accepts up to six parameters from which only the first two are mandatory. The first invocation parameter is the AttributeFilter, the second one is the success callback and the third one represents the error callback. After passing AttributeFilter to the findMessages method we can create the corresponding SQL query in the following way:

SELECT * FROM Messages WHERE type=”messaging.sms”

As you can see from the code sample we take into account all messages both sent and received.

Matching connections with contacts from the address book

The sample application displays a list of people with whom you keep the closest relations in a given period of time. The Tizen call API provides information about the recently dialed/received connections, but it doesn’t provide the real names of the contacts. In order to obtain real names of contacts from the address book we need to create a matching mechanism between the results returned by the Tizen call API, Tizen messaging API and Tizen contact API. To achieve this we will use AttributeFillters and CompositeFilter. Please refer to the Tizen documentation in order to get more information about filters. This article will only show you how to mach recently dialed/received numbers with the address book, we will not take into account messaging, for that please refer to the application source code.

For each connection we create an AttribiuteFilter. Please keep in mind that the filters array is created only once, and then we just add new AtribiuteFilters into this array.

var filters = [];

// adding new AttributeFilter for number
var number = "333444"; // just a sample phone number
var filter = new tizen.AttributeFilter('phoneNumbers.number', 'EXACTLY', number);
filters.push(filter);

AttributeFilter in the above code snippet corresponds to the following SQL query:

SELECT * FROM ? WHERE phoneNumber.number=”333444”

Usually the user dials/receives many connections, so we need to match them all with the contacts in the address book. To do that we need to use CompositeFilter. The aim of CompositeFilter is to merge many AttributeFillters together in a single query, using one logical operation. The CompositeFilter constructor takes two parameters:

  1. type – this is a string that defines the logical operation: UNION, INTERSECTION. UNION represents the OR logical operation and INTERSECTION represents the AND logical operation.
  2. AttribiuteFilter array – this is an array with AtrributeFiter object instances

In order to use tizen.contact API we should add the appropriate permissions in the config.xml file:

<tizen:privilege name="http://tizen.org/privilege/contact.read" />

We can then easily get the default device address book and pass our CompositeFilter as a parameter for the addressbook.find method:

var addressbook = tizen.contact.getDefaultAddressBook();
var filter = new tizen.CompositeFilter("UNION", filters);
addressbook.find(successCallback, errorCallback, filter);

Please keep in mind that the filters array was already created. The code snippet above corresponds to the following SQL query:

SELECT * FROM Contacts WHERE phoneNumber.number=”…” OR phoneNumber.number=”…” OR …

You invoke the addressbook.find method with three parameters. The first parameter is the success callback function. You can see the implementation of this function below. Please notice that the contacts array was already defined (please refer the source code). It contains contacts already recognized. The success callback function simply iterates through all contactsFound instances and then tries to match the found contact number with the contact phone number that was already stored in the contacts array. In case of a match the first name and second name are updated.

function contactsFound(contactsFound) {
    for (var i = 0; i < contactsFound.length; i++) {
        for ( var j = 0; j < contacts.length; j++) {
            if (contactsFound[i].phoneNumbers[0].number === contacts[j].number) {
                contacts[j].firstName = contactsFound[i].name.firstName;
                contacts[j].secondName = contactsFound[i].name.lastName;
                break;
            }
        }
    }
}

How to assign a dialed number or SMS message to a time period?

The sample application gives the possibility to filter connections and SMS messages by time period. You can choose between: Last 7 days, Last 30 days and Overall. The default options in Overall.

1. Last 7 days filter will output only calls/messages received within the last 7 days
2. Last 30 days filter will output only calls/messages received within the last 30 days
3. Overall will output all calls/messages

The Tizen API doesn’t provide tools to filter Calls or Messages by time period, although it does provide the information on when the call was made or the message was received. You should handle grouping by time on your own in the application. Fortunately it can be easily achieved. Please refer to the ./js/contact.js file. In this file the contact constructor function is defined. Each new connection is added for the contact using the following method:

this.addConnection = function(con) {
    var oneDay = 1000 * 60 * 60 * 24;
    var today = new Date();
    var delta = (today.getTime() - con.date.getTime()) / oneDay;

    if (delta < 7) {
        this.connections.week += con.connection;
        this.duration.week += con.duration;
        this.sms.week += con.sms;
    }
    if (delta < 30) {
        this.connections.month += con.connection;
        this.duration.month += con.duration;
        this.sms.month += con.sms;
    }
    this.connections.total += con.connection;
    this.duration.total += con.duration;
    this.sms.total += con.sms;

};

As you can see the connection object has dedicated properties for storing connections per last 7 days, last 30 days and overall (total). A simple delta comparison algorithm was created in order to correctly classify the connection for a given time period.

Summary

In this article we presented how to use Tizen call and messaging APIs in order to create simple statistics for dialed/received calls, sent/received SMS messages and connection duration. Thanks to these features, the sample application called LoveMeter has the ability to determine who is your best friend based on connections performed and SMS messages.

文件附件: