[Content] Apply changed Native API for changeListener 60/82960/11
authorMateusz Bruno-Kaminski <m.bruno2@samsung.com>
Thu, 4 Aug 2016 11:23:09 +0000 (13:23 +0200)
committerMateusz Bruno-Kaminski <m.bruno2@samsung.com>
Fri, 19 Aug 2016 13:14:33 +0000 (15:14 +0200)
[Details] Replace set/unsetChangeListener with add/removeChangeListener.

[Verification] Code compiles and passes TCT content test suite.

Change-Id: I95d2d401586a031024894f760e113ae8ce885b5b
Signed-off-by: Mateusz Bruno-Kaminski <m.bruno2@samsung.com>
src/content/content_instance.cc
src/content/content_instance.h
src/content/content_manager.cc
src/content/content_manager.h
src/content/js/manager.js

index 9008f68..8491b1d 100755 (executable)
@@ -31,6 +31,8 @@
 #include "content/content_manager.h"
 #include "common/filesystem/filesystem_provider.h"
 
+using namespace common;
+
 namespace extension {
 namespace content {
 
@@ -47,7 +49,9 @@ using common::PlatformResult;
 
 ContentInstance::ContentInstance() :
     noti_handle_(nullptr),
-    listener_data_(nullptr) {
+    listener_handle_(nullptr),
+    listener_data_(nullptr),
+    callback_data_(nullptr) {
   using std::placeholders::_1;
   using std::placeholders::_2;
 
@@ -59,6 +63,8 @@ ContentInstance::ContentInstance() :
   REGISTER_SYNC("ContentManager_scanFile", ContentManagerScanfile);
   REGISTER_SYNC("ContentManager_scanDirectory", ContentManagerScanDirectory);
   REGISTER_SYNC("ContentManager_cancelScanDirectory", ContentManagerCancelScanDirectory);
+  REGISTER_SYNC("ContentManager_addChangeListener", ContentManagerAddChangeListener);
+  REGISTER_SYNC("ContentManager_removeChangeListener", ContentManagerRemoveChangeListener);
   REGISTER_SYNC("ContentManager_unsetChangeListener", ContentManagerUnsetchangelistener);
   REGISTER_SYNC("ContentManager_setChangeListener", ContentManagerSetchangelistener);
   REGISTER_SYNC("ContentManager_getDirectories", ContentManagerGetdirectories);
@@ -88,14 +94,27 @@ ContentInstance::ContentInstance() :
 
 ContentInstance::~ContentInstance() {
   LoggerD("entered");
+
   if (noti_handle_) {
     media_content_remove_db_updated_cb(noti_handle_);
     noti_handle_ = nullptr;
   }
+
+  if (listener_handle_) {
+    media_content_remove_db_updated_cb(listener_handle_);
+    listener_handle_ = nullptr;
+  }
+
   if (listener_data_) {
     delete listener_data_;
     listener_data_ = nullptr;
   }
+
+  if (callback_data_) {
+    delete callback_data_;
+    callback_data_ = nullptr;
+  }
+
   ContentManager::getInstance()->setContentInstance(nullptr);
 }
 
@@ -226,8 +245,8 @@ static void ScanDirectoryCallback(media_content_error_e error, void* user_data)
   common::Instance::PostMessage(cbData->instance, picojson::value(out).serialize().c_str());
 }
 
-
-static void changedContentCallback(media_content_error_e error,
+// DEPRECATED CALLBACK. contentChangeCallback() is currently in use
+static void changedContentV1Callback(media_content_error_e error,
                                    int pid,
                                    media_content_db_update_item_type_e update_item,
                                    media_content_db_update_type_e update_type,
@@ -285,6 +304,7 @@ static void changedContentCallback(media_content_error_e error,
   }
 }
 
+// DEPRECATED CALLBACK. contentChangeCallback() is currently in use
 static void changedContentV2Callback(media_content_error_e error,
                                    int pid,
                                    media_content_db_update_item_type_e update_item,
@@ -343,6 +363,137 @@ static void changedContentV2Callback(media_content_error_e error,
   }
 }
 
+static PlatformResult prepareDirectoryChangeResponse(media_content_db_update_type_e update_type,
+                                                     char* uuid,
+                                                     picojson::object& obj) {
+  LoggerD("Media item is a directory");
+
+  if (MEDIA_CONTENT_DELETE == update_type) {
+    ReportSuccess(picojson::value(std::string(uuid)), obj);
+    obj["state"] = picojson::value("oncontentdirremoved");
+    return PlatformResult(ErrorCode::NO_ERROR);
+  }
+
+  media_folder_h folder = nullptr;
+  int ret = media_folder_get_folder_from_db(uuid, &folder);
+
+  if (MEDIA_CONTENT_ERROR_NONE != ret || nullptr == folder) {
+    LoggerE("Failed to get media item (media_folder_get_folder_from_db): %d", ret);
+    return PlatformResult(ErrorCode::ABORT_ERR);
+  }
+
+  picojson::object o;
+  ContentDirToJson(folder, o);
+
+  ret = media_folder_destroy(folder);
+
+  if (MEDIA_CONTENT_ERROR_NONE != ret) {
+    LoggerE("Failed to destroy media folder (media_folder_destroy): %d", ret);
+    return PlatformResult(ErrorCode::ABORT_ERR);
+  }
+
+  ReportSuccess(picojson::value(o), obj);
+
+  if (MEDIA_CONTENT_INSERT == update_type) {
+    obj["state"] = picojson::value("oncontentdiradded");
+  } else if (MEDIA_CONTENT_UPDATE == update_type) {
+    obj["state"] = picojson::value("oncontentdirupdated");
+  }
+
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+static PlatformResult prepareFileChangeResponse(media_content_db_update_type_e update_type,
+                                                char* uuid,
+                                                picojson::object& obj) {
+  LoggerD("Media item is a file");
+
+  if (MEDIA_CONTENT_DELETE == update_type) {
+    ReportSuccess(picojson::value(std::string(uuid)), obj);
+    obj["state"] = picojson::value("oncontentremoved");
+    return PlatformResult(ErrorCode::NO_ERROR);
+  }
+
+  media_info_h media = nullptr;
+  int ret = media_info_get_media_from_db(uuid, &media);
+
+  if (MEDIA_CONTENT_ERROR_NONE != ret || nullptr == media) {
+    LoggerE("Failed to get media item (media_info_get_media_from_db): %d", ret);
+    return PlatformResult(ErrorCode::ABORT_ERR);
+  }
+
+  picojson::object o;
+  ContentToJson(media, o);
+
+  ret = media_info_destroy(media);
+
+  if (MEDIA_CONTENT_ERROR_NONE != ret) {
+    LoggerE("Failed to destroy media info (media_info_destroy): %d", ret);
+    return PlatformResult(ErrorCode::ABORT_ERR);
+  }
+
+  ReportSuccess(picojson::value(o), obj);
+
+  if (MEDIA_CONTENT_INSERT == update_type) {
+    obj["state"] = picojson::value("oncontentadded");
+  } else if (MEDIA_CONTENT_UPDATE == update_type) {
+    obj["state"] = picojson::value("oncontentupdated");
+  }
+
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+static void contentChangeCallback(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) {
+  LoggerD("Entered directory and file change callback");
+
+  if (MEDIA_CONTENT_ERROR_NONE != error) {
+    LoggerE("Failed to perform contentChangeCallback: %d", error);
+    return;
+  }
+
+  if (!uuid) {
+    LoggerE("Provided uuid is NULL, ignoring");
+    return;
+  }
+
+  if (nullptr == user_data) {
+    LoggerE("Provided user data is NULL, ignoring");
+    return;
+  }
+
+  if (MEDIA_ITEM_DIRECTORY != update_item && MEDIA_ITEM_FILE != update_item) {
+    LoggerD("Media item is not a directory nor a file, skipping");
+    return;
+  }
+
+  ReplyCallbackData* cbData = static_cast<ReplyCallbackData*>(user_data);
+
+  picojson::value result = picojson::value(picojson::object());
+  picojson::object& obj = result.get<picojson::object>();
+
+  PlatformResult ret(ErrorCode::NO_ERROR);
+  if (MEDIA_ITEM_DIRECTORY == update_item) {
+    ret = prepareDirectoryChangeResponse(update_type, uuid, obj);
+  } else if (MEDIA_ITEM_FILE == update_item) {
+    ret = prepareFileChangeResponse(update_type, uuid, obj);
+  }
+
+  if (ret.IsSuccess()) {
+    obj["listenerId"] = picojson::value("ContentManagerChangeCallback");
+    Instance::PostMessage(cbData->instance, result.serialize().c_str());
+  } else {
+    LoggerD("Failed to prepare content change callback, ignoring");
+  }
+}
+
 #define CHECK_EXIST(args, name, out) \
   if (!args.contains(name)) {\
     LogAndReportError(common::PlatformResult(common::ErrorCode::TYPE_MISMATCH_ERR, (name" is required argument")), &out);\
@@ -482,10 +633,52 @@ void ContentInstance::ContentManagerCancelScanDirectory(const picojson::value& a
   }
 }
 
+void ContentInstance::ContentManagerAddChangeListener(const picojson::value& args,
+                                                      picojson::object& out) {
+  LoggerD("Entered");
+
+  callback_data_ = new ReplyCallbackData();
+  callback_data_->instance = this;
+  callback_data_->args = args;
+
+  if (ContentManager::getInstance()->isConnected()) {
+    callback_data_->cbType = ContentManagerAddChangeListenerCallback;
+  } else {
+    callback_data_->cbType = ContentManagerErrorCallback;
+  }
+
+  PlatformResult result =
+      ContentManager::getInstance()->addChangeListener(&listener_handle_,
+                                                       contentChangeCallback,
+                                                       static_cast<void*>(callback_data_));
+
+  if (result.IsError()) {
+    delete callback_data_;
+    callback_data_ = nullptr;
+    LogAndReportError(result, &out);
+  }
+}
+
+void ContentInstance::ContentManagerRemoveChangeListener(const picojson::value& args,
+                                                         picojson::object& out) {
+  LoggerD("Entered");
+
+  PlatformResult result = ContentManager::getInstance()->removeChangeListener(listener_handle_);
+
+  if (result.IsSuccess()) {
+    listener_handle_ = nullptr;
+    delete callback_data_;
+    callback_data_ = nullptr;
+  } else {
+    LogAndReportError(result, &out);
+  }
+}
 
 void ContentInstance::ContentManagerSetchangelistener(const picojson::value& args,
                                                       picojson::object& out) {
   LoggerD("entered");
+  LoggerW("DEPRECATION WARNING: setChangeListener() is deprecated and will be removed from next release. "
+      "Use addChangeListener() instead.");
   //CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
 
@@ -503,29 +696,36 @@ void ContentInstance::ContentManagerSetchangelistener(const picojson::value& arg
     listener_data_->cbType = ContentManagerErrorCallback;
   }
 
-  if (ContentManager::getInstance()->setChangeListener(changedContentCallback,
+  if (ContentManager::getInstance()->setChangeListener(changedContentV1Callback,
                                                        static_cast<void*>(listener_data_)).IsError()) {
     LogAndReportError(
         common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, "The callback did not register properly"), &out);
   }
-  if (ContentManager::getInstance()->setV2ChangeListener(&noti_handle_,
-                                                       changedContentV2Callback,
-                                                       static_cast<void*>(listener_data_)).IsError()) {
-    LogAndReportError(
-        common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, "The callback did not register properly"), &out);
+
+  if (nullptr == noti_handle_) { // To remain consistency with the previous implementation
+    if (ContentManager::getInstance()->addChangeListener(&noti_handle_,
+                                                         changedContentV2Callback,
+                                                         static_cast<void*>(listener_data_)).IsError()) {
+      LogAndReportError(
+          common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, "The callback did not register properly"), &out);
+    }
   }
 }
 
 void ContentInstance::ContentManagerUnsetchangelistener(const picojson::value& args, picojson::object& out) {
   LoggerD("entered");
+  LoggerW("DEPRECATION WARNING: unsetChangeListener() is deprecated and will be removed from next release. "
+        "Use removeChangeListener() instead.");
   //CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
 
   if (ContentManager::getInstance()->unSetChangeListener().IsError()) {
     LoggerD("unsuccesfull deregistering of callback");
   }
-  if (ContentManager::getInstance()->unSetV2ChangeListener(&noti_handle_).IsError()) {
+  if (ContentManager::getInstance()->removeChangeListener(noti_handle_).IsError()) {
     LoggerD("unsuccesfull deregistering of callback");
+  } else {
+    noti_handle_ = nullptr; // To remain consistency with the previous implementation
   }
 }
 
index a631e62..af1b717 100755 (executable)
@@ -17,6 +17,8 @@
 #ifndef CONTENT_CONTENT_INSTANCE_H_
 #define CONTENT_CONTENT_INSTANCE_H_
 
+#include <map>
+
 #include <media_content_internal.h>
 #include "common/extension.h"
 
@@ -26,6 +28,8 @@ namespace content {
 enum ContentCallbacks {
   ContentManagerFindCallback,
   ContentManagerScanfileCallback,
+  ContentManagerAddChangeListenerCallback,
+  ContentManagerRemoveChangeListenerCallback,
   ContentManagerUnsetchangelistenerCallback,
   ContentManagerSetchangelistenerCallback,
   ContentManagerGetdirectoriesCallback,
@@ -71,6 +75,8 @@ class ContentInstance : public common::ParsedInstance {
   void ContentManagerScanfile(const picojson::value& args, picojson::object& out);
   void ContentManagerScanDirectory(const picojson::value& args, picojson::object& out);
   void ContentManagerCancelScanDirectory(const picojson::value& args, picojson::object& out);
+  void ContentManagerAddChangeListener(const picojson::value& args, picojson::object& out);
+  void ContentManagerRemoveChangeListener(const picojson::value& args, picojson::object& out);
   void ContentManagerSetchangelistener(const picojson::value& args, picojson::object& out);
   void ContentManagerUnsetchangelistener(const picojson::value& args, picojson::object& out);
   void ContentManagerGetplaylists(const picojson::value& args, picojson::object& out);
@@ -92,8 +98,11 @@ class ContentInstance : public common::ParsedInstance {
   void PlaylistSetThumbnailUri(const picojson::value& args, picojson::object& out);
   void PlaylistGetNumberOfTracks(const picojson::value& args, picojson::object& out);
 
-  media_content_noti_h noti_handle_;
-  ReplyCallbackData* listener_data_;
+  media_content_noti_h noti_handle_; // Deprecated handle
+  media_content_noti_h listener_handle_;
+
+  ReplyCallbackData* listener_data_; // Deprecated callback data
+  ReplyCallbackData* callback_data_;
 };
 
 
index 28140da..01d631a 100644 (file)
@@ -889,63 +889,68 @@ PlatformResult ContentManager::cancelScanDirectory(const std::string& content_di
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
-PlatformResult ContentManager::setChangeListener(media_content_db_update_cb callback,
+PlatformResult ContentManager::addChangeListener(media_content_noti_h* noti_handle,
+                                                 media_content_db_update_cb callback,
                                                  void *user_data) {
   LoggerD("Enter");
 
-  int ret = media_content_set_db_updated_cb(callback, user_data);
-  if(ret != MEDIA_CONTENT_ERROR_NONE) {
+  int ret = media_content_add_db_updated_cb(callback, user_data, noti_handle);
+
+  if (MEDIA_CONTENT_ERROR_NONE != ret) {
     return LogAndCreateResult(
-              ErrorCode::UNKNOWN_ERR, "registering the listener is failed.",
-              ("Failed: registering the listener is failed %d (%s)", ret, get_error_message(ret)));
+        ErrorCode::ABORT_ERR,
+        "Failed to add the listener.",
+        ("Failed to add the listener: %d (%s)", ret, get_error_message(ret)));
   }
+
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
-PlatformResult ContentManager::unSetChangeListener() {
+PlatformResult ContentManager::removeChangeListener(media_content_noti_h noti_handle) {
   LoggerD("Enter");
 
-  int ret = media_content_unset_db_updated_cb();
-  if(ret != MEDIA_CONTENT_ERROR_NONE) {
-    return LogAndCreateResult(
-              ErrorCode::UNKNOWN_ERR, "unregistering the listener is failed.",
-              ("Failed: unregistering the listener is failed: %d (%s)", ret, get_error_message(ret)));
+  int ret = media_content_remove_db_updated_cb(noti_handle);
+
+  switch (ret) {
+    case MEDIA_CONTENT_ERROR_NONE:
+      return PlatformResult(ErrorCode::NO_ERROR);
+    case MEDIA_CONTENT_ERROR_INVALID_PARAMETER:
+      // Trying to remove non-existent listener, ignoring
+      LoggerI("Failed to remove the listener: %d (%s)", ret, get_error_message(ret));
+      return PlatformResult(ErrorCode::NO_ERROR);
+    default:
+      return LogAndCreateResult(
+          ErrorCode::ABORT_ERR,
+          "Failed to remove the listener.",
+          ("Failed to remove the listener: %d (%s)", ret, get_error_message(ret)));
   }
-  return PlatformResult(ErrorCode::NO_ERROR);
 }
 
-PlatformResult ContentManager::setV2ChangeListener(media_content_noti_h* noti_handle,
-                                                 media_content_db_update_cb callback,
+PlatformResult ContentManager::setChangeListener(media_content_db_update_cb callback,
                                                  void *user_data) {
   LoggerD("Enter");
-  if (nullptr == *noti_handle) {
-    int ret = media_content_add_db_updated_cb(callback, user_data, noti_handle);
-    if(ret != MEDIA_CONTENT_ERROR_NONE) {
-      return LogAndCreateResult(
-                ErrorCode::UNKNOWN_ERR, "registering the listener is failed.",
-                ("Failed: registering the listener of cb_v2 is failed: %d (%s)",
-                    ret, get_error_message(ret)));
-    }
+
+  int ret = media_content_set_db_updated_cb(callback, user_data);
+  if(ret != MEDIA_CONTENT_ERROR_NONE) {
+    return LogAndCreateResult(
+              ErrorCode::UNKNOWN_ERR, "registering the listener is failed.",
+              ("Failed: registering the listener is failed %d (%s)", ret, get_error_message(ret)));
   }
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
-PlatformResult ContentManager::unSetV2ChangeListener(media_content_noti_h* noti_handle) {
+PlatformResult ContentManager::unSetChangeListener() {
   LoggerD("Enter");
 
-  int ret = media_content_remove_db_updated_cb(*noti_handle);
+  int ret = media_content_unset_db_updated_cb();
   if(ret != MEDIA_CONTENT_ERROR_NONE) {
     return LogAndCreateResult(
               ErrorCode::UNKNOWN_ERR, "unregistering the listener is failed.",
-              ("Failed: unregistering the listener of cb_v2 is failed %d (%s)",
-                  ret, get_error_message(ret)));
+              ("Failed: unregistering the listener is failed: %d (%s)", ret, get_error_message(ret)));
   }
-  *noti_handle = nullptr;
-
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
-
 void ContentManager::createPlaylist(std::string name,
   const std::shared_ptr<ReplyCallbackData>& user_data) {
   LoggerD("Enter");
index 60a177f..5c8ed6d 100644 (file)
@@ -54,13 +54,13 @@ class ContentManager {
   int scanFile(std::string& uri);
   common::PlatformResult scanDirectory(media_scan_completed_cb callback, ReplyCallbackData* cbData);
   common::PlatformResult cancelScanDirectory(const std::string& content_dir_uri);
+  common::PlatformResult addChangeListener(media_content_noti_h* noti_handle,
+                                           media_content_db_update_cb callback,
+                                           void *user_data);
+  common::PlatformResult removeChangeListener(media_content_noti_h noti_handle);
   common::PlatformResult setChangeListener(media_content_db_update_cb callback,
                                            void *user_data);
   common::PlatformResult unSetChangeListener();
-  common::PlatformResult setV2ChangeListener(media_content_noti_h* noti_handler,
-                                           media_content_db_update_cb callback,
-                                           void *user_data);
-  common::PlatformResult unSetV2ChangeListener(media_content_noti_h* noti_handler);
 
 //Lyrics
   int getLyrics(const picojson::value& args,picojson::object& result);
index 00ffadd..6498d59 100755 (executable)
  *    limitations under the License.
  */
 
+var T_ = xwalk.utils.type;
+var CONTENT_MANAGER_LISTENER_ID = 'ContentManagerChangeCallback';
+
+
 function _ContentManagerChangeCallback(result) {
   if (result.state === 'oncontentadded' || result.state === 'oncontentupdated') {
     var content = native_.getResultObject(result);
@@ -26,6 +30,78 @@ function _ContentManagerChangeCallback(result) {
   }
 }
 
+
+var ContentListenersManager = (function() {
+
+  function changeContent(event) {
+    if (T_.isEmptyObject(this.listeners)) {
+      return;
+    }
+
+    var result = null;
+
+    if (event.state === 'oncontentadded' || event.state === 'oncontentupdated') {
+      result = createContentObject_(native_.getResultObject(event));
+    } else if (event.state === 'oncontentdiradded' || event.state === 'oncontentdirupdated') {
+      result = createContentDirObject_(native_.getResultObject(event));
+    } else if (event.state === 'oncontentremoved' || event.state === 'oncontentdirremoved') {
+      result = native_.getResultObject(event);
+    }
+
+    if (!result) {
+      return;
+    }
+
+    var callback;
+    for (var listenerId in this.listeners) {
+      if (this.listeners.hasOwnProperty(listenerId)) {
+        callback = this.listeners[listenerId];
+        if (T_.isFunction(callback[event.state])) {
+          callback[event.state](result);
+        }
+      }
+    }
+  }
+
+  function _ContentListenersManager() {
+    this.listeners = {};
+    this.lastListenerId = 0;
+    this.changeContent = changeContent.bind(this);
+  }
+
+  _ContentListenersManager.prototype.addChangeListener = function(changeCallback) {
+    if (T_.isEmptyObject(this.listeners)) {
+      var result = native_.callSync('ContentManager_addChangeListener');
+      if (native_.isFailure(result)) {
+        throw native_.getErrorObject(result);
+      }
+
+      native_.addListener(CONTENT_MANAGER_LISTENER_ID, this.changeContent);
+    }
+
+    this.listeners[++this.lastListenerId] = changeCallback;
+    return this.lastListenerId;
+  };
+
+  _ContentListenersManager.prototype.removeChangeListener = function(listenerId) {
+    delete this.listeners[listenerId];
+
+    if (T_.isEmptyObject(this.listeners)) {
+      var result = native_.callSync('ContentManager_removeChangeListener');
+      if (native_.isFailure(result)) {
+        throw native_.getErrorObject(result);
+      }
+
+      native_.removeListener(CONTENT_MANAGER_LISTENER_ID);
+    }
+  };
+
+  return _ContentListenersManager;
+})();
+
+var listenersManager = new ContentListenersManager();
+
+
 function ContentManager() {
 }
 
@@ -239,14 +315,36 @@ ContentManager.prototype.cancelScanDirectory = function(contentDirURI) {
   }
 };
 
+ContentManager.prototype.addChangeListener = function() {
+  var args = validator_.validateArgs(arguments, [{
+    name: 'changeCallback',
+    type: types_.LISTENER,
+    values: ['oncontentadded', 'oncontentupdated', 'oncontentremoved', 'oncontentdiradded', 'oncontentdirupdated', 'oncontentdirremoved']
+  }]);
+
+  return listenersManager.addChangeListener(args.changeCallback);
+};
+
+ContentManager.prototype.removeChangeListener = function() {
+  var args = validator_.validateArgs(arguments, [{
+    name: 'listenerId',
+    type: types_.LONG
+  }]);
+
+  listenersManager.removeChangeListener(args.listenerId);
+};
+
 ContentManager.prototype.setChangeListener = function(changeCallback) {
+  console.warn('DEPRECATION WARNING: setChangeListener() is deprecated and will be removed '
+      + 'from next release. Use addChangeListener() instead.');
+
   var args = validator_.validateArgs(arguments, [{
     name: 'changeCallback',
     type: types_.LISTENER,
     values: ['oncontentadded', 'oncontentupdated', 'oncontentremoved', 'oncontentdiradded', 'oncontentdirupdated', 'oncontentdirremoved']
   }]);
 
-  var listenerId = 'ContentManagerChangeCallback';
+  var listenerId = 'ContentManagerChangeCallback_';
 
   var data = {
     listenerId: listenerId
@@ -261,17 +359,20 @@ ContentManager.prototype.setChangeListener = function(changeCallback) {
     oncontentdirremoved: args.changeCallback.oncontentdirremoved
   };
 
-  native_.addListener('ContentManagerChangeCallback',
-      _ContentManagerChangeCallback.bind(callbacks));
-
   var result = native_.callSync('ContentManager_setChangeListener', data);
 
   if (native_.isFailure(result)) {
     throw native_.getErrorObject(result);
   }
+
+  native_.addListener('ContentManagerChangeCallback_',
+      _ContentManagerChangeCallback.bind(callbacks));
 };
 
 ContentManager.prototype.unsetChangeListener = function() {
+  console.warn('DEPRECATION WARNING: unsetChangeListener() is deprecated and will be removed '
+      + 'from next release. Use removeChangeListener() instead.');
+
   var data = {};
 
   var result = native_.callSync('ContentManager_unsetChangeListener', data);
@@ -279,6 +380,8 @@ ContentManager.prototype.unsetChangeListener = function() {
   if (native_.isFailure(result)) {
     throw native_.getErrorObject(result);
   }
+
+  native_.removeListener('ContentManagerChangeCallback_');
 };
 
 ContentManager.prototype.getPlaylists = function(successCallback, errorCallback) {