[Time] Listeners
authorPawel Kaczmarek <p.kaczmarek3@samsung.com>
Tue, 13 Jan 2015 10:48:07 +0000 (11:48 +0100)
committerRafal Galka <r.galka@samsung.com>
Tue, 13 Jan 2015 12:37:52 +0000 (21:37 +0900)
[Verification]
TCT Summary:
Test cases all: 475
Test cases passed: 468
Test cases failed: 3
Test cases timed out: 4
Test cases not run: 0

All listeners are working correctly.
4 TCT (timeouts) should be tested manually:
Listener set with setTimezoneChangeListener() method is called when device time zone has changed.
Listener set with setDateTimeChangeListener method is called when device time was set by the user.
It is not invoked when time changes.

Change-Id: I45f8febdb3b37ed921de24539482e4d564e61064
Signed-off-by: Pawel Kaczmarek <p.kaczmarek3@samsung.com>
src/time/time_api.js
src/time/time_instance.cc
src/time/time_instance.h

index 3b106d4ef0fce56425475b9828e6093c531e784a..89fb57dab57e885925bd6d4729b0ac27e3ec616b 100644 (file)
@@ -65,6 +65,71 @@ exports.isLeapYear = function(year) {
   return false;
 };
 
+var _timeUtilDateTimeChangeListener;
+
+function _timeUtilDateTimeChangeListenerCallback() {
+  native_.callIfPossible(_timeUtilDateTimeChangeListener);
+}
+
+exports.setDateTimeChangeListener = function() {
+  var args = AV.validateArgs(arguments, [
+    {
+      name: 'changeCallback',
+      type: AV.Types.FUNCTION
+    }
+  ]);
+  _timeUtilDateTimeChangeListener = args.changeCallback;
+  native_.addListener('DateTimeChangeListener',
+      _timeUtilDateTimeChangeListenerCallback);
+  var result = native_.callSync('Time_setDateTimeChangeListener', {});
+  if (native_.isFailure(result)) {
+    throw native_.getErrorObject(result);
+  }
+};
+
+exports.unsetDateTimeChangeListener = function() {
+  native_.removeListener('DateTimeChangeListener',
+      _timeUtilDateTimeChangeListenerCallback);
+  var result = native_.callSync('Time_unsetDateTimeChangeListener', {});
+  _timeUtilDateTimeChangeListener = undefined;
+  if (native_.isFailure(result)) {
+    throw native_.getErrorObject(result);
+  }
+};
+
+var _timeUtilTimezoneChangeListener;
+
+function _timeUtilTimezoneChangeListenerCallback() {
+  native_.callIfPossible(_timeUtilTimezoneChangeListener);
+}
+
+exports.setTimezoneChangeListener = function() {
+  var args = AV.validateArgs(arguments, [
+    {
+      name: 'changeCallback',
+      type: AV.Types.FUNCTION
+    }
+  ]);
+
+  _timeUtilTimezoneChangeListener = args.changeCallback;
+  native_.addListener('TimezoneChangeListener',
+      _timeUtilTimezoneChangeListenerCallback);
+  var result = native_.callSync('Time_setTimezoneChangeListener', {});
+  if (native_.isFailure(result)) {
+    throw native_.getErrorObject(result);
+  }
+};
+
+exports.unsetTimezoneChangeListener = function() {
+  native_.removeListener('TimezoneChangeListener',
+      _timeUtilTimezoneChangeListenerCallback);
+  var result = native_.callSync('Time_unsetTimezoneChangeListener', {});
+  _timeUtilTimezoneChangeListener = undefined;
+  if (native_.isFailure(result)) {
+    throw native_.getErrorObject(result);
+  }
+};
+
 function _throwProperTizenException(e) {
   if (e instanceof TypeError)
     throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
@@ -90,10 +155,10 @@ tizen.TimeDuration = function(length, unit) {
 
   Object.defineProperties(this, {
     length: {
-      get: function () {
+      get: function() {
         return length_;
       },
-      set: function (v) {
+      set: function(v) {
         if (v !== null) {
           length_ = Math.floor(v);
         }
@@ -101,10 +166,10 @@ tizen.TimeDuration = function(length, unit) {
       enumerable: true
     },
     unit: {
-      get: function () {
+      get: function() {
         return unit_;
       },
-      set: function (v) {
+      set: function(v) {
         if (TimeDurationUnit.indexOf(v) >= 0) {
           unit_ = v;
         }
@@ -668,9 +733,9 @@ tizen.TZDate.prototype.isDST = function() {
 
 tizen.TZDate.prototype.getPreviousDSTTransition = function() {
   var result = native_.callSync('Time_getDSTTransition', {
-    "timezone": this.timezone_,
-    "value": _getTimeWithOffset(this.date_),
-    "trans": 'NEXT_TRANSITION'
+    'timezone': this.timezone_,
+    'value': _getTimeWithOffset(this.date_),
+    'trans': 'NEXT_TRANSITION'
   });
   if (native_.isFailure(result)) {
     return null;
index daf19765e48a9ab23c70483044a241f47e568f6e..0f069d015746af1752441fff8564744ac0cb702d 100644 (file)
@@ -4,6 +4,8 @@
 // found in the LICENSE file.
 
 #include "time/time_instance.h"
+#include "common/platform_exception.h"
+#include "common/logger.h"
 
 #if defined(TIZEN)
 #include <vconf.h>
 namespace extension {
 namespace time {
 
+using namespace common;
+
+enum ListenerType {
+  kTimeChange,
+  kTimezoneChange
+};
+
 namespace {
 
 const int _hourInMilliseconds = 3600000;
+const char kTimezoneListenerId[] = "TimezoneChangeListener";
+const char kDateTimeListenerId[] = "DateTimeChangeListener";
 
 }  // namespace
 
 TimeInstance& TimeInstance::GetInstance() {
-    static TimeInstance instance;
-    return instance;
+  static TimeInstance instance;
+  return instance;
 }
 
 TimeInstance::TimeInstance() {
@@ -54,11 +65,18 @@ TimeInstance::TimeInstance() {
   REGISTER_SYNC("Time_toString", Time_toString);
   REGISTER_SYNC("Time_toDateString", Time_toDateString);
   REGISTER_SYNC("Time_toTimeString", Time_toTimeString);
+  REGISTER_SYNC("Time_setDateTimeChangeListener", Time_setDateTimeChangeListener);
+  REGISTER_SYNC("Time_unsetDateTimeChangeListener", Time_unsetDateTimeChangeListener);
+  REGISTER_SYNC("Time_setTimezoneChangeListener", Time_setTimezoneChangeListener);
+  REGISTER_SYNC("Time_unsetTimezoneChangeListener", Time_unsetTimezoneChangeListener);
 
 #undef REGISTER_SYNC
 #undef REGISTER_ASYNC
 }
 
+static void OnTimeChangedCallback(keynode_t* /*node*/, void* /*event_ptr*/);
+static std::string GetDefaultTimezone();
+
 TimeInstance::~TimeInstance() {}
 
 void TimeInstance::Time_getLocalTimeZone(const JsonValue& /*args*/,
@@ -83,7 +101,7 @@ void TimeInstance::Time_getAvailableTimeZones(const JsonValue& /*args*/,
   }
 
   JsonArray a;
-  const char *timezone = NULL;
+  const chartimezone = NULL;
   int i = 0;
   do {
     int32_t resultLen = 0;
@@ -92,7 +110,7 @@ void TimeInstance::Time_getAvailableTimeZones(const JsonValue& /*args*/,
       a.push_back(JsonValue(timezone));
       i++;
     }
-  }while(timezone && i < count);
+  } while (timezone && i < count);
 
   ReportSuccess(JsonValue(a), out);
 }
@@ -100,7 +118,7 @@ void TimeInstance::Time_getAvailableTimeZones(const JsonValue& /*args*/,
 void TimeInstance::Time_getTimeZoneOffset(const JsonValue& args,
                                           JsonObject& out) {
   std::unique_ptr<UnicodeString> id(
-    new UnicodeString(args.get("timezone").to_str().c_str()));
+      new UnicodeString(args.get("timezone").to_str().c_str()));
   UDate dateInMs = strtod(args.get("value").to_str().c_str(), NULL);
 
   if (errno == ERANGE) {
@@ -124,8 +142,7 @@ void TimeInstance::Time_getTimeZoneOffset(const JsonValue& args,
 
   int32_t offset = timezone->getRawOffset();
 
-  if (cal->inDaylightTime(ec))
-    offset += _hourInMilliseconds;
+  if (cal->inDaylightTime(ec)) offset += _hourInMilliseconds;
 
   std::stringstream offsetStr;
   offsetStr << offset;
@@ -136,7 +153,7 @@ void TimeInstance::Time_getTimeZoneOffset(const JsonValue& args,
 void TimeInstance::Time_getTimeZoneAbbreviation(const JsonValue& args,
                                                 JsonObject& out) {
   std::unique_ptr<UnicodeString> id(
-    new UnicodeString(args.get("timezone").to_str().c_str()));
+      new UnicodeString(args.get("timezone").to_str().c_str()));
   UDate dateInMs = strtod(args.get("value").to_str().c_str(), NULL);
 
   if (errno == ERANGE) {
@@ -146,7 +163,7 @@ void TimeInstance::Time_getTimeZoneAbbreviation(const JsonValue& args,
 
   UErrorCode ec = U_ZERO_ERROR;
   std::unique_ptr<Calendar> cal(
-    Calendar::createInstance(TimeZone::createTimeZone(*id), ec));
+      Calendar::createInstance(TimeZone::createTimeZone(*id), ec));
   if (U_FAILURE(ec)) {
     ReportError(out);
     return;
@@ -159,7 +176,7 @@ void TimeInstance::Time_getTimeZoneAbbreviation(const JsonValue& args,
   }
 
   std::unique_ptr<DateFormat> fmt(
-    new SimpleDateFormat(UnicodeString("z"), Locale::getEnglish(), ec));
+      new SimpleDateFormat(UnicodeString("z"), Locale::getEnglish(), ec));
   if (U_FAILURE(ec)) {
     ReportError(out);
     return;
@@ -181,7 +198,7 @@ void TimeInstance::Time_getTimeZoneAbbreviation(const JsonValue& args,
 
 void TimeInstance::Time_isDST(const JsonValue& args, JsonObject& out) {
   std::unique_ptr<UnicodeString> id(
-    new UnicodeString(args.get("timezone").to_str().c_str()));
+      new UnicodeString(args.get("timezone").to_str().c_str()));
   UDate dateInMs = strtod(args.get("value").to_str().c_str(), NULL);
   dateInMs -= _hourInMilliseconds;
 
@@ -192,7 +209,7 @@ void TimeInstance::Time_isDST(const JsonValue& args, JsonObject& out) {
 
   UErrorCode ec = U_ZERO_ERROR;
   std::unique_ptr<Calendar> cal(
-    Calendar::createInstance(TimeZone::createTimeZone(*id), ec));
+      Calendar::createInstance(TimeZone::createTimeZone(*id), ec));
   if (U_FAILURE(ec)) {
     ReportError(out);
     return;
@@ -210,7 +227,7 @@ void TimeInstance::Time_isDST(const JsonValue& args, JsonObject& out) {
 void TimeInstance::Time_getDSTTransition(const JsonValue& args,
                                          JsonObject& out) {
   std::unique_ptr<UnicodeString> id(
-    new UnicodeString(args.get("timezone").to_str().c_str()));
+      new UnicodeString(args.get("timezone").to_str().c_str()));
   std::string trans = args.get("trans").to_str();
   UDate dateInMs = strtod(args.get("value").to_str().c_str(), NULL);
 
@@ -258,7 +275,7 @@ void TimeInstance::Time_toDateString(const JsonValue& args, JsonObject& out) {
 
 void TimeInstance::Time_toTimeString(const JsonValue& args, JsonObject& out) {
   JsonValue val;
-  if(!this->toStringByFormat(args, val, TimeInstance::TIME_FORMAT)) {
+  if (!this->toStringByFormat(args, val, TimeInstance::TIME_FORMAT)) {
     ReportError(out);
     return;
   }
@@ -267,37 +284,31 @@ void TimeInstance::Time_toTimeString(const JsonValue& args, JsonObject& out) {
 }
 
 bool TimeInstance::toStringByFormat(const JsonValue& args, JsonValue& out,
-                                     DateTimeFormatType format) {
+                                    DateTimeFormatType format) {
   std::unique_ptr<UnicodeString> id(
-    new UnicodeString(args.get("timezone").to_str().c_str()));
+      new UnicodeString(args.get("timezone").to_str().c_str()));
   bool bLocale = args.get("locale").evaluate_as_boolean();
 
   UDate dateInMs = strtod(args.get("value").to_str().c_str(), NULL);
-  if (errno == ERANGE)
-    return false;
+  if (errno == ERANGE) return false;
 
   UErrorCode ec = U_ZERO_ERROR;
   std::unique_ptr<Calendar> cal(
-    Calendar::createInstance(TimeZone::createTimeZone(*id), ec));
-  if (U_FAILURE(ec))
-    return false;
+      Calendar::createInstance(TimeZone::createTimeZone(*id), ec));
+  if (U_FAILURE(ec)) return false;
 
   cal->setTime(dateInMs, ec);
-  if (U_FAILURE(ec))
-    return false;
+  if (U_FAILURE(ec)) return false;
 
-  std::unique_ptr<DateFormat> fmt(
-    new SimpleDateFormat(getDateTimeFormat(format, bLocale),
-                        (bLocale ? Locale::getDefault() : Locale::getEnglish()),
-                         ec));
-  if (U_FAILURE(ec))
-    return false;
+  std::unique_ptr<DateFormat> fmt(new SimpleDateFormat(
+      getDateTimeFormat(format, bLocale),
+      (bLocale ? Locale::getDefault() : Locale::getEnglish()), ec));
+  if (U_FAILURE(ec)) return false;
 
   UnicodeString uResult;
   fmt->setCalendar(*cal);
   fmt->format(cal->getTime(ec), uResult);
-  if (U_FAILURE(ec))
-    return false;
+  if (U_FAILURE(ec)) return false;
 
   std::string result = "";
   uResult.toUTF8String(result);
@@ -323,16 +334,14 @@ void TimeInstance::Time_getTimeFormat(const JsonValue& /*args*/,
   ReportSuccess(JsonValue(result), out);
 }
 
-
 UnicodeString TimeInstance::getDateTimeFormat(DateTimeFormatType type,
-                                             bool bLocale) {
+                                              bool bLocale) {
   UErrorCode ec = U_ZERO_ERROR;
   std::unique_ptr<DateTimePatternGenerator> dateTimepattern(
-     DateTimePatternGenerator::createInstance(
-        (bLocale ? Locale::getDefault() : Locale::getEnglish()), ec));
+      DateTimePatternGenerator::createInstance(
+          (bLocale ? Locale::getDefault() : Locale::getEnglish()), ec));
 
-  if (U_FAILURE(ec))
-    return "";
+  if (U_FAILURE(ec)) return "";
 
   UnicodeString pattern;
   if (type == DATE_FORMAT) {
@@ -341,8 +350,7 @@ UnicodeString TimeInstance::getDateTimeFormat(DateTimeFormatType type,
     pattern = dateTimepattern->getBestPattern(UDAT_YEAR_NUM_MONTH_DAY, ec);
   } else {
     std::string skeleton;
-    if (type != TIME_FORMAT)
-      skeleton = UDAT_YEAR_MONTH_WEEKDAY_DAY;
+    if (type != TIME_FORMAT) skeleton = UDAT_YEAR_MONTH_WEEKDAY_DAY;
 
 #if defined(TIZEN)
     int value = 0;
@@ -355,16 +363,170 @@ UnicodeString TimeInstance::getDateTimeFormat(DateTimeFormatType type,
 #endif
 
     pattern = dateTimepattern->getBestPattern(
-       *(new UnicodeString(skeleton.c_str())), ec);
-    if (U_FAILURE(ec))
-      return "";
+        *(new UnicodeString(skeleton.c_str())), ec);
+    if (U_FAILURE(ec)) return "";
 
-    if (!bLocale)
-      pattern += " 'GMT'Z v'";
+    if (!bLocale) pattern += " 'GMT'Z v'";
   }
 
   return pattern;
 }
 
+/////////////////////////// TimeUtilListeners ////////////////////////////////
+
+class TimeUtilListeners {
+ public:
+  TimeUtilListeners();
+  ~TimeUtilListeners();
+
+  void RegisterVconfCallback(ListenerType type);
+  void UnregisterVconfCallback(ListenerType type);
+
+  std::string GetCurrentTimezone();
+  void SetCurrentTimezone(std::string& newTimezone);
+
+ private:
+  std::string current_timezone_;
+  bool is_time_listener_registered_;
+  bool is_timezone_listener_registered_;
+};
+
+TimeUtilListeners::TimeUtilListeners()
+    : current_timezone_(GetDefaultTimezone()),
+      is_time_listener_registered_(false),
+      is_timezone_listener_registered_(false) {
+  LoggerD("Entered");
+}
+
+TimeUtilListeners::~TimeUtilListeners() {
+  LoggerD("Entered");
+  if (is_time_listener_registered_ || is_timezone_listener_registered_) {
+    if (0 != vconf_ignore_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED,
+                                      OnTimeChangedCallback)) {
+      LoggerE("Failed to unregister vconf callback");
+    }
+  }
+}
+
+void TimeUtilListeners::RegisterVconfCallback(ListenerType type) {
+  LoggerD("");
+  if (!is_time_listener_registered_ && !is_timezone_listener_registered_) {
+    LoggerD("registering listener on platform");
+    if (0 != vconf_notify_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED,
+                                      OnTimeChangedCallback, nullptr)) {
+      LoggerE("Failed to register vconf callback");
+      throw UnknownException("Failed to register vconf callback");
+    }
+  } else {
+    LoggerD("not registering listener on platform - already registered");
+  }
+  switch (type) {
+    case kTimeChange:
+      is_time_listener_registered_ = true;
+      LoggerD("time change listener registered");
+      break;
+    case kTimezoneChange:
+      is_timezone_listener_registered_ = true;
+      LoggerD("time zone change listener registered");
+      break;
+    default:
+      LoggerE("Unknown type of listener");
+      throw UnknownException("Unknown type of listener");
+  }
+}
+
+void TimeUtilListeners::UnregisterVconfCallback(ListenerType type) {
+  LoggerD("");
+  switch (type) {
+    case kTimeChange:
+      is_time_listener_registered_ = false;
+      LoggerD("time change listener unregistered");
+      break;
+    case kTimezoneChange:
+      is_timezone_listener_registered_ = false;
+      LoggerD("time zone change listener unregistered");
+      break;
+    default:
+      throw UnknownException("Unknown type of listener");
+  }
+  if (!is_time_listener_registered_ && !is_timezone_listener_registered_) {
+    LoggerD("unregistering listener on platform");
+    if (0 != vconf_ignore_key_changed(VCONFKEY_SYSTEM_TIME_CHANGED,
+                                      OnTimeChangedCallback)) {
+      LoggerE("Failed to unregister vconf callback");
+    }
+  }
+}
+
+std::string TimeUtilListeners::GetCurrentTimezone() {
+  return current_timezone_;
+}
+
+void TimeUtilListeners::SetCurrentTimezone(std::string& newTimezone) {
+  current_timezone_ = newTimezone;
+}
+
+static std::string GetDefaultTimezone() {
+  LoggerD("");
+  char buf[1024];
+  std::string result;
+  ssize_t len = readlink("/opt/etc/localtime", buf, sizeof(buf) - 1);
+  if (len != -1) {
+    buf[len] = '\0';
+  } else {
+    /* handle error condition */
+    return result;
+  }
+  result = std::string(buf + strlen("/usr/share/zoneinfo/"));
+
+  LoggerD("tzpath = %s", result.c_str());
+  return result;
+}
+
+/////////////////////////// TimeUtilListeners object ////////////////////////
+static TimeUtilListeners g_time_util_listeners_obj;
+
+static void PostMessage(const char* message) {
+  JsonValue result{JsonObject{}};
+  JsonObject& result_obj = result.get<JsonObject>();
+  result_obj.insert(std::make_pair("listenerId", message));
+  TimeInstance::GetInstance().PostMessage(result.serialize().c_str());
+}
+
+static void OnTimeChangedCallback(keynode_t* /*node*/, void* /*event_ptr*/) {
+  LoggerD("");
+  std::string defaultTimezone = GetDefaultTimezone();
+
+  if (g_time_util_listeners_obj.GetCurrentTimezone() != defaultTimezone) {
+    g_time_util_listeners_obj.SetCurrentTimezone(defaultTimezone);
+    PostMessage(kTimezoneListenerId);
+  }
+  PostMessage(kDateTimeListenerId);
+}
+
+void TimeInstance::Time_setDateTimeChangeListener(const JsonValue& /*args*/,
+                                                  JsonObject& out) {
+  g_time_util_listeners_obj.RegisterVconfCallback(kTimeChange);
+  ReportSuccess(out);
+}
+
+void TimeInstance::Time_unsetDateTimeChangeListener(const JsonValue& /*args*/,
+                                                    JsonObject& out) {
+  g_time_util_listeners_obj.UnregisterVconfCallback(kTimeChange);
+  ReportSuccess(out);
+}
+
+void TimeInstance::Time_setTimezoneChangeListener(const JsonValue& /*args*/,
+                                                  JsonObject& out) {
+  g_time_util_listeners_obj.RegisterVconfCallback(kTimezoneChange);
+  ReportSuccess(out);
+}
+
+void TimeInstance::Time_unsetTimezoneChangeListener(const JsonValue& /*args*/,
+                                                    JsonObject& out) {
+  g_time_util_listeners_obj.UnregisterVconfCallback(kTimezoneChange);
+  ReportSuccess(out);
+}
+
 }  // namespace time
 }  // namespace extension
index 08a62d50f446bf87b51a1eff10e8cc7ac792a7c0..ade64f57ca470f5f9d1503bcd8713d67214ce32e 100644 (file)
@@ -44,6 +44,10 @@ class TimeInstance : public common::ParsedInstance {
   void Time_toString(const JsonValue& args, JsonObject& out);
   void Time_toDateString(const JsonValue& args, JsonObject& out);
   void Time_toTimeString(const JsonValue& args, JsonObject& out);
+  void Time_setDateTimeChangeListener(const JsonValue& args, JsonObject& out);
+  void Time_unsetDateTimeChangeListener(const JsonValue& args, JsonObject& out);
+  void Time_setTimezoneChangeListener(const JsonValue& args, JsonObject& out);
+  void Time_unsetTimezoneChangeListener(const JsonValue& args, JsonObject& out);
 
   UnicodeString getDateTimeFormat(DateTimeFormatType type, bool bLocale);
   bool toStringByFormat(const JsonValue& args, JsonValue& out,