Mobile Web

Task: Chatter

This task, based on the Chatter sample delivered with the Tizen SDK, demonstrates how you can use the Messaging API to send, receive, and manage your messages. For more information on the sample functionality and creating the sample with the full source code, see Chatter.

This task consists of the following parts:

This sample is a fully functional application for managing, creating, and sending messages.

Defining the Application Layout

The Chatter sample application layout consists of 3 screens: the main screen displays a contact list, the Select contact screen displays a contact list and a pop-up allowing the user to enter a phone number, and the chat screen displays the conversation history, an input area, and a button, allowing the user to write and send a message.

The following figure shows the main screens of the application.

Figure: Chatter screens

Chatter screens

Defining the Main Screen

  1. main_page.tpl Source File

    The header section of the screen is defined within a <div> element whose data-role attribute is set to header. The header section determines the title of the screen.

    The content section of the screen is added dynamically and displays a contact list (in mobile or wearable applications). The footer section contains a tab bar with a button for starting a new chat.

    <!--Header section-->
    <div id="main-header" data-role="header" data-position="fixed">
       <h1>Chatter</h1>
    </div>
    
    <!--Content section-->
    <div id="main-content" data-role="content">
       <ul data-role="listview"></ul>
    </div>
    
    <!--Footer section-->
    <div id="main-footer" data-role="footer" data-position="fixed">
       <div data-role="tabbar" data-style="toolbar">
          <ul>
             <li><a href="#" id="contactSelect-button">New chat</a></li>
          </ul>
       </div>
    </div>
    
  2. app.ui.event.js Source File

    When the user taps the New chat button, an event is fired, and the contact list is loaded. The events are based on jQuery selectors.

    $('#contactSelect-button').on('click', function(event) 
    {
       event.preventDefault();
       event.stopPropagation();
       app.loadContacts();
    });
    

Defining the Select Contact Screen

  1. contactSelect.tpl Source File

    The content section of the Select contact screen displays a contact list. If the user taps the Enter number button (in mobile or wearable applications), a pop-up appears allowing the user to enter a phone number for a contact not on the list. The popup component (in mobile or wearable applications) contains a form for number input.

    <!--Content section-->
    <div data-role="content">
       <ul data-role="listview" id="contactSelect-list"></ul>
       <a href="#" id="enterNumber" data-role="button">Enter number</a>
    </div>
    	
    <div data-role="popup" id="enterNumber-popup">
       <div class="ui-popup-title">
       <h1>Enter phone number<h1>
    </div>
    <div class="ui-popup-text">
       <form id="numberForm">
             <input type="tel" id="number" name="number" maxlength="20"/>
       </form>
    </div>
    <div class="ui-popup-button-bg">
       <a data-role="button" id="enterNumberCreate" data-rel="back" 
          data-inline="true" class="ui-disabled">
          Create Chat
       </a>
    </div>
    

Defining the Chat Screen

  1. chat.tpl Source File

    When the user taps a list item on the main screen, the chat screen is displayed. The structure of the chat template is similar to main screen. The content section of the screen displays all messages from the selected contact, grouped into a conversation.

    The footer section contains a textarea for writing a message and a button for sending it.

    The template also contains a popup element used to show warning and exceptions information.

    <!--Content section-->
    <div id="chat-content" data-role="content">
       <ul data-role="listview" id="message-chat"></ul>
    </div>
    
    <!--Footer section-->
    <div id="chat-footer" data-role="footer" data-position="fixed">
       <div class="ui-textArea">
          <div class="ui-textArea-text">
             <textarea id="text" class="ui-textArea-text-text" placeholder="Your message" data-role="none"></textarea>
          </div>
          <div class="ui-textArea-button">
             <a data-role="button" id="send">Send</a>
             <div data-role="none" id="counter" class="counter"><p></p></div>
          </div>
       </div>
    </div>
    
    <div data-role="popup" id="alertPopup" class="ui-corner-all">
       <p class="text"></p>
       <div class="alertPopup-button">
          <a href="#" data-role="button" data-inline="true" data-rel="back">OK</a>
       </div>
    </div>
    

Initializing the Application

  1. config.xml Source File

    The required privileges are declared in the config.xml file.

    <!--Configuration file content-->
    <widget ...>
       <!--Other configuration details-->
       <tizen:privilege name="http://tizen.org/privilege/application.launch"/>
       <tizen:privilege name="http://tizen.org/privilege/contact.read"/> 
       <tizen:privilege name="http://tizen.org/privilege/contact.write"/> 
       <tizen:privilege name="http://tizen.org/privilege/messaging.read"/>
       <tizen:privilege name="http://tizen.org/privilege/messaging.send"/>
       <tizen:privilege name="http://tizen.org/privilege/messaging.write"/>
    </widget>

Managing Messages

This section builds upon the elements described in Managing Messages.

Retrieving SMS Messages

The message retrieval functionality is implemented in the app.model.js file.

  1. Preparing the Message Service

    To retrieve messages from the device storage, you must first initialize the MessageService interface and prepare the smsService object.

    initSmsService: function() 
    {
       var self = this;
       try 
       {
          tizen.messaging.getMessageServices("messaging.sms", function(s) 
          {
             self.smsService = s[0];
             self.prepareMessages(app.fillUpMessagePage.bind(self));
             self.messagesChangeListener();
          });
       }
    }
    
  2. Retrieving Messages for the Device Storage

    The messages are retrieved using the findMessages() method of the MessageStorage interface. The first argument of the method a is the AttributeFilter filter object of the Tizen API, and the second argument is an event handler for sorting the messages (newest on top). The retrieved messages are saved in the messagesList variable.

    prepareMessages: function(callback) 
    {
       var self = this;
       try 
       {
          this.smsService.messageStorage.findMessages(new tizen.AttributeFilter("type", "EXACTLY", "messaging.sms"),
                                                      function(messages) 
          {
             function compare(a, b) 
             {
                if (a.timestamp > b.timestamp) 
                {
                   return -1;
                } 
                else if (a.timestamp < b.timestamp) 
                {
                   return 1;
                } 
                else 
                {
                   return 0;
                }
             }
             messages.sort(compare);
             self.messagesList = self.groupMessages(messages);
             app.ui.loadCallerList();
             callback();
          },
       }
    }
    

Grouping SMS Messages

Chatter messages are grouped according to their recipient key. Each recipient object consists of a message array and the latest message. The grouped object can be used for both main and chat screens of the Chatter application.

The message grouping functionality is implemented in the app.model.js file.

  1. Creating the Group
    1. The groupMessages() method is used to filter the messages according to their status.

      groupMessages: function(messages) 
      {
         var i, obj = {}, folderId;
         for (i in messages) 
         {
            folderId = messages[i].folderId;
            if ((folderId !== null && folderId !== this.DRAFTS_FOLDER) || messages[i].messageStatus === 'DRAFT') 
            {
               if (messages.hasOwnProperty(i)) 
               {
                  obj = this.groupMessagesSingle(messages[i], obj);
               }
            }
         }
      
         return obj;
      }
      
    2. The groupMessagesSingle() method prepares the conversation array for the recipient key.

      groupMessagesSingle: function(message, obj) 
      {
         var key, j;
         if (message.from) 
         {
            key = message.from;
            obj[key] = this.pushData(message, obj[key]);
         } 
         else 
         {
            for (j in message.to) 
            {
               if (message.to.hasOwnProperty(j)) 
               {
                  key = message.to[j];
                  obj[key] = this.pushData(message, obj[key]);
               }
            }
         }
      
         return obj;
      }
      
  2. Pushing Data to the Conversation Array

    The pushData() method pushes the retrieved data into the recipient conversation array and sets the lastMessage object.

    pushData: function(message, obj) 
    {
       obj = obj || this.getGroupObject();
       obj.messages.push(message);
       if (app.helpers.objectLength(obj.lastMessage) === 0) 
       {
          obj.lastMessage = message;
       }
    
       return obj;
    }
    
  3. Including the Latest Message

    The getGroupObject() method is used to include the latest message in the array.

    getGroupObject: function() 
    {
       return 
       {
          messages: [], lastMessage: {},
          last: 
          {
             body: {plainBody: null}, timestamp: new Date()
          }
       };
    }
    

Creating and Sending Messages

This section builds upon the elements described in Creating and Sending Messages.

Getting the Contact List

  1. app.model.js Source File
    1. To get the contact list from the device memory, get the default address book using the getDefaultAddressBook() method.

      this.addressBook = tizen.contact.getDefaultAddressBook();
      
    2. The contacts are retrieved using the find() method of the AddressBook interface of the Contact API, and grouped into a contactsLoaded object.

      loadContacts: function(callback) 
      {
         var contactsFoundCB, errorCB;
      
         this.contactsLoaded = null;
      
         contactsFoundCB = function(contacts) 
         {
            var i;
            this.contactsLoaded = [];
            for (i in contacts) 
            {
               if (contacts.hasOwnProperty(i)
                  && this.getNumberFromContact(contacts[i])) 
               {
                  contacts[i].primaryNumber =
                     this.getNumberFromContact(contacts[i])
                  this.contactsLoaded.push(contacts[i]);
               }
            }
            if (callback instanceof Function) 
            {
               callback();
            }
         };
      
         this.addressBook.find(contactsFoundCB.bind(this), errorCB);
      }
      

Setting the Message Recipient

The message recipient selection functionality is implemented in the app.js file.

  1. Displaying the Contacts

    Before the user write the message text, they select the message recipient. The recipient list stored in the contactsLoaded object in the device memory is retrieved and displayed for the selection.

    showContactsLoaded: function() 
    {
       var i, len, sortedContactList = [];
    
       if (this.model.contactsLoaded !== null && this.model.contactsLoaded.length) 
       {
          len = this.model.contactsLoaded.length;
          for (i = 0; i < len; i += 1) 
          {
             if (this.model.contactsLoaded[i].phoneNumbers.length) 
             {
                sortedContactList.push(
                {
                   caller: this.helpers.getCallerName(this.model.contactsLoaded[i], 'no name'),
                   number: this.model.contactsLoaded[i].primaryNumber,
                   contact: this.model.contactsLoaded[i]
                });
             }
          }
    
          sortedContactList.sort(function(a, b) 
          {
             if (a.caller < b.caller) 
             {
                return -1;
             } 
             else if (a.caller > b.caller) 
             {
                return 1;
             }
    
             return 0;
          });
       }
       this.ui.fillContactList(sortedContactList);
    }
    
    fillContactList: function(sortedContactList) 
    {
       var i, ul = $("#contactSelect-list").empty(),
          len, listElement, self = this;
    
       len = sortedContactList.length;
    
       for (i = 0; i < len; i += 1) 
       {
          listElement = this.templateManager.get('contactRow', 
          {
             'number': sortedContactList[i].number,
             'callerName': sortedContactList[i].caller
          });
    
          if (app.helpers.validateNumberLength(sortedContactList[i].number)) 
          {
             ul.append(listElement);
          }
       }
  2. Selecting the Contact

    When the user clicks the wanted recipient, the touch event that sets the current list item as the recipient is triggered, and the chat screen is displayed.

       $('li.ui-li-has-multiline', ul).on('click', function(event) 
       {
          app.ui.onCallerListElementTap(event, $(this));
       });
    
       ul.trigger('create');
       ul.listview('refresh');
    }
    

Sending the Message

  1. app.model.js Source File

    After the user writes the message and taps the Send button, the sendMessage() method is called to send the message using the smsService object. The message type and recipient information are included as arguments for the method.

    sendMessage: function(number, text, callback, errorcallback) 
    {
       var message;
       callback = callback || new Function();
       errorcallback = errorcallback || new Function();
       message = new tizen.Message("messaging.sms", {plainBody: text, to: [number]});
       try 
       {
          this.smsService.sendMessage(message, callback, function(e) 
          {
             /* Error handling */
          });
       }
    },
    

Managing Message Status Changes

This section builds upon the elements described in Receiving Notifications on Message Storage Changes.

Getting the Message Status

  1. app.model.js Source File

    An event listener is added to detect whether messages have been changed, added, or removed using the addMessagesChangeListener() method of the MessageStorage interface. When messages have been added or removed, the prepareMessages() method is called to refresh the message list.

    messagesChangeListener: function() 
    {
       var self = this, config, messageChangeCallback = 
       {
          messagesupdated: function(messages) 
          {
             if (messages[0].messageStatus !== 'SENDING') 
             {
                app.ui.changeMessageStatus(messages[0]);
             }
          },
          messagesadded: function(messages) 
          {
             self.prepareMessages(app.ui.showMessageChat);
          },
          messagesremoved: function() 
          {
             self.prepareMessages(app.ui.showMessageChat);
          }
       };
       this.smsService.messageStorage.addMessagesChangeListener(messageChangeCallback);
    }
    

Displaying Updated Messages

  1. app.ui.js Source File

    When the message status has changed, the changeMessageStatus()method is called to update the message view.

    changeMessageStatus: function(message, loop) 
    {
       var warning = $('#' + message.id + ' .warning'),
           classes, i, self = this;
       loop = loop + 1 || 0;
       if (warning.length === 1) 
       {
          classes = warning.attr('class').split(' ');
          for (i in classes) 
          {
             if (classes.hasOwnProperty(i)) 
             {
                if (/status([A-Z]*)/.test(classes[i])) 
                {
                   warning.removeClass(classes[i]);
                }
             }
          }
          warning.addClass('status' + message.messageStatus);
       } 
       else if (loop < 3) 
       {
          setTimeout(function() 
          {
             self.changeMessageStatus(message, loop)
          }, 1000);
       }
    }
    
Go to top