Page Example

Bluetooth Chat Sample Overview

Wearable native

The (Circle) Bluetooth Chat sample application demonstrates how you can send and receive data between 2 devices.

The following figure illustrates the main screens of the (Circle) Bluetooth Chat application.

Figure: (Circle) Bluetooth Chat screens

(Circle) Bluetooth Chat screens

The main screen has 2 buttons for finding devices (Search friends) and creating a server (Wait a friend). The chat room screen is created with the [UI Sample] MessageBubble sample application.

Implementation

Client

To start the client application, click Search friends. The client draws its own layout (list of found devices) with the _search_layout_create() function.

While drawing the list, the application starts finding devices with the _discovery_start() function. Set the device discovery state changed callback, and the list updates whenever a new device is found.

static void 
_search_layout_create(appdata_s *ad)
{
   int ret = 0;

   ret_if(!ad);
   ret_if(!ad->navi);

   ad->role = BT_SOCKET_CLIENT;

   s_info.list = elm_list_add(ad->navi);
   ret_if(!s_info.list);

   evas_object_event_callback_add(s_info.list, EVAS_CALLBACK_DEL, _on_search_del_cb, ad);
   evas_object_size_hint_weight_set(s_info.list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(s_info.list, EVAS_HINT_FILL, EVAS_HINT_FILL);
   elm_list_go(s_info.list);
   elm_naviframe_item_push(ad->navi, "Search Friends", NULL, NULL, s_info.list, NULL);

   ret = bt_socket_set_connection_state_changed_cb(_socket_conn_state_changed_cb, ad);
   ret_if(ret != BT_ERROR_NONE);

   _discovery_start(ad);
}

If you click an item on the list, the application starts to bond with the target device, and requests to connect to the target server. When successfully connected with the target, the _socket_conn_state_changed_cb() function is called and the application moves to the chat room layout for chatting.

static void 
_socket_conn_state_changed_cb(int result, bt_socket_connection_state_e connection_state, 
                              bt_socket_connection_s *connection, void *user_data)
{
   appdata_s *ad = (appdata_s *)user_data;
   ret_if(!ad);

   ret_if(result != BT_ERROR_NONE);

   if (connection_state == BT_SOCKET_CONNECTED) 
   {
      if (connection != NULL) 
      {
         _D("Connected %d %d", ad->socket_fd, connection->socket_fd);
         ad->role = connection->local_role;
         ad->socket_fd = connection->socket_fd;
         bt_chat_room_layout_create(ad);
         if (s_info.noti) 
         {
            evas_object_del(s_info.noti);
            s_info.noti = NULL;
         }
      } 
      else 
      {
         _D("No connection data");
      }
   } 
   else 
   {
      ad->socket_fd = -1;
      _D("Disconnected");
   }
}

static void 
_device_bond_created_cb(int result, bt_device_info_s *device_info, void *user_data)
{
   int ret = 0;

   if (result != BT_ERROR_NONE) 
   {
      _E("Failed result: %d", result);
      goto DEL_NOTI;
   }

   if (device_info != NULL &&
       !strncmp(device_info->remote_address, s_info.info->remote_address, 
                strlen(device_info->remote_address))) 
   {
      ret = bt_socket_connect_rfcomm(s_info.info->remote_address, BT_MGR_UUID);
      if (ret != BT_ERROR_NONE) 
      {
         _E("[bt_socket_listen_and_accept_rfcomm] Failed");
         goto DEL_NOTI;
      }
   } 
   else 
   {
      _D("[bond create cb] Bonded with another device");
      goto DEL_NOTI;
   }

   return;

   DEL_NOTI:

      if (s_info.noti) 
      {
         evas_object_del(s_info.noti);
         s_info.noti = NULL;
      }

      return;
}

Server

To start the server application, click Wait a friend. The role of this application is to create a server for chatting, and wait for a client. Before drawing the proper layout, it checks whether the device is discoverable using the _server_create() function.

static void 
_server_create(appdata_s *ad)
{
   int server_socket_fd = -1;
   int ret = 0;

   ret = bt_socket_create_rfcomm(BT_MGR_UUID, &server_socket_fd);
   ret_if(ret != BT_ERROR_NONE);

   ad->server_socket_fd = server_socket_fd;
   _D("[bt_socket_create_rfcomm] socket %d", server_socket_fd);

   ret = bt_socket_set_connection_state_changed_cb(_socket_conn_state_changed_cb, ad);
   ret_if(ret != BT_ERROR_NONE);

   ret = bt_socket_listen_and_accept_rfcomm(server_socket_fd, MAX_NUM_PENDING);
   if (ret != BT_ERROR_NONE) 
   {
      _E("[bt_socket_listen_and_accept_rfcomm] Failed");
   }
}

The server application also registers a callback for detecting the state of the socket connection. The callback function is the same as that of the client. The application creates a server, and when the socket connection is established, it moves to the chat room layout.

static void 
_server_layout_create(appdata_s *ad)
{
   Evas_Object *layout = NULL;
   Evas_Object *progress = NULL;
   Elm_Object_Item *navi_it = NULL;
   char edj_path[PATH_MAX] = {0,};

   ad->role = BT_SOCKET_SERVER;

   app_resource_get(CREATE_SERVER, edj_path, (int)PATH_MAX);
   layout = elm_layout_add(ad->navi);
   goto_if(!layout, ERROR);
   elm_layout_file_set(layout, edj_path, "create_server");
   evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, 0.5);
   evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);

   progress = elm_progressbar_add(layout);
   elm_object_style_set(progress, "process/popup/small");
   evas_object_size_hint_align_set(progress, EVAS_HINT_FILL, 0.5);
   evas_object_size_hint_weight_set(progress, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_progressbar_pulse(progress, EINA_TRUE);
   evas_object_show(progress);

   elm_object_part_content_set(layout, "progress", progress);

   navi_it = elm_naviframe_item_push(ad->navi, "Wait a Friend", NULL, NULL, layout, NULL);
   elm_naviframe_item_title_enabled_set(navi_it, EINA_FALSE, EINA_FALSE);

   _server_create(ad);

   return;

   ERROR:
      _E("Failed to create server layout");

      if (layout) 
      {
         evas_object_del(layout);
         layout = NULL;
      }

      if (progress) 
      {
         evas_object_del(progress);
         progress = NULL;
      }

      return;
}

Chat Room

To implement the chat room layout and send a message:

  1. Draw the layout:
    static Evas_Object *
    _main_view_create(appdata_s *ad)
    {
       Evas_Object *layout = NULL;
       Evas_Object *input_field = NULL;
       Evas_Object *btn_send = NULL;
       char edj_path[PATH_MAX] = {0,};
    
       retv_if(!ad, NULL);
    
       layout = elm_layout_add(ad->navi);
       goto_if(!layout, ERROR);
       app_resource_get(EDJ_FILE, edj_path, (int)PATH_MAX);
       elm_layout_file_set(layout, edj_path, "chat_room");
       evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, EVAS_HINT_FILL);
       evas_object_show(layout);
    
       s_info.bubble_scroller = elm_scroller_add(layout);
       goto_if(!s_info.bubble_scroller, ERROR);
       elm_scroller_bounce_set(s_info.bubble_scroller, EINA_FALSE, EINA_TRUE);
       evas_object_size_hint_weight_set(s_info.bubble_scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_size_hint_align_set(s_info.bubble_scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
       evas_object_show(s_info.bubble_scroller);
    
       s_info.bubble_box = elm_box_add(s_info.bubble_scroller);
       goto_if(!s_info.bubble_box, ERROR);
       elm_box_align_set(s_info.bubble_box, 0, 0);
       evas_object_size_hint_weight_set(s_info.bubble_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_size_hint_align_set(s_info.bubble_box, EVAS_HINT_FILL, EVAS_HINT_FILL);
       evas_object_show(s_info.bubble_box);
       elm_box_padding_set(s_info.bubble_box, ELM_SCALE_SIZE(10), ELM_SCALE_SIZE(15));
       elm_object_content_set(s_info.bubble_scroller, s_info.bubble_box);
    
       elm_object_part_content_set(layout, "sw.scroller", s_info.bubble_scroller);
    
       s_info.input_field_entry = elm_entry_add(layout);
       goto_if(!s_info.input_field_entry, ERROR);
       elm_object_part_text_set(s_info.input_field_entry, "elm.guide", "Enter Message");
       evas_object_size_hint_weight_set(s_info.input_field_entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_size_hint_align_set(s_info.input_field_entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
       evas_object_show(s_info.input_field_entry);
       elm_object_part_content_set(layout, "sw.entry", s_info.input_field_entry);
    
       btn_send = elm_button_add(layout);
       goto_if(!btn_send, ERROR);
       evas_object_size_hint_weight_set(btn_send, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_size_hint_align_set(btn_send, EVAS_HINT_FILL, EVAS_HINT_FILL);
       elm_object_text_set(btn_send, "SEND");
       evas_object_smart_callback_add(btn_send, "clicked", _send_button_clicked_cb, ad);
       evas_object_show(btn_send);
       elm_object_part_content_set(layout, "sw.btn.send", btn_send);
    
       return layout;
    
       ERROR:
          if (layout) 
          {
             evas_object_del(layout);
             layout = NULL;
          }
    
          if (input_field) 
          {
             evas_object_del(input_field);
             input_field = NULL;
          }
    
          return NULL;
    }
    
    static void 
    _on_main_scroller_del_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
    {
       if (s_info.main_box) 
       {
          evas_object_del(s_info.main_box);
          s_info.main_box = NULL;
       }
    
       if (s_info.bubble_scroller) 
       {
          evas_object_del(s_info.bubble_scroller);
          s_info.bubble_scroller = NULL;
       }
    
       if (s_info.bubble_box) 
       {
          evas_object_del(s_info.bubble_box);
          s_info.bubble_box = NULL;
       }
    
       if (s_info.input_field_entry) 
       {
          evas_object_del(s_info.input_field_entry);
          s_info.input_field_entry = NULL;
       }
    }
    
  2. Manage the connection state changes:
    static void 
    _socket_conn_state_changed_cb(int result, bt_socket_connection_state_e connection_state, 
                                  bt_socket_connection_s *connection, void *user_data)
    {
       Evas_Object *noti = NULL;
       appdata_s *ad = NULL;
    
       ad = (appdata_s *)user_data;
       ret_if(!ad);
       ret_if(result != BT_ERROR_NONE);
    
       _D("[_socket_conn_state_changed_cb] Changed");
    
       if (connection_state == BT_SOCKET_DISCONNECTED) 
       {
          _I("[_socket_conn_state_changed_cb] Disconnected");
          ad->socket_fd = -1;
          noti = bt_noti_popup_create(ad->navi, "Disconnected with the Friend");
          if (!noti) 
          {
             _E("Failed to create popup noti");
          }
       }
    }
    
  3. Receive the data:
    static void 
    _socket_data_received_cb(bt_socket_received_data_s *data, void *user_data)
    {
       Evas_Object *bubble_table = NULL;
       char *message = NULL;
    
       ret_if(!data);
    
       message = strndup(data->data, data->data_size);
       goto_if(!message, ERROR);
    
       bubble_table = _bubble_table_create(s_info.bubble_box, MESSAGE_BUBBLE_RECEIVE, 
                                           message, _current_time_get());
       goto_if(!bubble_table, ERROR);
    
       evas_object_show(bubble_table);
       elm_box_pack_end(s_info.bubble_box, bubble_table);
    
       evas_object_event_callback_add(s_info.bubble_box, EVAS_CALLBACK_RESIZE, 
                                      _bubble_box_resize_cb, NULL);
    
       free(message);
    
       return;
    
       ERROR:
          if (bubble_table) 
          {
             evas_object_del(bubble_table);
             bubble_table = NULL;
          }
    
          if (message) 
          {
             free(message);
             message = NULL;
          }
    
          return;
    }
    
  4. The chat room is created by calling the bt_chat_room_layout_create() function. The function draws the layout for chatting, and unsets and re-registers the callback for the connection state change. It also registers the data received callback. When the message is received from the target, the application makes a message bubble for the new message.
    void 
    bt_chat_room_layout_create(appdata_s *ad)
    {
       Evas_Object *main_scroller = NULL;
       Elm_Object_Item *navi_it = NULL;
       int ret = -1;
    
       bt_socket_set_data_received_cb(_socket_data_received_cb, NULL);
    
       ret = bt_socket_unset_connection_state_changed_cb();
       ret_if(ret != BT_ERROR_NONE);
    
       ret = bt_socket_set_connection_state_changed_cb(_socket_conn_state_changed_cb, ad);
       ret_if(ret != BT_ERROR_NONE);
    
       main_scroller = _main_view_create(ad);
       ret_if(!main_scroller);
    
       evas_object_event_callback_add(main_scroller, EVAS_CALLBACK_DEL, _on_main_scroller_del_cb, NULL);
       navi_it = elm_naviframe_item_push(ad->navi, "Chatting", NULL, NULL, main_scroller, NULL);
       elm_naviframe_item_title_enabled_set(navi_it, EINA_FALSE, EINA_FALSE);
    }
    
  5. Send a message:
    static void 
    _message_send(appdata_s *ad)
    {
       Evas_Object *bubble_table = NULL;
       Evas_Object *noti = NULL;
       const char *main_text = NULL;
       int ret = 0;
    
       ret_if(!ad);
       ret_if(!s_info.input_field_entry);
    
       main_text = elm_entry_entry_get(s_info.input_field_entry);
       ret_if(!main_text || (strlen(main_text) == 0));
    
       ret = bt_socket_send_data(ad->socket_fd, main_text, strlen(main_text)+1);
       if (ret == -1) 
       {
          _E("[bt_socket_send_data] failed to send: %s", main_text);
          noti = bt_noti_popup_create(ad->navi, "Send Failed");
          if (!noti) 
          {
             _E("Failed to create popup noti");
          }
       } 
       else 
       {
          bubble_table = _bubble_table_create(s_info.bubble_box, MESSAGE_BUBBLE_SENT, 
                                              elm_entry_entry_get(s_info.input_field_entry), 
                                              _current_time_get());
          ret_if(!bubble_table);
    
          evas_object_show(bubble_table);
          elm_box_pack_end(s_info.bubble_box, bubble_table);
          elm_entry_entry_set(s_info.input_field_entry, "");
    
          evas_object_event_callback_add(s_info.bubble_box, EVAS_CALLBACK_RESIZE, 
                                         _bubble_box_resize_cb, NULL);
       }
    }
    

    When the user clicks SEND, the message in the input area is sent to the target device. If the sending succeeds, the application makes a message bubble for the sent message.

    static void 
    _send_button_clicked_cb(void *data, Evas_Object *obj, void *event_info)
    {
       appdata_s *ad = (appdata_s *)data;
       ret_if(!ad);
    
       _message_send(ad);
    }