[Application] Event listeners
authorPawel Kaczmarek <p.kaczmarek3@samsung.com>
Thu, 6 Aug 2015 08:18:29 +0000 (10:18 +0200)
committerPawel Kaczmarek <p.kaczmarek3@samsung.com>
Mon, 10 Aug 2015 10:25:55 +0000 (12:25 +0200)
[Verification]
Event listeners should work

Change-Id: I955cbbff9ece49c6025b3881eecfe70c631d544d
Signed-off-by: Pawel Kaczmarek <p.kaczmarek3@samsung.com>
src/application/application_api.js
src/application/application_instance.cc
src/application/application_manager.cc
src/application/application_manager.h

index 79a0977..8107ff7 100755 (executable)
@@ -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
   };
 
index 4b4bfa9..69cd278 100755 (executable)
@@ -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<std::string>();
+
+  LOGGER(DEBUG) << "event_name: " << event_name;
+
+  JsonCallback cb = [this, args](picojson::value* event) -> void {
+   picojson::object& event_o = event->get<picojson::object>();
+   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<std::string>();
+
+  LOGGER(DEBUG) << "event_name: " << event_name;
+
+  manager_.StopEventListener(event_name);
 }
 
 }  // namespace application
index 7732ae0..1871968 100755 (executable)
@@ -25,7 +25,6 @@
 #include <aul.h>
 #include <package_manager.h>
 #include <pkgmgr-info.h>
-#include <app_event.h>
 #include <bundle.h>
 #include <bundle_internal.h>
 
@@ -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<std::string, std::string> 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<ApplicationManager*>(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<picojson::object>();
+
+  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
index 74168d8..0609659 100755 (executable)
 #ifndef SRC_APPLICATION_APPLICATION_MANAGER_H__
 #define SRC_APPLICATION_APPLICATION_MANAGER_H__
 
-#include <string>
+#include <app_event.h>
+#include <bundle.h>
+#include <functional>
 #include <memory>
-
 #include <package-manager.h>
+#include <string>
 
 #include "common/picojson.h"
 #include "common/platform_result.h"
 
+typedef std::function<void(picojson::value*)> 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<picojson::value>* 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<std::string, event_handler_h> event_handler_map_;
+  static void OnEvent(const char* event_name,
+                      bundle* event_data,
+                      void* user_data);
 };
 
 } // namespace application