Added support for changing locale dynamically 19/17019/1 submit/tizen/20140228.192930
authorJimmy Huang <jimmy.huang@intel.com>
Fri, 28 Feb 2014 19:15:27 +0000 (11:15 -0800)
committerJimmy Huang <jimmy.huang@intel.com>
Fri, 28 Feb 2014 19:21:13 +0000 (11:21 -0800)
- 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 <jimmy.huang@intel.com>
Makefile.am
dialer/gui.c
dialer/main.c
utils/i18n.c [new file with mode: 0644]
utils/i18n.h

index 6b0c393..cd5a83b 100644 (file)
@@ -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 \
index 4cf8e36..04bb454 100644 (file)
@@ -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);
index a8cc274..99b220e 100644 (file)
@@ -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 (file)
index 0000000..005f39f
--- /dev/null
@@ -0,0 +1,383 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <Elementary.h>
+#include <E_DBus.h>
+
+#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);
+}
index 907c417..aa0538a 100644 (file)
 #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