From ce560f81daf327fdcbd585810d3492d8dc8bce38 Mon Sep 17 00:00:00 2001 From: Michal Michalski Date: Fri, 21 Dec 2018 20:35:17 +0100 Subject: [PATCH] [widgetservice][utils] Common implementation for ListenerManager. I extracted broken ListenerManager implementation from widgetservice module fixed it, and placed it inside utils_api.js module as CommonListenerManager. This implementation has customizable behavior if removeListener cannot find listener for given id, so you can throw exception, do nothing, or return error object. [Verification] Test usage of this CommonListenerManager implementation is provided in this commit using widgetservice widgetStateChangeListener which I replaced. Manual tests were performed to verify that the implementation behaves as expected. Change-Id: I0e18e091b9ad8cacc685663c65f3358cc022188d --- src/utils/utils_api.js | 79 +++++++++++++++++++++++++- src/widgetservice/widgetservice_api.js | 66 +++++++-------------- 2 files changed, 96 insertions(+), 49 deletions(-) diff --git a/src/utils/utils_api.js b/src/utils/utils_api.js index 83f18c9a..d642eb8c 100644 --- a/src/utils/utils_api.js +++ b/src/utils/utils_api.js @@ -29,6 +29,77 @@ var signature_to_type = { 'o': 'object' }; + +/** + * @brief CommonListenerManager constructor function. + * @param nativeMgr NativeManager handle. + * @param managerName Name which will be used as native listener name. + * @param onIdNotFound Callback without arguments invoked when trying to remove not existing listener. If unspecified it is replaced by empty function. + */ +var CommonListenerManager = function(nativeMgr, managerName, onIdNotFound) { + this.listeners = {} + this.nextId = 1; + this.numberOfListeners = 0; + this.name = managerName; + this.native = nativeMgr; + this.hasNativeListener = false; + if (onIdNotFound === undefined) { + this.idNotFoundBehavior = function() {}; + } else { + this.idNotFoundBehavior = onIdNotFound; + } +} + +/** + * @brief Callback for native listener, which invokes all existing listeners. + * @param msg native Arguments object for native listener callback. + */ +CommonListenerManager.prototype.onListenerCalled = function(msg) { + for (var watchId in this.listeners) { + if (this.listeners.hasOwnProperty(watchId)) { + this.listeners[watchId](this.native.getResultObject(msg)); + } + } +}; + +/** + * @brief Registers new listener. + * @param callback Callback function taking single argument. + * @retval integer listener id. Use it later to remove listener. + */ +CommonListenerManager.prototype.addListener = function(callback) { + if (!this.hasNativeListener) { + this.native.addListener(this.name, this.onListenerCalled.bind(this)); + this.hasNativeListener = true; + } + var id = this.nextId++; + this.listeners[id] = callback; + this.numberOfListeners++; + return id; +} + +/** + * @brief Remove previously registered listener. + * + * If listener for given id is not found, onIdNotFound callback is invoked. + * + * @param watchId Listener id returned by addListener. + * @retval onIdNotFoundCallback return value. + */ +CommonListenerManager.prototype.removeListener = function(watchId) { + if (this.listeners.hasOwnProperty(watchId)) { + delete this.listeners[watchId]; + this.numberOfListeners--; + if (this.numberOfListeners == 0) { + this.native.removeListener(this.name); + this.hasNativeListener = false; + this.nextId = 1; + } + } else { + return this.idNotFoundBehavior(); + } +} + var DateConverter = function() {}; DateConverter.prototype.toTZDate = function(v, isAllDay) { @@ -70,10 +141,10 @@ var _dateConverter = new DateConverter(); /** @constructor */ function Utils() { - + /** - * Cynara(since tizen 3.0) only support native privilege. - * simply web privilege convert native privilege for checking access. + * Cynara(since tizen 3.0) only support native privilege. + * simply web privilege convert native privilege for checking access. */ var privilege = { ACCOUNT_READ: 'http://tizen.org/privilege/account.read', @@ -1385,6 +1456,7 @@ Utils.prototype.type = _type; Utils.prototype.converter = _converter; Utils.prototype.validator = _validator; Utils.prototype.NativeManager = NativeManager; +Utils.prototype.CommonListenerManager = CommonListenerManager; var native_ = new NativeManager(extension); @@ -1394,4 +1466,5 @@ Object.freeze(exports); Object.freeze(exports.utils); Object.freeze(Utils.prototype); Object.freeze(NativeManager.prototype); +Object.freeze(CommonListenerManager.prototype); diff --git a/src/widgetservice/widgetservice_api.js b/src/widgetservice/widgetservice_api.js index e09d3fb0..0126865e 100644 --- a/src/widgetservice/widgetservice_api.js +++ b/src/widgetservice/widgetservice_api.js @@ -184,7 +184,14 @@ WidgetInstance.prototype.getContent = function() { } }; + +var widgetToListenerManagerMapping = {}; + function Widget(data) { + if (!widgetToListenerManagerMapping.hasOwnProperty(data.id)) { + widgetToListenerManagerMapping[data.id] = new xwalk.utils.CommonListenerManager( + native, 'WidgetStateChangeCallback_' + data.id); + } Object.defineProperties(this, { id: { value: data.id, @@ -210,7 +217,7 @@ function Widget(data) { value: data.noDisplay, writable: false, enumerable: true - }, + } }); }; @@ -316,51 +323,17 @@ Widget.prototype.getVariants = function() { } }; -function ListenerManager(native, listenerName) { - this.listeners = {}; - this.nextId = 1; - this.nativeSet = false; - this.native = native; - this.listenerName = listenerName; -}; - -ListenerManager.prototype.onListenerCalled = function(msg) { - for (var watchId in this.listeners) { - if (this.listeners.hasOwnProperty(watchId) ) { - this.listeners[watchId](this.native.getResultObject(msg)); - } - } -}; - -ListenerManager.prototype.addListener = function(callback) { - var id = this.nextId; - if (!this.nativeSet) { - this.native.addListener(this.listenerName, this.onListenerCalled.bind(this)); - this.nativeSet = true; - } - this.listeners[id] = callback; - ++this.nextId; - return id; -}; - -ListenerManager.prototype.removeListener = function(watchId) { - if (this.listeners.hasOwnProperty(watchId)) { - delete this.listeners[watchId]; - } -}; - -var WIDGET_STATE_CHANGE_LISTENER = 'WidgetStateChangeCallback'; -var widgetStateChangeListener = new ListenerManager(native, WIDGET_STATE_CHANGE_LISTENER); - Widget.prototype.addStateChangeListener = function() { var args = validator.validateMethod(arguments, [{ name : 'eventCallback', type : types.FUNCTION, }]); - var result = native.callSync('Widget_addStateChangeListener', {widgetId : this.id}); - if (native.isFailure(result)) { - throw native.getErrorObject(result); + if (widgetToListenerManagerMapping[this.id].numberOfListeners == 0) { + var result = native.callSync('Widget_addStateChangeListener', {widgetId : this.id}); + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } } var func = function(msg) { @@ -369,7 +342,7 @@ Widget.prototype.addStateChangeListener = function() { } }.bind(this); - return widgetStateChangeListener.addListener(func); + return widgetToListenerManagerMapping[this.id].addListener(func); }; Widget.prototype.removeStateChangeListener = function() { @@ -378,11 +351,12 @@ Widget.prototype.removeStateChangeListener = function() { type : types.LONG, }]); - widgetStateChangeListener.removeListener(args.watchId); - - var result = native.callSync('Widget_removeStateChangeListener', {widgetId : this.id}); - if (native.isFailure(result)) { - throw native.getErrorObject(result); + widgetToListenerManagerMapping[this.id].removeListener(args.watchId); + if (widgetToListenerManagerMapping[this.id].numberOfListeners == 0) { + var result = native.callSync('Widget_removeStateChangeListener', {widgetId : this.id}); + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } } }; -- 2.34.1