From: Pawel Kaczmarek Date: Thu, 6 Aug 2015 08:18:29 +0000 (+0200) Subject: [Application] Event listeners X-Git-Tag: submit/tizen/20151026.073646^2^2~199 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3664b06fe3c9b1ec38d5da8e29edca2a3a2af814;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Application] Event listeners [Verification] Event listeners should work Change-Id: I955cbbff9ece49c6025b3881eecfe70c631d544d Signed-off-by: Pawel Kaczmarek --- diff --git a/src/application/application_api.js b/src/application/application_api.js index 79a09773..8107ff74 100755 --- a/src/application/application_api.js +++ b/src/application/application_api.js @@ -44,7 +44,7 @@ var SystemEvent = { LOCATION_ENABLE_STATE: 'LOCATION_ENABLE_STATE', GPS_ENABLE_STATE: 'GPS_ENABLE_STATE', NPS_ENABLE_STATE: 'NPS_ENABLE_STATE', - INCOMMING_MSG: 'INCOMMING_MSG', + INCOMING_MSG: 'INCOMING_MSG', TIME_CHANGED: 'TIME_CHANGED', TIME_ZONE: 'TIME_ZONE', HOUR_FORMAT: 'HOUR_FORMAT', @@ -637,6 +637,7 @@ Application.prototype.getRequestedAppControl = function() { }; function _checkEventName(name) { + var name = name || ''; if (!(/^([a-zA-Z_]){1}([a-zA-Z0-9_]){0,126}$/.test(name))) { throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid event name'); @@ -650,50 +651,111 @@ function _checkAppId(appId) { } } -Application.prototype.addEventListener = function(name, callback) { - var args = AV.validateMethod(arguments, [ - {name: 'name', type: AV.Types.STRING}, +var event_listeners_ = {}; +var watchId_ = 0; +function nextWatchId() { + return ++watchId_; +} + +Application.prototype.addEventListener = function(event, callback) { + var args = AV.validateArgs(arguments, [ + {name: 'event', type: AV.Types.DICTIONARY}, {name: 'callback', type: AV.Types.FUNCTION} ]); - var data = { - listenerId: 'AppEventListener' - }; + var data = {}; - if (Object.keys(SystemEvent).indexOf(args.name) > -1) { - data.name = 'tizen.system.event.' + args.name.toLowerCase(); + if (Object.keys(SystemEvent).indexOf(args.event.name) > -1) { + data.name = 'tizen.system.event.' + args.event.name.toLowerCase(); } else { - var _arr = args.name.split('.'); - var _event_name = _arr.pop(); - var _appid = _arr.join('.'); + _checkEventName(args.event.name); + _checkAppId(args.event.appId); - _checkEventName(_event_name); - _checkAppId(_appid); + data.name = 'event.' + args.event.appId + '.' + args.event.name; + } - data.name = 'event.' + _appid + '.' + _event_name; + var watchId = nextWatchId(); + data.listenerId = data.name; + event_listeners_[data.name] = !T.isObject(event_listeners_[data.name]) + ? {} : event_listeners_[data.name]; + + if (!Object.keys(event_listeners_[data.name]).length) { + native.addListener(data.name, function(msg) { + var eventName = msg.name; + var event = eventName.split('.').pop(); + for (var id in event_listeners_[eventName]) { + if (event_listeners_[eventName].hasOwnProperty(id)) { + if (msg.data) { + event_listeners_[eventName][id](event, msg.data); + } else { + delete msg.name; + msg.type = event; //TODO: type should come from native site + event_listeners_[eventName][id](event.toUpperCase(), msg); + } + } + } + }); + + var result = native.callSync('Application_addEventListener', data); + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } } - // @TODO: register eventListener + event_listeners_[data.name][watchId] = args.callback; + return watchId; }; +function getEventNameById(watchId) { + var eventName; + for (var event in event_listeners_) { + if (event_listeners_.hasOwnProperty(event)) { + for (var id in event_listeners_[event]) { + if (event_listeners_[event].hasOwnProperty(id) && Converter.toLong(id) === watchId) { + eventName = event; + } + } + } + } + return eventName; +} + Application.prototype.removeEventListener = function(watchId) { - var args = AV.validateMethod(arguments, [ + var args = AV.validateArgs(arguments, [ {name: 'watchId', type: AV.Types.LONG} ]); - // @TODO: remove eventListener + var eventName = getEventNameById(args.watchId); + + if (!eventName) { + return; + } + + delete event_listeners_[eventName][args.watchId]; + + if (!Object.keys(event_listeners_[eventName]).length) { + native.removeListener(eventName); + var result = native.callSync('Application_removeEventListener', {name: eventName}); + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } + } }; -Application.prototype.broadcastEvent = function(name, data) { +Application.prototype.broadcastEvent = function(event, data) { var args = AV.validateMethod(arguments, [ - {name: 'name', type: AV.Types.STRING}, + {name: 'event', type: AV.Types.DICTIONARY}, {name: 'data', type: AV.Types.DICTIONARY} ]); - _checkEventName(args.name); + _checkEventName(args.event.name); + + if (this.appInfo.id !== args.event.appId) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid appId'); + } var nativeData = { - name: 'event.' + this.appInfo.id + '.' + args.name, + name: 'event.' + this.appInfo.id + '.' + args.event.name, data: args.data }; @@ -704,16 +766,20 @@ Application.prototype.broadcastEvent = function(name, data) { } }; -Application.prototype.broadcastTrustedEvent = function(name, data) { +Application.prototype.broadcastTrustedEvent = function(event, data) { var args = AV.validateMethod(arguments, [ - {name: 'name', type: AV.Types.STRING}, + {name: 'event', type: AV.Types.DICTIONARY}, {name: 'data', type: AV.Types.DICTIONARY} ]); - _checkEventName(args.name); + _checkEventName(args.event.name); + + if (this.appInfo.id !== args.event.appId) { + throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid appId'); + } var nativeData = { - name: 'event.' + this.appInfo.id + '.' + args.name, + name: 'event.' + this.appInfo.id + '.' + args.event.name, data: args.data }; diff --git a/src/application/application_instance.cc b/src/application/application_instance.cc index 4b4bfa93..69cd2783 100755 --- a/src/application/application_instance.cc +++ b/src/application/application_instance.cc @@ -221,27 +221,46 @@ void ApplicationInstance::GetAppsInfo(const picojson::value& args, picojson::obj void ApplicationInstance::BroadcastEvent(const picojson::value& args, picojson::object& out) { LoggerD("Entered"); - bool trusted = false; - manager_.BroadcastEventHelper(args, out, trusted); + manager_.BroadcastEventHelper(args, out, false); } void ApplicationInstance::BroadcastTrustedEvent(const picojson::value& args, picojson::object& out) { LoggerD("Entered"); - bool trusted = true; - manager_.BroadcastEventHelper(args, out, trusted); + manager_.BroadcastEventHelper(args, out, true); } void ApplicationInstance::AddEventListener(const picojson::value& args, picojson::object& out) { LoggerD("Entered"); - manager_.StartEventListener(&out); + const std::string& event_name = args.get("name").get(); + + LOGGER(DEBUG) << "event_name: " << event_name; + + JsonCallback cb = [this, args](picojson::value* event) -> void { + picojson::object& event_o = event->get(); + event_o["listenerId"] = args.get("listenerId"); + LOGGER(DEBUG) << event->serialize().c_str(); + PostMessage(event->serialize().c_str()); + LOGGER(DEBUG) << event->serialize().c_str(); + }; + + PlatformResult result = manager_.StartEventListener(event_name, cb); + if (result) { + ReportSuccess(out); + } else { + ReportError(result, &out); + } } void ApplicationInstance::RemoveEventListener(const picojson::value& args, picojson::object& out) { LoggerD("Entered"); - manager_.StopEventListener(); + const std::string& event_name = args.get("name").get(); + + LOGGER(DEBUG) << "event_name: " << event_name; + + manager_.StopEventListener(event_name); } } // namespace application diff --git a/src/application/application_manager.cc b/src/application/application_manager.cc index 7732ae00..18719685 100755 --- a/src/application/application_manager.cc +++ b/src/application/application_manager.cc @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -61,6 +60,31 @@ const std::string kOnInstalled = "oninstalled"; const std::string kOnUpdated = "onupdated"; const std::string kOnUninstalled = "onuninstalled"; const std::string kData = "data"; + +const std::map event_map_ = { + {SYSTEM_EVENT_BATTERY_CHARGER_STATUS, EVENT_KEY_BATTERY_CHARGER_STATUS}, + {SYSTEM_EVENT_BATTERY_LEVEL_STATUS, EVENT_KEY_BATTERY_LEVEL_STATUS}, + {SYSTEM_EVENT_USB_STATUS, EVENT_KEY_USB_STATUS}, + {SYSTEM_EVENT_EARJACK_STATUS, EVENT_KEY_EARJACK_STATUS}, + {SYSTEM_EVENT_DISPLAY_STATE, EVENT_KEY_DISPLAY_STATE}, + {SYSTEM_EVENT_LOW_MEMORY, EVENT_KEY_LOW_MEMORY}, + {SYSTEM_EVENT_WIFI_STATE, EVENT_KEY_WIFI_STATE}, + {SYSTEM_EVENT_BT_STATE, EVENT_KEY_BT_STATE}, + {SYSTEM_EVENT_LOCATION_ENABLE_STATE, EVENT_KEY_LOCATION_ENABLE_STATE}, + {SYSTEM_EVENT_GPS_ENABLE_STATE, EVENT_KEY_GPS_ENABLE_STATE}, + {SYSTEM_EVENT_NPS_ENABLE_STATE, EVENT_KEY_NPS_ENABLE_STATE}, + {SYSTEM_EVENT_INCOMMING_MSG, EVENT_KEY_MSG_TYPE}, + {SYSTEM_EVENT_TIME_ZONE, EVENT_KEY_TIME_ZONE}, + {SYSTEM_EVENT_HOUR_FORMAT, EVENT_KEY_HOUR_FORMAT}, + {SYSTEM_EVENT_LANGUAGE_SET, EVENT_KEY_LANGUAGE_SET}, + {SYSTEM_EVENT_REGION_FORMAT, EVENT_KEY_REGION_FORMAT}, + {SYSTEM_EVENT_SILENT_MODE, EVENT_KEY_SILENT_MODE}, + {SYSTEM_EVENT_VIBRATION_STATE, EVENT_KEY_VIBRATION_STATE}, + {SYSTEM_EVENT_SCREEN_AUTOROTATE_STATE, EVENT_KEY_SCREEN_AUTOROTATE_STATE}, + {SYSTEM_EVENT_MOBILE_DATA_STATE, EVENT_KEY_MOBILE_DATA_STATE}, + {SYSTEM_EVENT_DATA_ROAMING_STATE, EVENT_KEY_DATA_ROAMING_STATE}, + {SYSTEM_EVENT_FONT_SET, EVENT_KEY_FONT_SET} +}; } ApplicationManager::ApplicationManager(ApplicationInstance& instance) : @@ -1348,16 +1372,106 @@ void ApplicationManager::BroadcastEventHelper( } } -void ApplicationManager::StartEventListener(picojson::object* out) { +void ApplicationManager::OnEvent(const char* event_name, + bundle* event_data, + void* user_data) { + LoggerD("Entered"); + LOGGER(DEBUG) << event_name; + + ApplicationManager* manager = static_cast(user_data); + + if (!manager->event_callback_) { + LOGGER(DEBUG) << "No event listener registered, skipping."; + return; + } + + picojson::value event = picojson::value(picojson::object()); + picojson::object& event_o = event.get(); + + int ret; + char* val = nullptr; + + if (event_map_.count(event_name)) { // system event + const std::string& key = event_map_.at(event_name); + std::string state = "true"; + if (key != "") { + ret = bundle_get_str(event_data, key.c_str(), &val); + if (EVENT_ERROR_NONE != ret) { + LOGGER(ERROR) << "failed to read bundle data, error: " << ret; + return; + } + + state = std::string(val); + } + + LOGGER(DEBUG) << "state is: " << state; + event_o["value"] = picojson::value(state); + + } else { // user event + ret = bundle_get_str(event_data, "data", &val); + if (EVENT_ERROR_NONE != ret) { + LOGGER(ERROR) << "failed to read bundle data, error: " << ret; + return; + } + + picojson::value data; + std::string err; + picojson::parse(data, val, val + strlen(val), &err); + if (!err.empty()) { + LOGGER(ERROR) << "Failed to parse bundle data: " << err; + return; + } + + event_o["data"] = data; + } + + LOGGER(DEBUG) << "event_name is: " << event_name; + event_o["name"] = picojson::value(event_name); + + manager->event_callback_(&event); +} + +PlatformResult ApplicationManager::StartEventListener(const std::string& event_name, + const JsonCallback& callback) { LoggerD("Entered"); - //TODO: please implement + int ret; + event_handler_h event_handler; + + ret = event_add_event_handler(event_name.c_str(), OnEvent, this, &event_handler); + LOGGER(DEBUG) << "event_add_event_handler() result: " << ret; + if (EVENT_ERROR_PERMISSION_DENIED == ret) { + LOGGER(ERROR) << "event_add_event_handler failed, error: " << ret; + return PlatformResult(ErrorCode::SECURITY_ERR, "The privilege is required"); + } else if (EVENT_ERROR_NONE != ret) { + LOGGER(ERROR) << "event_add_event_handler failed, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Error setting event listener"); + } + + event_handler_map_[event_name] = event_handler; + + event_callback_ = callback; + LOGGER(DEBUG) << "event_add_event_handler success"; + return PlatformResult(ErrorCode::NO_ERROR); } -void ApplicationManager::StopEventListener() { +void ApplicationManager::StopEventListener(const std::string& event_name) { LoggerD("Entered"); - //TODO: please implement + int ret; + event_handler_h event_handler; + + if (event_handler_map_.find(event_name) != event_handler_map_.end()) { + event_handler = event_handler_map_[event_name]; + + ret = event_remove_event_handler(event_handler); + if (EVENT_ERROR_NONE != ret) { + LOGGER(ERROR) << "event_remove_event_handler failed, error: " << ret; + return; + } + + event_handler_map_.erase(event_name); + } } } // namespace application diff --git a/src/application/application_manager.h b/src/application/application_manager.h index 74168d85..0609659e 100755 --- a/src/application/application_manager.h +++ b/src/application/application_manager.h @@ -17,14 +17,18 @@ #ifndef SRC_APPLICATION_APPLICATION_MANAGER_H__ #define SRC_APPLICATION_APPLICATION_MANAGER_H__ -#include +#include +#include +#include #include - #include +#include #include "common/picojson.h" #include "common/platform_result.h" +typedef std::function JsonCallback; + namespace extension { namespace application { @@ -51,15 +55,23 @@ class ApplicationManager { void StopAppInfoEventListener(); void GetApplicationInformationSize(const picojson::value& args, picojson::object* out); void AsyncResponse(common::PlatformResult& result, std::shared_ptr* response); + void BroadcastEventHelper(const picojson::value& args, picojson::object& out, bool trusted); - void StartEventListener(picojson::object* out); - void StopEventListener(); + common::PlatformResult StartEventListener(const std::string& event_name, + const JsonCallback& callback); + void StopEventListener(const std::string& event_name); private: char* GetPackageId(const std::string& app_id); pkgmgr_client* pkgmgr_client_handle_; ApplicationInstance& instance_; + + JsonCallback event_callback_; + std::map event_handler_map_; + static void OnEvent(const char* event_name, + bundle* event_data, + void* user_data); }; } // namespace application