Page Example

(Circle) Dialer Sample Overview

Wearable native

The (Circle) Dialer sample application demonstrates how you can create a circular dialer and use the Entry UI component to show the digits entered by the user. It shows how to hijack upper layer touch events to determine, without a button, which digits the user has touched.

With this application, you can understand a simple circular dialer view and touch event control techniques. The application does not include call functions or companion mode operation.

For information on creating the sample application project in the IDE, see Creating Sample Applications.

The following figure illustrates the main screen of the (Circle) Dialer.

Figure: (Circle) Dialer screen

(Circle) Dialer screen

On the application screen, the user can dial a phone number by touching the digits at the edge of the screen.

Source Files

You can create and view the sample application project including the source files in the IDE.

Table: Source files
File name Description
edje/images This file contains the image files used in the main.edc file.
inc/data.h This file contains information and definition of the variables and functions used in the C files, especially in the data.c file.
inc/dialer.h This file contains information and definition of the variables and functions used in the C files, especially in the main.c file.
inc/view.h This file contains information and definition of the variables and functions used in the C files, especially in the view.c file.
res/edje/main.edc This file is for the UI and contains style, image, and position of the sample application.
res/images This directory contains the image files used in the C files.
src/data.c This file contains the functions for retrieving and creating data for the application.
src/main.c This file contains the functions related to the application life-cycle, callback functions, and view control.
src/view.c This file contains the functions for implementing the views and handling events.

Implementation

Application Layout

To create the basic application layout, use the view_create() function. The window and conformant components are essential parts of the application layout.

void 
view_create(void)
{
   // Create a window 
   s_info.win = view_create_win(PACKAGE);
   if (s_info.win == NULL) 
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a window.");

      return;
   }

   // Create a conformant 
   s_info.conform = view_create_conformant_without_indicator(s_info.win);
   if (s_info.conform == NULL) 
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a conformant");

      return;
   }

   // Show the window after the main view is set up 
   evas_object_show(s_info.win);
}

To create a layout with the EDJ file, use the view_dialer_create() function:

void 
view_dialer_create(const char *file_path)
{
   s_info.layout = view_create_layout_for_conformant(s_info.conform, file_path, GRP_MAIN, 
                                                     _dialer_layout_cb, NULL);
   if (s_info.layout == NULL) 
   {
      evas_object_del(s_info.win);
      dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a content.");

      return;
   }
}

Main View

The main view contains digit buttons, function image buttons (Call and Delete), and a digit input field showing the selected digits.

Figure: Main view

Main view

The structure of the main view is very simple. The circular positioning text and digits are merged with a background image. The result is a screen where the user feels like they are touching a button at the edge of the dial, while, in the actual implementation, the button is just an image and text. For the details of this kind of trick button implementation, see Making a Digit Button.

Figure: Main view structure

Main view structure

To implement the main view:

  • Set the background image and the image buttons using the view_set_image() and view_set_color() functions:
    // main.c
    // Set the background image to the "sw.button.bg" part of EDC 
    image = data_get_image_path("sw.button.bg");
    view_set_image(view_dialer_get_layout_object(), "sw.button.bg", image);
    view_set_color(view_dialer_get_layout_object(), "sw.button.bg", 8, 36, 61, 255);
    
    image = data_get_image_path("sw.button.call");
    view_set_image(view_dialer_get_layout_object(), "sw.button.call", image);
    view_set_color(view_dialer_get_layout_object(), "sw.button.call", 0, 214, 46, 255);
    
    image = data_get_image_path("sw.button.delete");
    view_set_button(view_dialer_get_layout_object(), "sw.button.delete", "focus", 
                    image, NULL, _btn_down_cb, _btn_up_cb, NULL, NULL);
    view_set_color(view_dialer_get_layout_object(), "sw.button.delete", 250, 250, 250, 255);
    
  • To do something when the button goes down or up, customize the _btn_down_cb() and _btn_up_cb() callbacks, or create another function for the action.

    In this example, the _btn_down_cb() and _btn_up_cb() callbacks handle the touch events of the Delete button. When the user touches the Delete button, the _btn_down_cb() callback changes the color of the button and removes a digit in the digit input field, while the _btn_up_cb() callback returns the button color to normal.

    static void 
    _btn_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
    {
       dlog_print(DLOG_DEBUG, LOG_TAG, "button is pressed.");
    
       // Delete an input character 
       view_dialer_set_entry_text(ENTRY_TEXT_BACKSPACE, NULL);
    
       // Change the button color 
       evas_object_color_set(obj, 250, 250, 250, 102);
    }
    
    static void 
    _btn_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
    {
       dlog_print(DLOG_DEBUG, LOG_TAG, "button is released.");
    
       // Change the button color 
       evas_object_color_set(obj, 250, 250, 250, 255);
    }
  • To implement the digit input field in the center of the screen, use the EFL Entry component. The EFL entry is a convenient UI component that shows a box in which the user can enter text.

    The view_create_entry() function creates an entry component and returns the component object. The object is needed to control the entry component, for example, to delete its text or change the text style.

    void 
    view_dialer_set_entry(const char *part_name)
    {
    
       if (s_info.layout == NULL) 
       {
          dlog_print(DLOG_ERROR, LOG_TAG, "parent layout is NULL.");
    
          return;
       }
    
       if (part_name == NULL) 
       {
          dlog_print(DLOG_ERROR, LOG_TAG, "part name is NULL.");
    
          return;
       }
    
       s_info.entry = view_create_entry(s_info.layout, part_name, NULL, NULL);
    }

Making a Digit Button

The trick digit button in the dialer is implemented touch event hijacking. The button is actually just images and texts as shown in the main view structure. You can transform those images and text into a trick button by hijacking user touch events.

Figure: Transparent layer for touch event hijacking

Transparent layer for touch event hijacking

To implement touch event hijacking:

  1. Create a transparent layer for touch event handling
    • The evas_object_rectangle_add() function creates a rectangle object on the canvas.
    • The evas_object_repeat_events_set() function enables the touch events to be repeated on the lower layers. Event repetition is very important to correctly handle touch events for each layer.
    • Register callback functions using the evas_object_event_callback_add() function.
    // Create a rectangle object to the target window for hijacking touch events
    Evas_Object 
    *view_dialer_create_rectangle()
    {
       Evas_Object *rect = NULL;
    
       // Add the rectangle object to parent 
       rect = evas_object_rectangle_add(evas_object_evas_get(s_info.layout));
       if (rect == NULL) 
       {
          dlog_print(DLOG_ERROR, LOG_TAG, "failed to add a rectangle");
    
          return NULL;
       }
    
       // Set the color to transparent
       // You can change the color for other purposes, such as a color filter layer 
       evas_object_color_set(rect, 255, 255, 255, 0);
    
       // Set the event repeat mode 
       evas_object_repeat_events_set(rect, EINA_TRUE);
    
       // Set the size of the object 
       evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       elm_win_resize_object_add(s_info.win, rect);
    
       evas_object_show(rect);
    
       // Set a callback for object events 
       evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_DOWN, _rectangle_mouse_down_cb, NULL);
       evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_UP, _rectangle_mouse_up_cb, NULL);
       evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_MOVE, _rectangle_mouse_move_cb, NULL);
    
       return rect;
    }
  2. You must calculate the position of the touch events that the transparent layer catches on the canvas. Events are handled or discarded according to the position of the touch event.

    In the calculation, check the radius and slope. The radius from the center of the screen to the position of the touch event must be longer than the distance from the center to the button image. If the radius is shorter than the threshold, the touch event is ignored and just repeated on the lower layers.

    To determine the correct digit being touched, use the slope of the touch event position. For example, in the following figure, if the slope of some event position is higher than line 1's slope and lower than line 2's slope, you can determine that the digit 1 button is being touched.

    Calculating digit positions

    The _rectangle_mouse_down_cb() callback is called with the touch event information from the transparent layer. The _get_btn_dial_number() function is used to calculate the position of the digit by checking the radius and slope.

    static void 
    _rectangle_mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
    {
       Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down*) event_info;
       s_info.mouse_down_dial_num = _get_btn_dial_number(ev->output.x, ev->output.y);
    }
    
    // Get the dial number of the touch event position by calculating distance and slope
    static int 
    _get_btn_dial_number(int evt_x, int evt_y)
    {
       int result = -1;
    
       // Calculate x and y from the CENTER_REF point 
       int x = evt_x - CENTER_REF_X;
       int y = CENTER_REF_Y - evt_y;
    
       // Calculate the slope and radius from the CENTER_REF point 
       float slope = (float)y / (float)x;
       float radius = sqrt(x*x + y*y);
    
       if (radius > RADIUS_MIN) 
       {
          if (x == 0) 
          {
             if (y >= 0)
                result = 0;
             else
                result = 5;
          } 
          else if (y == 0) 
          {
             if (x >= 0)
                result = 2;
             else
                result = 8;
          } 
          else 
          {
             if (slope > SLOPE_72_DEGREE) 
             {
                if (y > 0)
                   result = 0;
                else
                   result = 5;
             } 
             else if (slope > SLOPE_36_DEGREE) 
             {
                if (y > 0)
                   result = 1;
                else
                   result = 6;
             } 
             else if (slope > SLOPE_180_DEGREE) 
             {
                if (y > 0)
                   result = 2;
                else
                   result = 7;
             } 
             else if (slope > SLOPE_144_DEGREE) 
             {
                if (y > 0)
                   result = 8;
                else
                   result = 3;
             } 
             else if (slope > SLOPE_108_DEGREE) 
             {
                if (y > 0)
                   result = 9;
                else
                   result = 4;
             } 
             else 
             {
                if (y > 0)
                   result = 0;
                else
                   result = 5;
             }
          }
       } 
       else 
       {
          dlog_print(DLOG_DEBUG, LOG_TAG, "Ignore touch event under min radius");
       }
    
       return result;
    }