From: Mateusz Bruno-Kaminski Date: Thu, 4 Aug 2016 11:23:09 +0000 (+0200) Subject: [Content] Apply changed Native API for changeListener X-Git-Tag: submit/tizen/20160831.012604~8^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8b0da034a4f5152b9097eefc3181a6a8f26dc0d9;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Content] Apply changed Native API for changeListener [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 --- diff --git a/src/content/content_instance.cc b/src/content/content_instance.cc index 9008f68..8491b1d 100755 --- a/src/content/content_instance.cc +++ b/src/content/content_instance.cc @@ -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(user_data); + + picojson::value result = picojson::value(picojson::object()); + picojson::object& obj = result.get(); + + 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(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(listener_data_)).IsError()) { LogAndReportError( common::PlatformResult(common::ErrorCode::UNKNOWN_ERR, "The callback did not register properly"), &out); } - if (ContentManager::getInstance()->setV2ChangeListener(¬i_handle_, - changedContentV2Callback, - static_cast(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(¬i_handle_, + changedContentV2Callback, + static_cast(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(¬i_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 } } diff --git a/src/content/content_instance.h b/src/content/content_instance.h index a631e62..af1b717 100755 --- a/src/content/content_instance.h +++ b/src/content/content_instance.h @@ -17,6 +17,8 @@ #ifndef CONTENT_CONTENT_INSTANCE_H_ #define CONTENT_CONTENT_INSTANCE_H_ +#include + #include #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_; }; diff --git a/src/content/content_manager.cc b/src/content/content_manager.cc index 28140da..01d631a 100644 --- a/src/content/content_manager.cc +++ b/src/content/content_manager.cc @@ -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& user_data) { LoggerD("Enter"); diff --git a/src/content/content_manager.h b/src/content/content_manager.h index 60a177f..5c8ed6d 100644 --- a/src/content/content_manager.h +++ b/src/content/content_manager.h @@ -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); diff --git a/src/content/js/manager.js b/src/content/js/manager.js index 00ffadd..6498d59 100755 --- a/src/content/js/manager.js +++ b/src/content/js/manager.js @@ -14,6 +14,10 @@ * 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) {