From: Jimmy Huang Date: Fri, 28 Feb 2014 19:15:27 +0000 (-0800) Subject: Added support for changing locale dynamically X-Git-Tag: submit/tizen/20140228.192930 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Ftags%2Fsubmit%2Ftizen%2F20140228.192930;p=profile%2Fivi%2Flemolo.git Added support for changing locale dynamically - Added support for monitoring dbus signals from localed for locale changes - Change locale dynamically through gettext by resetting LANG env Change-Id: Ia7450891fd5c37d84047fd5b4e489932285ccf9c Signed-off-by: Jimmy Huang --- diff --git a/Makefile.am b/Makefile.am index 6b0c393..cd5a83b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,6 +31,7 @@ utils_libofono_efl_utils_la_SOURCES = \ utils/amb.c \ utils/amb.h \ utils/contacts-ofono-efl.h \ + utils/i18n.c \ utils/i18n.h \ utils/log.h \ utils/ofono.c \ diff --git a/dialer/gui.c b/dialer/gui.c index 4cf8e36..04bb454 100644 --- a/dialer/gui.c +++ b/dialer/gui.c @@ -33,6 +33,7 @@ static Evas_Object *current_view = NULL; static OFono_Callback_List_Modem_Node *callback_node_modem_changed = NULL; static OFono_Callback_List_USSD_Notify_Node *callback_node_ussd_notify = NULL; static AMB_Callback_List_Node *callback_node_amb_properties_changed = NULL; +static Locale_Callback_List_Node *callback_node_locale_properties_changed = NULL; /* XXX elm_flip should just do the right thing, but it does not */ static Eina_Bool in_call = EINA_FALSE; @@ -286,6 +287,20 @@ static void _amb_properties_changed(void *data __UNUSED__) } } +static void _locale_properties_changed(void *data __UNUSED__) +{ + const char *new_lang = locale_lang_get(); + if (new_lang) { + if (strncmp(new_lang, "LANG=", 5) == 0) + new_lang += 5; + + DBG("Switching UI language to %s", new_lang); + setenv ("LANG", new_lang, 1); + setlocale (LC_ALL, new_lang); + _gui_set_text(); + } +} + static void _ofono_ussd_notify(void *data __UNUSED__, Eina_Bool needs_reply, const char *message) { @@ -406,6 +421,8 @@ Eina_Bool gui_init(void) ofono_ussd_notify_cb_add(_ofono_ussd_notify, NULL); callback_node_amb_properties_changed = amb_properties_changed_cb_add(_amb_properties_changed, NULL); + callback_node_locale_properties_changed = + locale_properties_changed_cb_add(_locale_properties_changed, NULL); /* TODO: make it match better with Tizen: icon and other properties */ obj = elm_layout_edje_get(lay); diff --git a/dialer/main.c b/dialer/main.c index a8cc274..99b220e 100644 --- a/dialer/main.c +++ b/dialer/main.c @@ -13,6 +13,7 @@ #include "amb.h" #include "gui.h" +#include "i18n.h" #include "log.h" #include "ofono.h" #include "pulseaudio.h" @@ -218,13 +219,19 @@ EAPI int elm_main(int argc, char **argv) _app_exit_code = EXIT_FAILURE; goto end_util; } + + if (!locale_init()) { + CRITICAL("Could not setup locale"); + _app_exit_code = EXIT_FAILURE; + goto end_amb; + } #endif if (!pa_init()) { CRITICAL("Could not setup pulseaudio"); _app_exit_code = EXIT_FAILURE; #ifdef HAVE_TIZEN - goto end_amb; + goto end_locale; #else goto end_util; #endif @@ -257,6 +264,8 @@ EAPI int elm_main(int argc, char **argv) end_pulseaudio: pa_shutdown(); #ifdef HAVE_TIZEN +end_locale: + locale_shutdown(); end_amb: amb_shutdown(); #endif diff --git a/utils/i18n.c b/utils/i18n.c new file mode 100644 index 0000000..005f39f --- /dev/null +++ b/utils/i18n.c @@ -0,0 +1,383 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include + +#include "i18n.h" +#include "log.h" + +typedef struct _Locale_Manager Locale_Manager; +typedef struct _Locale_Bus_Object Locale_Bus_Object; + +static const char bus_name[] = "org.freedesktop.locale1"; + +static E_DBus_Connection *bus_conn = NULL; +static char *bus_id = NULL; +static Locale_Manager *locale_manager = NULL; +static E_DBus_Signal_Handler *sig_locale_properties_changed = NULL; + +struct _Locale_Callback_List_Node +{ + EINA_INLIST; + void (*cb)(void *data); + const void *cb_data; +}; + +static Eina_Inlist *cbs_locale_connected = NULL; +static Eina_Inlist *cbs_locale_disconnected = NULL; +static Eina_Inlist *cbs_locale_properties_changed = NULL; + +#define LOCALE_IFACE "org.freedesktop.locale1" +#define LOCALE_BUS_PATH "/org/freedesktop/locale1" +#define LOCALE_PROPERTIES_IFACE "org.freedesktop.DBus.Properties" +#define PROP_LOCALE_STRING "Locale" + +struct _Locale_Bus_Object +{ + const char *path; + Eina_List *dbus_signals; /* of E_DBus_Signal_Handler */ +}; + +static void _bus_object_free(Locale_Bus_Object *o) +{ + E_DBus_Signal_Handler *sh; + + eina_stringshare_del(o->path); + + EINA_LIST_FREE(o->dbus_signals, sh) + e_dbus_signal_handler_del(bus_conn, sh); +} + +struct _Locale_Manager +{ + Locale_Bus_Object base; + const char *name; + unsigned int interfaces; + const char *lang; +}; + +static Locale_Manager *_locale_get(void) +{ + return locale_manager; +} + +static void _notify_locale_callbacks_list(Eina_Inlist *list) +{ + Locale_Callback_List_Node *node; + + EINA_INLIST_FOREACH(list, node) + node->cb((void *) node->cb_data); +} + +static void _locale_get_property_reply(void *data __UNUSED__, DBusMessage *msg, + DBusError *err) +{ + if (!msg) { + if (err) + ERR("%s: %s", err->name, err->message); + else + ERR("No message"); + return; + } + + EINA_SAFETY_ON_NULL_RETURN(data); + + DBusError e; + dbus_error_init(&e); + DBusMessageIter iter; + DBusMessageIter variant, array; + char *locale; + + if (dbus_message_iter_init(msg, &iter) == FALSE) { + ERR("Invalid dbus response"); + return; + } + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + ERR("Unexpected dbus response type"); + return; + } + + dbus_message_iter_recurse(&iter, &variant); + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY) { + ERR("Message does not contain property string"); + return; + } + + dbus_message_iter_recurse(&variant, &array); + if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRING) { + ERR("Message does not contain property string"); + return; + } + + dbus_message_iter_get_basic(&array, &locale); + if(!locale) { + ERR("Could not get property"); + return; + } + + char *property_name = (char*) data; + Locale_Manager *m = _locale_get(); + dbus_message_iter_recurse(&iter, &variant); + + if (strcmp(property_name, PROP_LOCALE_STRING) == 0) { + if (m->lang) + eina_stringshare_replace(&m->lang, locale); + else + m->lang = eina_stringshare_add(locale); + + DBG("Locale is set to: %s", m->lang); + } else { + return; + } + + _notify_locale_callbacks_list(cbs_locale_properties_changed); +} + +static void _locale_property_get(char *property, char *path, E_DBus_Method_Return_Cb cb, void *data) +{ + DBusMessage *msg; + char *interface_name; + + EINA_SAFETY_ON_NULL_RETURN(property); + EINA_SAFETY_ON_NULL_RETURN(path); + + if (strcmp(property, PROP_LOCALE_STRING) == 0) { + interface_name = LOCALE_IFACE; + } else { + return; + } + + msg = dbus_message_new_method_call( + bus_id, LOCALE_BUS_PATH, LOCALE_PROPERTIES_IFACE, "Get"); + + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &interface_name, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + + e_dbus_message_send(bus_conn, msg, cb, -1, data); + + dbus_message_unref(msg); +} + +static Locale_Manager *_locale_new(const char *path) +{ + Locale_Manager *m = calloc(1, sizeof(Locale_Manager)); + EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL); + + m->base.path = eina_stringshare_add(path); + EINA_SAFETY_ON_NULL_GOTO(m->base.path, error_path); + + return m; + +error_path: + free(m); + return NULL; +} + +static void _locale_free(Locale_Manager *m) +{ + if (m->lang) + eina_stringshare_del(m->lang); + + eina_stringshare_del(m->base.path); + + _bus_object_free(&m->base); + free(m); +} + +static void _locale_properties_changed(void *data __UNUSED__, DBusMessage *msg) +{ + DBusMessageIter iter, value; + const char *interface; + + if (!msg || !dbus_message_iter_init(msg, &iter)) { + ERR("Could not handle message %p", msg); + return; + } + + dbus_message_iter_get_basic(&iter, &interface); + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + DBG("Locale property changed at %s", interface); + if (strcmp(interface, LOCALE_IFACE) == 0) { + _locale_property_get(PROP_LOCALE_STRING, LOCALE_BUS_PATH, _locale_get_property_reply, PROP_LOCALE_STRING); + } +} + +static void _locale_load(void) +{ + locale_manager = _locale_new("/"); + EINA_SAFETY_ON_NULL_RETURN(locale_manager); +} + +static void _locale_connected(const char *id) +{ + free(bus_id); + bus_id = strdup(id); + + sig_locale_properties_changed = e_dbus_signal_handler_add( + bus_conn, bus_id, NULL, + LOCALE_PROPERTIES_IFACE, + "PropertiesChanged", + _locale_properties_changed, NULL); + + _locale_load(); + + _notify_locale_callbacks_list(cbs_locale_connected); +} + +static void _locale_disconnected(void) +{ + if (sig_locale_properties_changed) { + e_dbus_signal_handler_del(bus_conn, sig_locale_properties_changed); + sig_locale_properties_changed = NULL; + } + + if (bus_id) { + _notify_locale_callbacks_list(cbs_locale_disconnected); + free(bus_id); + bus_id = NULL; + } +} + +static void _name_owner_changed(void *data __UNUSED__, DBusMessage *msg) +{ + DBusError err; + const char *name, *from, *to; + + dbus_error_init(&err); + if (!dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &from, + DBUS_TYPE_STRING, &to, + DBUS_TYPE_INVALID)) { + ERR("Could not get NameOwnerChanged arguments: %s: %s", + err.name, err.message); + dbus_error_free(&err); + return; + } + + if (strcmp(name, bus_name) != 0) + return; + + DBG("NameOwnerChanged %s from=%s to=%s", name, from, to); + + if (from[0] == '\0' && to[0] != '\0') { + INF("localed appeared as %s", to); + _locale_connected(to); + } else if (from[0] != '\0' && to[0] == '\0') { + INF("localed disappeared from %s", from); + _locale_disconnected(); + } +} + +static void _locale_get_name_owner(void *data __UNUSED__, DBusMessage *msg, DBusError *err) +{ + DBusMessageIter itr; + const char *id; + + if (!msg) { + if (err) + ERR("%s: %s", err->name, err->message); + else + ERR("No message"); + return; + } + + dbus_message_iter_init(msg, &itr); + dbus_message_iter_get_basic(&itr, &id); + if (!id || id[0] == '\0') { + ERR("No name owner fo %s!", bus_name); + return; + } + + INF("localed bus id: %s", id); + _locale_connected(id); +} + +const char* locale_lang_get(void) +{ + Locale_Manager *m = _locale_get(); + EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE); + + return m->lang; +} + +Eina_Bool locale_init(void) +{ + if (!elm_need_e_dbus()) { + CRITICAL("Elementary does not support DBus."); + return EINA_FALSE; + } + + bus_conn = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!bus_conn) { + CRITICAL("Could not get DBus System Bus"); + return EINA_FALSE; + } + + e_dbus_signal_handler_add(bus_conn, E_DBUS_FDO_BUS, E_DBUS_FDO_PATH, + E_DBUS_FDO_INTERFACE, + "NameOwnerChanged", + _name_owner_changed, NULL); + + e_dbus_get_name_owner(bus_conn, bus_name, _locale_get_name_owner, NULL); + + return EINA_TRUE; +} + +void locale_shutdown(void) +{ + if (locale_manager) { + _locale_free(locale_manager); + locale_manager = NULL; + } + _locale_disconnected(); +} + +static Locale_Callback_List_Node * _locale_callback_list_node_create( + void (*cb)(void *data),const void *data) +{ + Locale_Callback_List_Node *node_new; + + node_new = calloc(1, sizeof(Locale_Callback_List_Node)); + EINA_SAFETY_ON_NULL_RETURN_VAL(node_new, NULL); + + node_new->cb_data = data; + node_new->cb = cb; + + return node_new; +} + +static void _locale_callback_list_delete(Eina_Inlist **list, + Locale_Callback_List_Node *node) +{ + EINA_SAFETY_ON_NULL_RETURN(*list); + *list = eina_inlist_remove(*list, EINA_INLIST_GET(node)); + free(node); +} + +Locale_Callback_List_Node * +locale_properties_changed_cb_add(void (*cb)(void *data), const void *data) +{ + Locale_Callback_List_Node *node; + + EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL); + node = _locale_callback_list_node_create(cb, data); + EINA_SAFETY_ON_NULL_RETURN_VAL(node, NULL); + + cbs_locale_properties_changed = eina_inlist_append(cbs_locale_properties_changed, + EINA_INLIST_GET(node)); + + return node; +} + +void locale_properties_changed_cb_del(Locale_Callback_List_Node *node) +{ + EINA_SAFETY_ON_NULL_RETURN(node); + _locale_callback_list_delete(&cbs_locale_properties_changed, node); +} diff --git a/utils/i18n.h b/utils/i18n.h index 907c417..aa0538a 100644 --- a/utils/i18n.h +++ b/utils/i18n.h @@ -10,4 +10,16 @@ #define N_(String) gettext_noop (String) #endif +typedef struct _Locale_Callback_List_Node Locale_Callback_List_Node; + +Locale_Callback_List_Node *locale_properties_changed_cb_add(void (*cb)(void *data), const void *data); + +void locale_properties_changed_cb_del(Locale_Callback_List_Node *callback_node); + +const char* locale_lang_get(void); + +Eina_Bool locale_init(void); + +void locale_shutdown(void); + #endif