From 5ca4c7a770797a58e3d66697e285c7e05760ac29 Mon Sep 17 00:00:00 2001
From: Pawel Kaczmarek
Date: Tue, 13 Jan 2015 11:48:07 +0100
Subject: [PATCH] [Time] Listeners
[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
---
src/time/time_api.js | 79 ++++++++++--
src/time/time_instance.cc | 250 +++++++++++++++++++++++++++++++-------
src/time/time_instance.h | 4 +
3 files changed, 282 insertions(+), 51 deletions(-)
diff --git a/src/time/time_api.js b/src/time/time_api.js
index 3b106d4e..89fb57da 100644
--- a/src/time/time_api.js
+++ b/src/time/time_api.js
@@ -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;
diff --git a/src/time/time_instance.cc b/src/time/time_instance.cc
index daf19765..0f069d01 100644
--- a/src/time/time_instance.cc
+++ b/src/time/time_instance.cc
@@ -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
@@ -25,15 +27,24 @@
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 char* timezone = 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 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 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 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 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 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 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 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 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 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 fmt(
- new SimpleDateFormat(getDateTimeFormat(format, bLocale),
- (bLocale ? Locale::getDefault() : Locale::getEnglish()),
- ec));
- if (U_FAILURE(ec))
- return false;
+ std::unique_ptr 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 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();
+ 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
diff --git a/src/time/time_instance.h b/src/time/time_instance.h
index 08a62d50..ade64f57 100644
--- a/src/time/time_instance.h
+++ b/src/time/time_instance.h
@@ -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,
--
2.34.1