Page Example

Bluetooth Chat Sample Overview

Mobile native

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

The following figure illustrates the screens of the sample application.

Figure: Bluetooth Chat screens

Bluetooth screens Bluetooth 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 Message Bubble UI sample application.

Implementation

After a button click, the application calls the bt_mgr_initialize() function, and checks whether Bluetooth is on or off. If Bluetooth is off, the application calls the Bluetooth ON/OFF application to switch Bluetooth on.

Figure: Bluetooth ON/OFF application

Bluetooth ON/OFF application Bluetooth ON/OFF application

If the bt_mgr_initialize() function is called and Bluetooth is on, the application draws proper layouts according to its current role (client or server):

void 
bt_mgr_initialize(void *data, bt_mgr_type type)
{
   appdata_s *ad = NULL;
   bt_adapter_state_e bt_ad_state = BT_ADAPTER_DISABLED;
   bt_adapter_visibility_mode_e bt_ad_visibility = BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE;
   int ret = 0;
   int duration = 1;

   ad = (appdata_s *)data;
   ret_if(!ad);

   ret = bt_initialize();
   ret_if(ret != BT_ERROR_NONE);

   ret = bt_adapter_get_state(&bt_ad_state);
   ret_if(ret != BT_ERROR_NONE);

   if (bt_ad_state == BT_ADAPTER_DISABLED) 
   {
      _onoff_operation();
   } 
   else 
   {
      switch (type) 
      {
         case BT_MGR_SEARCH:
            _search_layout_create(ad);
            break;
         case BT_MGR_WAIT:
            ret = bt_adapter_get_visibility(&bt_ad_visibility, &duration);
            if (ret != BT_ERROR_NONE) 
            {
               _E("Failed to get the adapter visibility");

               return;
            }

            if (bt_ad_visibility != BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE) 
            {
               _server_layout_create(ad);
            } 
            else 
            {
               _visibility_operation_set();
            }
            break;
      }
   }
}
static void 
_onoff_operation(void)
{
   int ret = 0;
   app_control_h service = NULL;

   app_control_create(&service);
   ret_if(!service);

   app_control_set_operation(service, "APP_CONTROL_OPERATION_SETTING_BT_ENABLE");
   ret = app_control_send_launch_request(service, NULL, NULL);
   if (ret != APP_CONTROL_ERROR_NONE) 
   {
      _E("Failed to relaunch Bluetooth On/off app");
   }

   app_control_destroy(service);
}

Client

To start the client application, click Search Friends. The client draws its own layout by the _search_layout_create() function, which is called by the bt_mgr_initialize() function. The layout is a list of the found devices.

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);
}

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

static void 
_discovery_start(appdata_s *ad)
{
   bt_error_e ret = BT_ERROR_NONE;

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

   ad->bt = TRUE;

   ret = bt_adapter_set_device_discovery_state_changed_cb(_adapter_discovery_state_changed_cb, ad);
   ret_if(ret != BT_ERROR_NONE);

   ret = bt_adapter_start_device_discovery();
   ret_if(ret != BT_ERROR_NONE);
}

static void 
_adapter_discovery_state_changed_cb(int result, bt_adapter_device_discovery_state_e discovery_state, 
                                    bt_adapter_device_discovery_info_s *discovery_info, void *user_data)
{
   appdata_s *ad = NULL;
   bt_adapter_device_discovery_info_s *new_device_info = NULL;
   int ret = 0;

   ad = (appdata_s *)user_data;
   ret_if(!ad);

   ret_if(!s_info.list);
   ret_if(result != BT_ERROR_NONE);

   switch (discovery_state) 
   {
      case BT_ADAPTER_DEVICE_DISCOVERY_STARTED:
         _D("BT_ADAPTER_DEVICE_DISCOVERY_STARTED");
         break;
      case BT_ADAPTER_DEVICE_DISCOVERY_FINISHED:
         _D("BT_ADAPTER_DEVICE_DISCOVERY_FINISHED");
         ret = bt_adapter_unset_device_discovery_state_changed_cb();
         if (ret != BT_ERROR_NONE) 
         {
            _E("[BT_ADAPTER_DEVICE_DISCOVERY_FINISHED] Failed to unset the state discovery cb");

            return;
         }
         ad->bt = FALSE;
         break;
      case BT_ADAPTER_DEVICE_DISCOVERY_FOUND:
         _D("BT_ADAPTER_DEVICE_DISCOVERY_FOUND");
         if (discovery_info != NULL && s_info.list != NULL) 
         {
            new_device_info = malloc(sizeof(bt_adapter_device_discovery_info_s));
            if (new_device_info != NULL) 
            {
               _D("Device Name is: %s", discovery_info->remote_name);
               memcpy(new_device_info, discovery_info, sizeof(bt_adapter_device_discovery_info_s));
               new_device_info->remote_address = strdup(discovery_info->remote_address);
               new_device_info->remote_name = strdup(discovery_info->remote_name);
               elm_list_item_append(s_info.list, new_device_info->remote_name, 
                                    NULL, NULL, _click_friend_item_cb, new_device_info);
               elm_list_go(s_info.list);
            }
         }
         break;
   }
}

If an item on the list is clicked, the application starts to bond with the target device, and requests to connect to the target server:

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;
         elm_naviframe_item_pop(ad->navi);
         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;
}

When the application is successfully connected with the target, the _socket_conn_state_changed_cb() function is called and the application moves to the chat room layout by calling the _bt_chat_room_layout_create() function.

Server

To start the server application, click Wait a friend. The role of this application is making a server for chatting, and waiting for a client. Before drawing the proper layout, the application checks whether the device is discoverable by using the _search_layout_create() function. If the device is not discoverable, the application calls the Bluetooth Visibility application.

static void 
_visibility_operation_set(void)
{
   app_control_h service = NULL;
   int ret = 0;

   app_control_create(&service);
   ret_if(!service);

   app_control_set_operation(service, "APP_CONTROL_OPERATION_SETTING_BT_VISIBILITY");
   ret = app_control_send_launch_request(service, NULL, NULL);
   if (ret != APP_CONTROL_ERROR_NONE) 
   {
      _E("Failed to relaunch Bluetooth Visibility app");
   }

   app_control_destroy(service);
}

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

static void 
_server_layout_create(appdata_s *ad)
{
   Evas_Object *layout = NULL;
   Evas_Object *progress = 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_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);

   progress = elm_progressbar_add(layout);
   goto_if(!progress, ERROR);
   elm_object_style_set(progress, "process_large");
   evas_object_size_hint_min_set(progress, 100, 100);
   evas_object_size_hint_align_set(progress, 0.5, 0.5);
   evas_object_size_hint_weight_set(progress, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   elm_progressbar_pulse(progress, EINA_TRUE);

   elm_object_part_content_set(layout, "progress", progress);

   elm_naviframe_item_push(ad->navi, "Wait a Friend", NULL, NULL, layout, NULL);

   _server_create(ad);

   return;

   ERROR:
      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 *main_scroller = NULL;
       Evas_Object *input_field_table = NULL;
    
       retv_if(!ad, NULL);
    
       main_scroller = elm_scroller_add(ad->navi);
       goto_if(!main_scroller, ERROR);
       elm_scroller_bounce_set(main_scroller, EINA_FALSE, EINA_TRUE);
       evas_object_size_hint_weight_set(main_scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_size_hint_align_set(main_scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
       evas_object_show(main_scroller);
    
       s_info.main_box = elm_box_add(main_scroller);
       goto_if(!s_info.main_box, ERROR);
       elm_box_align_set(s_info.main_box, 0, 0);
       evas_object_size_hint_weight_set(s_info.main_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       evas_object_show(s_info.main_box);
    
       s_info.bubble_scroller = elm_scroller_add(s_info.main_box);
       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);
    
       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_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);
       evas_object_show(s_info.bubble_scroller);
       elm_box_pack_end(s_info.main_box, s_info.bubble_scroller);
    
       input_field_table = _input_field_table_create(ad);
       goto_if(!input_field_table, ERROR);
       evas_object_show(input_field_table);
       elm_box_pack_end(s_info.main_box, input_field_table);
       elm_object_content_set(main_scroller, s_info.main_box);
    
       return main_scroller;
    
       ERROR:
          if (main_scroller) 
          {
             evas_object_del(main_scroller);
             main_scroller = NULL;
          }
    
          if (input_field_table) 
          {
             evas_object_del(input_field_table);
             input_field_table = 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", EINA_FALSE);
       }
    }
    
  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;
    }
    

    The chat room is created by calling the bt_chat_room_layout_create() function. The function draws the layout for chatting, and unsets the connection state change callback and reregisters it. It also registers the data received callback. When the message is received from the target, the application makes a message bubble for a new message.

    void 
    bt_chat_room_layout_create(appdata_s *ad)
    {
       Evas_Object *main_scroller = 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);
       elm_naviframe_item_push(ad->navi, "Chatting", NULL, NULL, main_scroller, NULL);
    }
    
  4. 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", EINA_FALSE);
          evas_object_show(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 on the input area is sent to the target device. If sending is successful, the application makes a message bubble for the sent message.