[Content] Add listeners and events support
authorSakari Poussa <sakari.poussa@intel.com>
Fri, 13 Jun 2014 07:01:38 +0000 (10:01 +0300)
committerSakari Poussa <sakari.poussa@intel.com>
Fri, 27 Jun 2014 11:01:46 +0000 (14:01 +0300)
Add support for subscribing to media content change events
via setChangeListeners and unsetChangeListeners methods.

Also, some debug macro tunings.

content/content_api.js
content/content_instance.cc
content/content_instance.h
examples/content.html

index 0491f93..a702533 100644 (file)
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 var _callbacks = {};
-var _nextReplyId = 0;
+var _nextReplyId = 1; // 0 is reserved for events
 
 function getNextReplyId() {
   return _nextReplyId++;
@@ -25,7 +25,16 @@ extension.setMessageListener(function(msg) {
   var replyId = m.replyId;
   var callback = _callbacks[replyId];
 
-  if (typeof(callback) === 'function') {
+  if (replyId == 0) { // replyId zero is for events
+    if (exports.changeListener != null) {
+      if (m.eventType == 'INSERT')
+        exports.changeListener.oncontentadded(m.value);
+      else if (m.eventType == 'DELETE')
+        exports.changeListener.oncontentremoved(m.value.id);
+      else if (m.eventType == 'UPDATE')
+        exports.changeListener.oncontentupdated(m.value);
+    }
+  } else if (typeof(callback) === 'function') {
     callback(m);
     delete m.replyId;
     delete _callbacks[replyId];
@@ -94,6 +103,7 @@ function ContentVideo(obj, album, artists, duration, width, height) {
 }
 
 function ContentManager() {
+  this.changeListener = null;
 }
 
 ContentManager.prototype.update = function(content) {
@@ -216,13 +226,23 @@ ContentManager.prototype.scanFile = function(contentURI, onsuccess, onerror) {
   });
 };
 
-ContentManager.prototype.setChangeListener = function(onchange) {
-  if (!xwalk.utils.validateArguments('f', arguments)) {
+ContentManager.prototype.setChangeListener = function(listener) {
+  if (!xwalk.utils.validateArguments('o', arguments)) {
+    throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
+  }
+
+  if (!xwalk.utils.validateObject(listener, 'fff',
+      ['oncontentadded', 'oncontentupdated', 'oncontentremoved'])) {
     throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
   }
+
+  this.changeListener = listener;
+  sendSyncMessage({cmd: 'ContentManager.setChangeListener'});
 };
 
-ContentManager.prototype.unsetChangeLIstener = function() {
+ContentManager.prototype.unsetChangeListener = function() {
+  this.changeListener = null;
+  sendSyncMessage({cmd: 'ContentManager.unsetChangeListener'});
 };
 
 exports = new ContentManager();
index 38518ee..67ccf50 100644 (file)
@@ -18,6 +18,7 @@
 namespace {
 const std::string STR_FILTER("filter");
 const std::string STR_CONTENT_URI("contentURI");
+const std::string STR_EVENT_TYPE("eventType");
 
 std::string createUriFromLocalPath(const std::string path) {
   static std::string fileScheme("file://");
@@ -64,7 +65,7 @@ void ContentInstance::HandleMessage(const char* message) {
     std::cerr << "Ignoring message.\n";
     return;
   }
-#ifdef DEBUG_JSON
+#ifdef DEBUG_JSON_CMD
   std::cout << "HandleMessage: " << message << std::endl;
 #endif
   std::string cmd = v.get("cmd").to_str();
@@ -94,7 +95,11 @@ void ContentInstance::PostAsyncSuccessReply(const picojson::value& msg,
     picojson::value::object& reply) {
   reply["isError"] = picojson::value(false);
   reply["replyId"] = picojson::value(msg.get("replyId").get<double>());
-
+  if (msg.contains(STR_EVENT_TYPE))
+    reply[STR_EVENT_TYPE] = picojson::value(msg.get(STR_EVENT_TYPE));
+#ifdef DEBUG_JSON_CMD
+  std::cout << "reply: " << msg.serialize().c_str() << std::endl;
+#endif
   picojson::value v(reply);
   PostMessage(v.serialize().c_str());
 }
@@ -112,12 +117,42 @@ void ContentInstance::PostAsyncSuccessReply(const picojson::value& msg,
 }
 
 void ContentInstance::HandleSyncMessage(const char* message) {
+  picojson::value v;
+  picojson::value::object o;
+
+  std::string err;
+  picojson::parse(v, message, message + strlen(message), &err);
+  if (!err.empty()) {
+    std::cerr << "Ignoring message.\n";
+    return;
+  }
+#ifdef DEBUG_JSON_CMD
+  std::cout << "HandleSyncMessage: " << message << std::endl;
+#endif
+  std::string cmd = v.get("cmd").to_str();
+  int rc = MEDIA_CONTENT_ERROR_INVALID_OPERATION;
+
+  if (cmd == "ContentManager.setChangeListener") {
+    rc = media_content_set_db_updated_cb(MediaContentChangeCallback, this);
+  } else if (cmd == "ContentManager.unsetChangeListener") {
+    rc = media_content_unset_db_updated_cb();
+  } else {
+    std::cerr << "Message " + cmd + " is not supported.\n";
+  }
+
+  if (rc != MEDIA_CONTENT_ERROR_NONE)
+    std::cerr << "error " << cmd << std::endl;
+
+#ifdef DEBUG_JSON_CMD
+  std::cout << "Reply: " << v.serialize().c_str() << std::endl;
+#endif
+  SendSyncReply(v.serialize().c_str());
 }
 
 void ContentInstance::HandleGetDirectoriesRequest(const picojson::value& msg) {
   ContentFolderList folderList;
   if (media_folder_foreach_folder_from_db(NULL,
-          mediaFolderCallback,
+          MediaFolderCallback,
           reinterpret_cast<void*>(&folderList)) != MEDIA_CONTENT_ERROR_NONE) {
     std::cerr << "media_folder_foreach_folder_from_db: error" << std::endl;
   } else {
@@ -147,7 +182,7 @@ void ContentInstance::HandleGetDirectoriesReply(const picojson::value& msg,
   PostAsyncSuccessReply(msg, value);
 }
 
-bool ContentInstance::mediaFolderCallback(media_folder_h handle,
+bool ContentInstance::MediaFolderCallback(media_folder_h handle,
     void* user_data) {
   if (!user_data)
     return false;
@@ -181,7 +216,7 @@ void ContentInstance::HandleFindRequest(const picojson::value& msg) {
   }
 
   if (media_info_foreach_media_from_db(filterHandle,
-      mediaInfoCallback,
+      MediaInfoCallback,
       reinterpret_cast<ContentFolderList*>(&itemList))
       != MEDIA_CONTENT_ERROR_NONE) {
     std::cerr << "media_info_foreach_media_from_db: error" << std::endl;
@@ -257,14 +292,14 @@ void ContentInstance::HandleFindReply(
     items.push_back(picojson::value(o));
   }
   picojson::value value(items);
-#ifdef DEBUG_JSON
+#ifdef DEBUG_JSON_REPLY
   std::cout << "JSON reply: " << std::endl <<
      value.serialize().c_str() << std::endl;
 #endif
   PostAsyncSuccessReply(msg, value);
 }
 
-bool ContentInstance::mediaInfoCallback(media_info_h handle, void* user_data) {
+bool ContentInstance::MediaInfoCallback(media_info_h handle, void* user_data) {
   if (!user_data)
     return false;
 
@@ -279,6 +314,54 @@ bool ContentInstance::mediaInfoCallback(media_info_h handle, void* user_data) {
   return true;
 }
 
+void ContentInstance::MediaContentChangeCallback(
+    media_content_error_e error,
+    int pid,
+    media_content_db_update_item_type_e update_item,
+    media_content_db_update_type_e update_type,
+    media_content_type_e media_type,
+    char* uuid,
+    char* path,
+    char* mime_type,
+    void* user_data) {
+#ifdef DEBUG_ITEM
+  std::cout << "MediaContentChangeCallback: error=" << error <<
+      ", item=" << update_item << ", type=" << update_type << ", " <<
+      uuid << ", " << path << std::endl;
+#endif
+  if (!user_data)
+    return;
+
+  ContentInstance* self =
+      reinterpret_cast<ContentInstance*>(user_data);
+
+  picojson::value::object om;
+  om["replyId"] = picojson::value(static_cast<double>(0));
+  const std::string type = (update_type == MEDIA_CONTENT_INSERT ?
+      "INSERT" : (update_type == MEDIA_CONTENT_DELETE ? "DELETE" : "UPDATE"));
+  om[STR_EVENT_TYPE] = picojson::value(type);
+  picojson::value::object ov;
+  ov["type"] = picojson::value(static_cast<double>(media_type));
+
+  if (uuid)
+    ov["id"] = picojson::value(uuid);
+
+  if (path)
+    ov["contentURI"] = picojson::value(path);
+
+  if (mime_type)
+    ov["mimeType"] = picojson::value(mime_type);
+
+  picojson::value msg(om);
+  picojson::value value(ov);
+#ifdef DEBUG_JSON_REPLY
+  std::cout << "JSON event msg: " << msg.serialize().c_str() << std::endl;
+  std::cout << "JSON event val: " << value.serialize().c_str() << std::endl;
+#endif
+
+  self->PostAsyncSuccessReply(msg, value);
+}
+
 void ContentFolder::init(media_folder_h handle) {
   char* str = NULL;
   time_t date;
index a38eaef..8285455 100644 (file)
@@ -48,8 +48,18 @@ class ContentInstance : public common::Instance {
   void PostAsyncSuccessReply(const picojson::value&);
 
   // Tizen CAPI helpers
-  static bool mediaFolderCallback(media_folder_h handle, void *user_data);
-  static bool mediaInfoCallback(media_info_h handle, void *user_data);
+  static bool MediaFolderCallback(media_folder_h handle, void *user_data);
+  static bool MediaInfoCallback(media_info_h handle, void *user_data);
+  static void MediaContentChangeCallback(
+      media_content_error_e error,
+      int pid,
+      media_content_db_update_item_type_e update_item,
+      media_content_db_update_type_e update_type,
+      media_content_type_e media_type,
+      char* uuid,
+      char* path,
+      char* mime_type,
+      void* user_data);
 
   static unsigned m_instanceCount;
 };
index 93bc419..369c06f 100644 (file)
@@ -9,6 +9,8 @@
 <button onClick="handleGetDirectories()">Get Directories</button>
 <button onClick="handleFind()">Find</button>
 <button onClick="handleScanFile()">Scan File</button>
+<button onClick="enableEvents()">Listener (ON)</button>
+<button onClick="disableEvents()">Listener (OFF)</button>
 
 <div>
   <h2>Attribute Filter</h2>
@@ -60,8 +62,7 @@ function handleGetDirectories()
     function(err) {
       debug(err.name);
     });
-  }
-  catch (err) {
+  } catch (err) {
     debug(err.name);
   }
 }
@@ -89,8 +90,7 @@ function handleFind()
     function(err) {
       debug('find: error');
     }, null, filter);
-  }
-  catch (err) {
+  } catch (err) {
     debug(err.name);
   }
 }
@@ -105,8 +105,36 @@ function handleScanFile()
       function(err) {
         debug("scan ERR: ")
       });
+  } catch (err) {
+    debug(err.name);
   }
-  catch (err) {
+}
+var listener = {
+    oncontentadded: function(content) {
+      debug("Event ADD: " + content.contentURI);
+    },
+    oncontentupdated: function(content) {
+      debug("Event UPDATE: " + content.contentURI);
+    },
+    oncontentremoved: function(id) {
+      debug("Event REMOVE: " + id);
+    }
+ };
+
+function enableEvents() {
+  try {
+    debug('tizen.content.setChangeListener:');
+    tizen.content.setChangeListener(listener);
+  } catch (err) {
+    debug(err.name);
+  }
+}
+
+function disableEvents() {
+  try {
+    debug('tizen.content.unsetChangeListener:');
+    tizen.content.unsetChangeListener();
+  } catch (err) {
     debug(err.name);
   }
 }