Wearable native

The Ambient Weather sample application demonstrates how you can implement a simple watch application that displays current weather information.

The following figure illustrates the main screen of the Ambient Weather.

Figure: Ambient Weather screen

Ambient Weather screen

The application shows current weather information for a detected location of the device as well as the current date. The white dot on the outer border of the screen indicates the current time.

The application is multi-threaded. The time-consuming task of requesting the weather data runs in a separate thread to avoid UI lockups. The following figure shows the weather data request diagram.

Figure: Weather data request diagram

Weather data request diagram

Prerequisites

To ensure proper application execution, the following privileges must be set:

  • http://tizen.org/privilege/location
  • http://tizen.org/privilege/network.get
  • http://tizen.org/privilege/keymanager
  • http://tizen.org/privilege/internet
  • http://tizen.org/privilege/alarm.set

Implementation

The main() function of the sample application starts the application loop with the watch_app_main() function:

  1. The watch_app_lifecycle_callback_s type object is passed to the application loop with the watch_app_main() function. The object differs from the ui_app_lifecycle_callback_s type used in the standard application type, because it introduces 3 new pointers to the following callback functions:

    • watch_app_time_tick_cb(): Called every second when the application is in the normal mode, giving the application a chance to refresh the UI.
    • watch_app_ambient_tick_cb(): Called every minute when the application is in the ambient mode, giving the application a chance to refresh the UI.
    • watch_app_ambient_changed_cb(): Called whenever the application enters the normal or ambient mode.
    int
    main(int argc, char *argv[])
    {
        int ret = 0;
    
        watch_app_lifecycle_callback_s event_callback = {0,};
    
        event_callback.create = app_create;
        event_callback.terminate = app_terminate;
        event_callback.pause = app_pause;
        event_callback.resume = app_resume;
        event_callback.app_control = app_control;
        event_callback.time_tick = app_time_tick;
        event_callback.ambient_tick = app_ambient_tick;
        event_callback.ambient_changed = app_ambient_changed;
    
        ret = watch_app_main(argc, argv, &event_callback, NULL);
        /* Error handling */
    
        return ret;
    }
    
    void
    app_time_tick(watch_time_h watch_time, void* user_data)
    {
        _set_date_time(watch_time);
    }
    
    void
    app_ambient_tick(watch_time_h watch_time, void* user_data)
    {
        _set_date_time(watch_time);
    }
    
    void
    app_ambient_changed(bool ambient_mode, void* user_data)
    {
        view_toggle_clouds(!ambient_mode);
    }
    
  2. The _set_date_time() function is called every time the time and date view needs to be displayed, either from the app_time_tick() or app_ambient_tick() function:

    static void
    _set_date_time(watch_time_h watch_time)
    {
        date_time_t dt = {0,};
    
        data_get_date_time_from_watch_time(watch_time, &dt);
    
        view_set_time(dt.hour, dt.minute);
        view_set_date(dt.day, dt.month, dt.year, dt.day_of_week);
    }
    
  3. The data_get_date_time_from_watch_time() function is invoked to get the current time set on the device. It is used to set the proper time and date displayed by the application.

    void
    data_get_date_time_from_watch_time(const watch_time_h watch_time, date_time_t *dt)
    {
        watch_time_get_hour24(watch_time, &dt->hour);
        watch_time_get_minute(watch_time, &dt->minute);
        watch_time_get_second(watch_time, &dt->second);
        watch_time_get_year(watch_time, &dt->year);
        watch_time_get_month(watch_time, &dt->month);
        watch_time_get_day(watch_time, &dt->day);
        watch_time_get_day_of_week(watch_time, &dt->day_of_week);
    }
    

Weather Poll Thread Module

To poll the weather information from the server:

  1. When the weather poll thread module is initialized, it introduces a single ecore_timer instance, whose callback is launched periodically to execute the server data poll function in a separate thread:
    bool
    weather_poll_thread_init(weather_poll_data_ready_cb cb)
    {
        /* Error handling */
    
        s_info.cb = cb;
    
        s_info.tim = ecore_timer_add(1.0, __weather_poll_timer_cb, NULL);
    
        /* Error handling */
    
        return true;
    }
    
    static Eina_Bool
    __weather_poll_timer_cb(void *data)
    {
        ecore_timer_interval_set(s_info.tim, WEATHER_POLL_INTERVAL_SEC);
    
        if (!s_info.thread_h) {
            s_info.thread_h = ecore_thread_run(__thread_function, __thread_end_cb, __thread_cancelled_cb, NULL);
    
            /* Error handling */
        }
    
        return EINA_TRUE;
    }
    
  2. Both the __thread_function() and __weather_data_read_cb() functions are executed in a separate thread. Since the weather_query() function blocks until it is finished, its execution from the main thread locks up the UI.
    static void
    __thread_function(void *data, Ecore_Thread *thread)
    {
        if (!weather_query(s_info.geo_location.latitude, s_info.geo_location.longitude, __weather_data_read_cb))
            /* Error handling */
    }
    
  3. Any UI update function calls from the __weather_data_read_cb() callback are prohibited. The callback only stores pointers to received data in a common memory area.
    static void
    __weather_data_read_cb(xml_weather_t *weather)
    {
        s_info.weather = weather;
    }
    
  4. The __thread_end_cb() callback is called automatically when the __thread_function() function finishes successfully. The callback is called from the main thread, and this is why it can call a callback from the main controller (updating the UI). The server data pointers, previously stored by the __weather_data_read_cb() callback, are used.
    static void
    __thread_end_cb(void *data, Ecore_Thread *thread)
    {
        if (s_info.cb)
            s_info.cb(s_info.weather);
    
        data_dispose_weather(s_info.weather);
        s_info.weather = NULL;
        s_info.thread_h = NULL;
    }
    
  5. The __weather_poll_data_ready_cb() callback is called in the main thread context whenever the weather polling thread ends its work successfully. This function updates the UI by displaying the data received from the server.
    static void
    __weather_poll_data_ready_cb(xml_weather_t *weather, const void *icon_data, int icon_data_len)
    {
        if (weather) {
            view_set_temperature(weather->temperature.value, weather->temperature.unit);
            view_set_city(weather->city.name, weather->city.country);
            view_set_humidity(weather->humidity.value, weather->humidity.unit);
            view_set_pressure(weather->pressure.value, weather->pressure.unit);
            view_set_wind(weather->wind.name, weather->wind.speed_value, weather->wind.direction_name);
            view_set_clouds(weather->clouds.name);
            view_set_precipitation(weather->precipitation.value);
            view_set_last_update(asctime(&weather->last_update));
        }
    
        if (icon_data && icon_data_len > 0)
            view_set_icon(icon_data, icon_data_len);
    }
    

View Module

The view module handles the time tick events and mode changes:

  • The watch_app_get_elm_win() function is used to obtain a special watch type window. This way the application can receive the ambient and normal mode switch events properly.
    void
    view_create(void)
    {
        s_info.win = view_create_win(PACKAGE);
        /* Error handling */
    
        s_info.layout = _create_layout();
        /* Error handling */
    
        s_info.image = _create_image(PART_IMAGE);
        /* Error handling */
    
        if (!_create_clouds())
        /* Error handling */
    
        evas_object_show(s_info.win);
    }
    
  • Each time a time tick occurs, the main module calls the view_set_time() function. It updates the time indicator displayed on the outer border of the screen.
    void
    view_set_time(int hour, int minute)
    {
        Edje_Message_Int_Set *msg = NULL;
    
        /* Error handling */
    
        msg = (Edje_Message_Int_Set *)malloc(sizeof(Edje_Message_Int_Set) + 2 * sizeof(int));
    
        /* Error handling */
    
        if (hour >= 12)
            hour -= 12;
    
        msg->count = 2;
        msg->val[0] = hour;
        msg->val[1] = minute;
    
        edje_object_message_send(elm_layout_edje_get(s_info.layout), EDJE_MESSAGE_INT_SET, MSG_ID_TIME, msg);
    }
    
  • The view_toggle_clouds() function is called each time the application mode changes. The function toggles the visibility of the cloud animation, which is disabled for power saving reasons in the ambient mode.
    void
    view_toggle_clouds(bool visible)
    {
        if (visible) {
            _emit_signal(PART_CLOUD_1, SIGNAL_CLOUDS_ANIMATION_RESET);
            _emit_signal(PART_CLOUD_2, SIGNAL_CLOUDS_ANIMATION_RESET);
            _emit_signal(PART_CLOUD_3, SIGNAL_CLOUDS_ANIMATION_RESET);
        } else {
            _emit_signal(PART_CLOUD_1, SIGNAL_CLOUDS_ANIMATION_STOP);
            _emit_signal(PART_CLOUD_2, SIGNAL_CLOUDS_ANIMATION_STOP);
            _emit_signal(PART_CLOUD_3, SIGNAL_CLOUDS_ANIMATION_STOP);
        }
    }