Chronograph Watch Sample Overview

Wearable native

The Chronograph Watch sample application demonstrates how to create a circular watch with continuously moving hands. The Ecore Animator API is introduced to show the moving hands continuously.

This sample application includes only the watch functionality. It does not include any additional functionalities, such as a stopwatch.

The following figure illustrates the main screen of the Chronograph Watch.

Figure: Chronograph Watch screen

Chronograph Watch 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 directory contains the image files used in the main.edc file.
inc/chronograph.h This file contains information and definition of the variables and functions used in the C files, especially in the main.c 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/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 the style, images, and position of the sample application.
res/image/ This directory contains the image files used in the C files.
src/data.c This file contains the functions for retrieving and making 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:

  1. Create the basic layout using the view_create() function. The win variable is essential to make the application UI, and the bg variable is the main background image of the application.
    void
    view_create(void)
    {
        /* Create the watch window object */
        win = view_create_watch_window(width, height);
        if (win == NULL) {
            dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create a watch window");
    
            return;
        }
    
        /* Create the background object */
        data_get_resource_path(IMAGE_BG, bg_path, sizeof(bg_path));
        bg = view_create_bg(win, bg_path, width, height);
        if (bg == NULL) {
            dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create a bg");
    
            return;
        }
    }
    
  2. Create the chronograph layout using the .edj (.edc) file and make parts for the clock hands:
    /* Create a layout to display the chronograph view in the watch */
    data_get_resource_path(EDJ_FILE, edj_path, sizeof(edj_path));
    chronograph_layout = view_chronograph_create_layout(bg, edj_path);
    if (chronograph_layout == NULL) {
        dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create chronograph_layout");
    
        return;
    }
    
    /* Create hands and shadow hands to display in the watch  */
    for (i = 0 ;  i < PARTS_TYPE_NUM ; i++)
        _create_parts(target_parts[i]);
    
  3. In the _create_parts() function, the parts are created according to the predefined size and position in the chronograph.h file:
    /*
       @brief Create parts of watch
       @param[in] type Parts type
    */
    void
    _create_parts(parts_type_e type)
    {
        char *parts_image_path = NULL;
        int position_x = 0, position_y = 0;
        int size_w = 0, size_h = 0;
    
        /* Get the information about the part */
        parts_image_path = data_get_parts_image_path(type);
        data_get_parts_position(type, &position_x, &position_y);
        size_w = data_get_parts_width_size(type);
        size_h = data_get_parts_height_size(type);
    
        /* Create the part object */
        view_chronograph_create_parts(parts_image_path, position_x, position_y, size_w, size_h, type);
    
        free(parts_image_path);
    }
    

    You can adjust the size and position of the parts through the defined variables in the chronograph.h file.

    The shadow padding is needed to add reality to the watch view. The shadow hands are synchronized with the time hands. The pivot position can be changed.

    /* Layout */
    #define BASE_WIDTH 360
    #define BASE_HEIGHT 360
    
    #define HANDS_SEC_BG_SIZE 126
    #define HANDS_SEC_WIDTH 12
    #define HANDS_SEC_HEIGHT 126
    #define HANDS_MIN_WIDTH 28
    #define HANDS_MIN_HEIGHT 360
    #define HANDS_HOUR_WIDTH 28
    #define HANDS_HOUR_HEIGHT 360
    
    #define HANDS_SW_SEC_WIDTH 20
    #define HANDS_SW_SEC_HEIGHT 360
    
    #define HANDS_SEC_SHADOW_PADDING 3
    #define HANDS_MIN_SHADOW_PADDING 6
    #define HANDS_HOUR_SHADOW_PADDING 6
    
    #define HANDS_SEC_POSITION_Y 117
    
    #define HANDS_SEC_PIVOT_POSITION_X 93
    #define HANDS_SEC_PIVOT_POSITION_Y BASE_HEIGHT / 2
    
    #define HANDS_STOPWATCH_30S_PIVOT_POSITION_X BASE_WIDTH /2
    #define HANDS_STOPWATCH_30S_PIVOT_POSITION_Y 93
    #define HANDS_STOPWATCH_12H_PIVOT_POSITION_X BASE_WIDTH /2
    #define HANDS_STOPWATCH_12H_PIVOT_POSITION_Y 360-93
    

Clock Hands

To create the clock hands:

  1. The Ecore Animator API is a helper to simplify creating animations. This sample application uses the ecore_animator_add() function to run the animation for an unspecified amount of time because the time hands must move continuously when the watch face is activated.

    The following animator is controlled by the application. The view_chronograph_update_time variable is an actual animation drawing function, which is registered as an animator callback function.

    /* @brief Start the chronograph watch animation */
    void
    view_chronograph_animator_add(void)
    {
        s_info.animator = ecore_animator_add(view_chronograph_update_time, NULL);
    }
    
    /* @brief Resume the chronograph watch animation */
    void
    view_chronograph_animator_thaw(void)
    {
        if (s_info.animator)
            ecore_animator_thaw(s_info.animator);
    }
    
    /* @brief Pause the chronograph watch animation */
    void
    view_chronograph_animator_freeze(void)
    {
        if (s_info.animator)
            ecore_animator_freeze(s_info.animator);
    }
    
  2. When the application is created, the animator is created with the ecore_animator_add() function. After creating the animator, the application uses the created animator only. When the app_resume() function is called, the animator must be thawed and when the app_pause() function is called, the animator must be frozen for application performance and device resource saving.

    static void
    app_resume(void *user_data)
    {
        /* Take necessary actions when application becomes visible */
        view_chronograph_animator_thaw();
    }
    
    static void
    app_pause(void *user_data)
    {
        /* Take necessary actions when application becomes invisible */
        view_chronograph_animator_freeze();
    }
    
  3. The view_chronograph_update_time() function redraws the hands at every animator frame. The function gets the current time and calculates the position of each hand and finally redraws them in the calculated positions.

    /*
       @brief Update the time oh the chronograph watch
       @param[in] user_data The user data to be passed to the callback functions
    */
    Eina_Bool
    view_chronograph_update_time(void *data)
    {
        int msec = 0;
        int sec = 0;
        int min = 0;
        int hour = 0;
        double degree = 0.0f;
        int ret = 0;
        watch_time_h watch_time;
    
        /* Get the current time */
        ret = watch_time_get_current_time(&watch_time);
        if (ret != APP_ERROR_NONE) {
            dlog_print(DLOG_ERROR, LOG_TAG, "Failed to get current time. err = %d", ret);
    
            return EINA_TRUE;
        }
    
        watch_time_get_hour(watch_time, &hour);
        watch_time_get_minute(watch_time, &min);
        watch_time_get_second(watch_time, &sec);
        watch_time_get_millisecond(watch_time, &msec);
    
        /* Free the time data */
        watch_time_delete(watch_time);
    
        /* Rotate watch hands */
        degree = sec * SEC_ANGLE;
        degree += msec * SEC_ANGLE / 1000.0;
    
        view_rotate_hand(s_info.hands_sec, degree, HANDS_SEC_PIVOT_POSITION_X, HANDS_SEC_PIVOT_POSITION_Y);
        /* HANDS_SEC_PIVOT_POSITION_Y + HANDS_SEC_SHADOW_PADDING */
        view_rotate_hand(s_info.hands_sec_shadow, degree, HANDS_SEC_PIVOT_POSITION_X, 183);
    
        degree = min * MIN_ANGLE;
        degree += sec * MIN_ANGLE / 60.0;
    
        view_rotate_hand(s_info.hands_min, degree, (BASE_WIDTH / 2), (BASE_HEIGHT / 2));
        view_rotate_hand(s_info.hands_min_shadow, degree, (BASE_WIDTH / 2), (BASE_HEIGHT / 2) + HANDS_MIN_SHADOW_PADDING);
    
        degree = hour * HOUR_ANGLE;
        degree += min * HOUR_ANGLE / 60.0;
    
        view_rotate_hand(s_info.hands_hour, degree, (BASE_WIDTH / 2), (BASE_HEIGHT / 2));
        view_rotate_hand(s_info.hands_hour_shadow, degree, (BASE_WIDTH / 2), (BASE_HEIGHT / 2) + HANDS_HOUR_SHADOW_PADDING);
    
        return EINA_TRUE;
    }