f023dea14d0fb949fac3fb23f3b9a958d0342a8d
[platform/core/api/webapi-plugins.git] / src / humanactivitymonitor / humanactivitymonitor_api.js
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 var utils_ = xwalk.utils;
18 var privilege_ = utils_.privilege;
19 var type_ = utils_.type;
20 var converter_ = utils_.converter;
21 var validator_ = utils_.validator;
22 var types_ = validator_.Types;
23 var native_ = new xwalk.utils.NativeManager(extension);
24
25 var callbackId = 0;
26 var callbacks = {};
27
28 function nextCallbackId() {
29     return callbackId++;
30 }
31
32 function SetReadOnlyProperty(obj, n, v) {
33     Object.defineProperty(obj, n, { value: v, writable: false });
34 }
35
36 var ACCUMULATIVE_PEDOMETER_DATA = 'ACCUMULATIVE_PEDOMETER_DATA';
37 var MIN_OPTION_INTERVAL = 0;
38 var MIN_OPTION_RETENTION_PERIOD = 1;
39 var MIN_QUERY_TIME = 0;
40 var MIN_QUERY_INTERVAL = 0;
41
42 var HumanActivityType = {
43     PEDOMETER: 'PEDOMETER',
44     WRIST_UP: 'WRIST_UP',
45     HRM: 'HRM',
46     GPS: 'GPS',
47     SLEEP_MONITOR: 'SLEEP_MONITOR',
48     SLEEP_DETECTOR: 'SLEEP_DETECTOR',
49     STRESS_MONITOR: 'STRESS_MONITOR'
50 };
51
52 var HumanActivityRecorderType = {
53     PEDOMETER: 'PEDOMETER',
54     HRM: 'HRM',
55     SLEEP_MONITOR: 'SLEEP_MONITOR',
56     PRESSURE: 'PRESSURE'
57 };
58
59 var PedometerStepStatus = {
60     NOT_MOVING: 'NOT_MOVING',
61     WALKING: 'WALKING',
62     RUNNING: 'RUNNING',
63     UNKNOWN: 'UNKNOWN'
64 };
65
66 var ActivityRecognitionType = {
67     STATIONARY: 'STATIONARY',
68     WALKING: 'WALKING',
69     RUNNING: 'RUNNING',
70     IN_VEHICLE: 'IN_VEHICLE'
71 };
72
73 var ActivityAccuracy = {
74     LOW: 'LOW',
75     MEDIUM: 'MEDIUM',
76     HIGH: 'HIGH'
77 };
78
79 var SleepStatus = {
80     ASLEEP: 'ASLEEP',
81     AWAKE: 'AWAKE'
82 };
83
84 var GestureType = {
85     GESTURE_DOUBLE_TAP: 'GESTURE_DOUBLE_TAP',
86     GESTURE_MOVE_TO_EAR: 'GESTURE_MOVE_TO_EAR',
87     GESTURE_NO_MOVE: 'GESTURE_NO_MOVE',
88     GESTURE_PICK_UP: 'GESTURE_PICK_UP',
89     GESTURE_SHAKE: 'GESTURE_SHAKE',
90     GESTURE_SNAP: 'GESTURE_SNAP',
91     GESTURE_TILT: 'GESTURE_TILT',
92     GESTURE_TURN_FACE_DOWN: 'GESTURE_TURN_FACE_DOWN',
93     GESTURE_WRIST_UP: 'GESTURE_WRIST_UP'
94 };
95
96 function convertActivityData(type, data) {
97     switch (type) {
98     case HumanActivityType.PEDOMETER:
99         return new HumanActivityPedometerData(data);
100     case ACCUMULATIVE_PEDOMETER_DATA:
101         return new HumanActivityAccumulativePedometerData(data);
102     case HumanActivityType.WRIST_UP:
103         return null;
104     case HumanActivityType.HRM:
105         return new HumanActivityHRMData(data);
106     case HumanActivityType.GPS:
107         var gpsInfo = [];
108         for (var i = 0, max = data.gpsInfo.length; i < max; i++) {
109             gpsInfo.push(new HumanActivityGPSInfo(data.gpsInfo[i]));
110         }
111         return new HumanActivityGPSInfoArray(gpsInfo);
112     case HumanActivityType.SLEEP_MONITOR:
113         return new HumanActivitySleepMonitorData(data);
114     case HumanActivityType.SLEEP_DETECTOR:
115         return new HumanActivitySleepDetectorData(data);
116     case HumanActivityType.STRESS_MONITOR:
117         return new HumanActivityStressMonitorData(data);
118     default:
119         utils_.error('Uknown human activity type: ' + type);
120     }
121 }
122
123 function createRecorderData(func, data) {
124     var array = [];
125
126     data.forEach(function(d) {
127         array.push(new func(d));
128     });
129
130     return array;
131 }
132
133 function convertActivityRecorderData(type, data) {
134     var func = undefined;
135     switch (type) {
136     case HumanActivityRecorderType.PEDOMETER:
137         func = HumanActivityRecorderPedometerData;
138         break;
139     case HumanActivityRecorderType.HRM:
140         func = HumanActivityRecorderHRMData;
141         break;
142     case HumanActivityRecorderType.SLEEP_MONITOR:
143         func = HumanActivityRecorderSleepMonitorData;
144         break;
145     case HumanActivityRecorderType.PRESSURE:
146         func = HumanActivityRecorderPressureData;
147         break;
148     default:
149         utils_.error('Uknown human activity recorder type: ' + type);
150         return;
151     }
152
153     return createRecorderData(func, data);
154 }
155
156 function StressMonitorDataRange(label, min, max) {
157     validator_.validateConstructorCall(this, tizen.StressMonitorDataRange);
158
159     var args = validator_.validateArgs(arguments, [
160         { name: 'label', type: types_.STRING, optional: true, nullable: false },
161         { name: 'min', type: types_.UNSIGNED_LONG, optional: true, nullable: false },
162         { name: 'max', type: types_.UNSIGNED_LONG, optional: true, nullable: false }
163     ]);
164
165     var _label = !type_.isNullOrUndefined(args.label) ? args.label : '';
166     var _min = !type_.isNullOrUndefined(args.min) ? args.min : 0;
167     var _max = !type_.isNull(args.max) ? args.max : undefined;
168
169     Object.defineProperties(this, {
170         label: {
171             get: function() {
172                 return _label;
173             },
174             set: function(v) {
175                 _label = !type_.isNullOrUndefined(v) ? v : _label;
176             },
177             enumerable: true
178         },
179         min: {
180             get: function() {
181                 return _min;
182             },
183             set: function(v) {
184                 _min = !type_.isNullOrUndefined(v) ? converter_.toUnsignedLong(v) : _min;
185             },
186             enumerable: true
187         },
188         max: {
189             get: function() {
190                 return _max;
191             },
192             set: function(v) {
193                 _max = !type_.isNullOrUndefined(v) ? converter_.toUnsignedLong(v) : _max;
194             },
195             enumerable: true
196         }
197     });
198 }
199
200 function ActivityRecognitionListenerManager() {
201     this.listeners = {};
202     this.nextId = 1;
203     this.nativeSet = false;
204     this.native = native_;
205     this.listenerName = 'ActivityRecognitionListener';
206 }
207
208 ActivityRecognitionListenerManager.prototype.onListener = function(data) {
209     var watchId = data.watchId;
210
211     if (this.listeners[watchId]) {
212         if (native_.isFailure(data)) {
213             native_.callIfPossible(
214                 this.listeners[watchId].errorCallback,
215                 native_.getErrorObject(data)
216             );
217             return;
218         }
219
220         native_.callIfPossible(
221             this.listeners[watchId].listener,
222             new HumanActivityRecognitionData(native_.getResultObject(data))
223         );
224     }
225 };
226
227 ActivityRecognitionListenerManager.prototype.addListener = function(
228     watchId,
229     listener,
230     errorCallback
231 ) {
232     this.listeners[watchId] = {
233         listener: listener,
234         errorCallback: errorCallback
235     };
236
237     if (!this.nativeSet) {
238         this.native.addListener(this.listenerName, this.onListener.bind(this));
239         this.nativeSet = true;
240     }
241 };
242
243 ActivityRecognitionListenerManager.prototype.removeListener = function(watchId) {
244     if (this.listeners.hasOwnProperty(watchId)) {
245         delete this.listeners[watchId];
246         if (type_.isEmptyObject(this.listeners)) {
247             this.native.removeListener(this.listenerName);
248             this.nativeSet = false;
249         }
250     }
251 };
252
253 var activityRecognitionListener = new ActivityRecognitionListenerManager();
254
255 function HumanActivityMonitorManager() {}
256
257 HumanActivityMonitorManager.prototype.getHumanActivityData = function(
258     type,
259     successCallback,
260     errorCallback
261 ) {
262     var args = validator_.validateArgs(arguments, [
263         { name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType) },
264         { name: 'successCallback', type: types_.FUNCTION },
265         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
266     ]);
267
268     if (-1 === [HumanActivityType.HRM, HumanActivityType.PEDOMETER].indexOf(args.type)) {
269         throw new WebAPIException(WebAPIException.NOT_SUPPORTED_ERR);
270     }
271
272     var data = {
273         type: args.type
274     };
275
276     var callback = function(result) {
277         if (native_.isFailure(result)) {
278             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
279             return;
280         }
281
282         native_.callIfPossible(
283             args.successCallback,
284             convertActivityData(args.type, native_.getResultObject(result))
285         );
286     };
287
288     var result = native_.call(
289         'HumanActivityMonitorManagerGetHumanActivityData',
290         data,
291         callback
292     );
293
294     if (native_.isFailure(result)) {
295         throw native_.getErrorObject(result);
296     }
297 };
298
299 function startListener(listenerId, listener, method, data) {
300     if (!native_.isListenerSet(listenerId)) {
301         var result = native_.callSync(method, data);
302         if (native_.isFailure(result)) {
303             throw native_.getErrorObject(result);
304         }
305     }
306
307     // always set the listener, if it's another call to startListener()
308     // overwrite the old one
309     native_.addListener(listenerId, listener);
310 }
311
312 function checkPrivilegesForMethod(method, type) {
313     utils_.checkPrivilegeAccess(utils_.privilege.HEALTHINFO);
314     if ('HumanActivityMonitorManagerStop' === method && 'GPS' === type) {
315         utils_.checkPrivilegeAccess(utils_.privilege.LOCATION);
316     }
317 }
318
319 function stopListener(listenerId, method, data, doRemoval) {
320     if (!native_.isListenerSet(listenerId)) {
321         checkPrivilegesForMethod(method, data.type);
322         return;
323     }
324
325     if (doRemoval) {
326         var result = native_.callSync(method, data);
327         if (native_.isFailure(result)) {
328             throw native_.getErrorObject(result);
329         }
330
331         native_.removeListener(listenerId);
332     }
333 }
334
335 // Pedometer listener and accumulative pedometer listener are handled by a single
336 // callback. Native side sends both objects, JS side needs to pass the data to
337 // appropriate listeners.
338 var pedometerListener = null;
339 var accumulativePedometerListener = null;
340
341 function pedometerCallback(result) {
342     if (pedometerListener) {
343         pedometerListener(convertActivityData(HumanActivityType.PEDOMETER, result));
344     }
345
346     if (accumulativePedometerListener) {
347         accumulativePedometerListener(
348             convertActivityData(ACCUMULATIVE_PEDOMETER_DATA, result)
349         );
350     }
351 }
352
353 var GPSListener = null;
354 function GPSCallback(result) {
355     if (GPSListener) {
356         GPSListener(result);
357     }
358 }
359
360 var stressListener = null;
361
362 HumanActivityMonitorManager.prototype.start = function(type, changedCallback) {
363     var args = validator_.validateArgs(arguments, [
364         { name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType) },
365         {
366             name: 'changedCallback',
367             type: types_.FUNCTION,
368             optional: true,
369             nullable: true
370         },
371         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true },
372         { name: 'options', type: types_.DICTIONARY, optional: true, nullable: true }
373     ]);
374
375     if (HumanActivityType.WRIST_UP === args.type) {
376         utils_.warn(
377             'DEPRECATION WARNING: HumanActivityType.WRIST_UP is deprecated since ' +
378                 'Tizen 4.0. Use GestureType and addGestureRecognitionListener to ' +
379                 'monitor WRIST_UP gesture'
380         );
381     }
382
383     var listenerId = 'HumanActivityMonitor_' + args.type;
384     var optionsAttributes = ['callbackInterval', 'sampleInterval'],
385         options = args.options || {};
386
387     var callbackInterval = null,
388         sampleInterval = null;
389
390     switch (args.type) {
391     case HumanActivityType.GPS:
392         callbackInterval = !type_.isNullOrUndefined(options[optionsAttributes[0]])
393             ? options[optionsAttributes[0]]
394             : 150000;
395         sampleInterval = !type_.isNullOrUndefined(options[optionsAttributes[1]])
396             ? options[optionsAttributes[1]]
397             : 1000;
398         break;
399     case HumanActivityType.HRM:
400         callbackInterval = !type_.isNullOrUndefined(options[optionsAttributes[0]])
401             ? options[optionsAttributes[0]]
402             : 100;
403         if (callbackInterval < 10 || callbackInterval > 1000) {
404             throw new WebAPIException(
405                 WebAPIException.INVALID_VALUES_ERR,
406                 'callbackInterval is out of range'
407             );
408         }
409         break;
410     }
411
412     var listener = null;
413     switch (args.type) {
414     case HumanActivityType.PEDOMETER:
415         listener = pedometerCallback;
416         break;
417     case HumanActivityType.GPS:
418         listener = GPSCallback;
419         break;
420     case HumanActivityType.STRESS_MONITOR:
421         listener = stressMonitorListener.onListener;
422         break;
423     default:
424         listener = function(result) {
425             native_.callIfPossible(
426                 args.changedCallback,
427                 convertActivityData(args.type, result)
428             );
429         };
430     }
431
432     utils_.log(
433         'callbackInterval = ' + callbackInterval + ', sampleInterval = ' + sampleInterval
434     );
435     startListener(listenerId, listener, 'HumanActivityMonitorManagerStart', {
436         type: args.type,
437         listenerId: listenerId,
438         callbackInterval: callbackInterval,
439         sampleInterval: sampleInterval
440     });
441
442     if (HumanActivityType.PEDOMETER === args.type) {
443         pedometerListener = args.changedCallback;
444     }
445
446     if (
447         HumanActivityType.GPS === args.type ||
448         HumanActivityType.STRESS_MONITOR === args.type
449     ) {
450         var callback = function(result) {
451             if (native_.isFailure(result)) {
452                 native_.callIfPossible(
453                     args.errorCallback,
454                     native_.getErrorObject(result)
455                 );
456             } else {
457                 native_.callIfPossible(
458                     args.changedCallback,
459                     convertActivityData(args.type, result)
460                 );
461             }
462         };
463
464         if (HumanActivityType.GPS === args.type) {
465             GPSListener = callback;
466         } else if (HumanActivityType.STRESS_MONITOR === args.type) {
467             stressListener = callback;
468         }
469     }
470 };
471
472 HumanActivityMonitorManager.prototype.stop = function(type) {
473     var args = validator_.validateArgs(arguments, [
474         { name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType) }
475     ]);
476
477     if (HumanActivityType.WRIST_UP === args.type) {
478         utils_.warn(
479             'DEPRECATION WARNING: HumanActivityType.WRIST_UP is deprecated since ' +
480                 'Tizen 4.0. Use GestureType and addGestureRecognitionListener to ' +
481                 'monitor WRIST_UP gesture'
482         );
483     }
484
485     if (HumanActivityType.PEDOMETER === args.type) {
486         stopListener(
487             'HumanActivityMonitor_PEDOMETER',
488             'HumanActivityMonitorManagerStop',
489             { type: HumanActivityType.PEDOMETER },
490             pedometerListener && !accumulativePedometerListener
491         );
492         pedometerListener = null;
493     } else {
494         stopListener(
495             'HumanActivityMonitor_' + args.type,
496             'HumanActivityMonitorManagerStop',
497             { type: args.type },
498             true
499         );
500     }
501
502     if (HumanActivityType.GPS === args.type) {
503         GPSListener = null;
504     }
505
506     if (HumanActivityType.STRESS_MONITOR === args.type) {
507         stressListener = null;
508     }
509 };
510
511 HumanActivityMonitorManager.prototype.setAccumulativePedometerListener = function() {
512     var args = validator_.validateArgs(arguments, [
513         { name: 'changeCallback', type: types_.FUNCTION }
514     ]);
515
516     var oldPedometerListener = pedometerListener;
517
518     // calling start() will overwrite pedometerListener, needs to be restored afterwards
519     this.start(HumanActivityType.PEDOMETER, args.changeCallback);
520
521     accumulativePedometerListener = args.changeCallback;
522     pedometerListener = oldPedometerListener;
523 };
524
525 HumanActivityMonitorManager.prototype.unsetAccumulativePedometerListener = function() {
526     stopListener(
527         'HumanActivityMonitor_PEDOMETER',
528         'HumanActivityMonitorManagerStop',
529         { type: HumanActivityType.PEDOMETER },
530         accumulativePedometerListener && !pedometerListener
531     );
532     accumulativePedometerListener = null;
533 };
534
535 HumanActivityMonitorManager.prototype.addActivityRecognitionListener = function() {
536     var args = validator_.validateArgs(arguments, [
537         { name: 'type', type: types_.ENUM, values: Object.keys(ActivityRecognitionType) },
538         { name: 'listener', type: types_.FUNCTION },
539         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
540     ]);
541
542     var result = native_.call(
543         'HumanActivityMonitorManagerAddActivityRecognitionListener',
544         { type: args.type, listenerId: activityRecognitionListener.listenerName }
545     );
546     if (native_.isFailure(result)) {
547         throw native_.getErrorObject(result);
548     }
549
550     var watchId = result.watchId;
551     activityRecognitionListener.addListener(watchId, args.listener, args.errorCallback);
552
553     return watchId;
554 };
555
556 HumanActivityMonitorManager.prototype.removeActivityRecognitionListener = function() {
557     var args = validator_.validateArgs(arguments, [
558         { name: 'watchId', type: types_.LONG },
559         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
560     ]);
561
562     var result = native_.call(
563         'HumanActivityMonitorManagerRemoveActivityRecognitionListener',
564         { watchId: args.watchId }
565     );
566     if (native_.isFailure(result)) {
567         setTimeout(function() {
568             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
569         }, 0);
570         return;
571     }
572     activityRecognitionListener.removeListener(args.watchId);
573 };
574
575 HumanActivityMonitorManager.prototype.startRecorder = function() {
576     var args = validator_.validateArgs(arguments, [
577         {
578             name: 'type',
579             type: types_.ENUM,
580             values: Object.keys(HumanActivityRecorderType)
581         },
582         { name: 'options', type: types_.DICTIONARY, optional: true, nullable: false }
583     ]);
584
585     var callArgs = {};
586
587     if (args.options) {
588         if (
589             MIN_OPTION_INTERVAL > args.options.interval ||
590             MIN_OPTION_RETENTION_PERIOD > args.options.interval
591         ) {
592             throw new WebAPIException(
593                 WebAPIException.INVALID_VALUES_ERR,
594                 'Invalid option value'
595             );
596         }
597
598         callArgs.options = args.options;
599     }
600
601     callArgs.type = args.type;
602
603     var result = native_.callSync('HumanActivityMonitorManagerStartRecorder', callArgs);
604
605     if (native_.isFailure(result)) {
606         throw native_.getErrorObject(result);
607     }
608 };
609
610 HumanActivityMonitorManager.prototype.stopRecorder = function() {
611     var args = validator_.validateArgs(arguments, [
612         {
613             name: 'type',
614             type: types_.ENUM,
615             values: Object.keys(HumanActivityRecorderType)
616         }
617     ]);
618
619     var callArgs = {};
620     callArgs.type = args.type;
621
622     var result = native_.callSync('HumanActivityMonitorManagerStopRecorder', callArgs);
623
624     if (native_.isFailure(result)) {
625         throw native_.getErrorObject(result);
626     }
627 };
628
629 HumanActivityMonitorManager.prototype.readRecorderData = function() {
630     var args = validator_.validateArgs(arguments, [
631         {
632             name: 'type',
633             type: types_.ENUM,
634             values: Object.keys(HumanActivityRecorderType)
635         },
636         { name: 'query', type: types_.DICTIONARY, optional: false, nullable: true },
637         { name: 'successCallback', type: types_.FUNCTION },
638         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
639     ]);
640
641     var callArgs = {};
642
643     if (args.query) {
644         if (
645             (args.query.startTime && MIN_QUERY_TIME > args.query.startTime) ||
646             (args.query.endTime && MIN_QUERY_TIME > args.query.endTime) ||
647             (args.query.anchorTime && MIN_QUERY_TIME > args.query.anchorTime) ||
648             (args.query.interval && MIN_QUERY_INTERVAL > args.query.interval) ||
649             (args.query.startTime &&
650                 args.query.endTime &&
651                 args.query.startTime > args.query.endTime)
652         ) {
653             throw new WebAPIException(
654                 WebAPIException.INVALID_VALUES_ERR,
655                 'Invalid query value'
656             );
657         }
658     }
659
660     callArgs.options = args.options;
661     callArgs.type = args.type;
662     callArgs.query = args.query;
663
664     var callback = function(result) {
665         if (native_.isFailure(result)) {
666             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
667         } else {
668             var array = convertActivityRecorderData(
669                 args.type,
670                 native_.getResultObject(result)
671             );
672             args.successCallback(array);
673         }
674     };
675
676     var result = native_.call(
677         'HumanActivityMonitorManagerReadRecorderData',
678         callArgs,
679         callback
680     );
681
682     if (native_.isFailure(result)) {
683         throw native_.getErrorObject(result);
684     }
685 };
686
687 HumanActivityMonitorManager.prototype.isGestureSupported = function() {
688     var args = validator_.validateMethod(arguments, [
689         {
690             name: 'type',
691             type: types_.ENUM,
692             values: Object.keys(GestureType)
693         }
694     ]);
695
696     var callArgs = {};
697     callArgs.type = args.type;
698
699     var result = native_.callSync('GestureManagerIsGestureSupported', callArgs);
700     if (native_.isFailure(result)) {
701         throw native_.getErrorObject(result);
702     }
703
704     return native_.getResultObject(result);
705 };
706
707 function GestureListenerManager(native, listenerName) {
708     this.listeners = {};
709     //below maps keep information about number of registered listeners for the specific
710     //type there are two maps as one keeps information about listeners which should be
711     //always called and one keeps information about number of the listeners which should
712     //be called only if power-saving mode is off
713     this.typeCountMapDefault = {};
714     this.typeCountMapAlwaysOn = {};
715     this.nextId = 1;
716     this.nativeSet = false;
717     this.native = native;
718     this.listenerName = listenerName;
719     for (var type in GestureType) {
720         this.typeCountMapDefault[type] = this.typeCountMapAlwaysOn[type] = 0;
721     }
722 }
723
724 GestureListenerManager.prototype.onListenerCalled = function(msg) {
725     var d = undefined;
726     var result = undefined;
727     var alwaysOn = msg.alwaysOn;
728     switch (msg.action) {
729     case 'ondetect':
730         d = new GestureData(this.native.getResultObject(msg));
731         break;
732     case 'onerror':
733         d = this.native.getErrorObject(msg);
734         break;
735     default:
736         utils_.log('Unknown mode: ' + msg.action);
737         return;
738     }
739
740     for (var watchId in this.listeners) {
741         if (this.listeners.hasOwnProperty(watchId)) {
742             var listener = this.listeners[watchId];
743             var call = alwaysOn ? listener.alwaysOn : true;
744             if (call && listener[msg.action]) {
745                 listener[msg.action](d);
746             }
747         }
748     }
749 };
750
751 GestureListenerManager.prototype.addListener = function(
752     successCb,
753     errorCb,
754     type,
755     alwaysOn
756 ) {
757     var listener = {
758         type: type,
759         alwaysOn: converter_.toBoolean(alwaysOn),
760         ondetect: successCb,
761         onerror: errorCb
762     };
763
764     var typeCountMap = alwaysOn ? this.typeCountMapAlwaysOn : this.typeCountMapDefault;
765     if (typeCountMap[type] === 0) {
766         var result = this.native.callSync(
767             'GestureManagerAddGestureRecognitionListener',
768             listener
769         );
770         if (this.native.isFailure(result)) {
771             throw this.native.getErrorObject(result);
772         }
773     }
774
775     typeCountMap[type]++;
776     var id = this.nextId++;
777     this.listeners[id] = listener;
778
779     if (!this.nativeSet) {
780         this.native.addListener(this.listenerName, this.onListenerCalled.bind(this));
781         this.nativeSet = true;
782     }
783
784     return id;
785 };
786
787 GestureListenerManager.prototype.removeListener = function(watchId) {
788     if (this.listeners.hasOwnProperty(watchId)) {
789         var listener = this.listeners[watchId];
790         var typeCountMap = listener.alwaysOn
791             ? this.typeCountMapAlwaysOn
792             : this.typeCountMapDefault;
793
794         if (typeCountMap[listener.type] === 1) {
795             var result = this.native.callSync(
796                 'GestureManagerRemoveGestureRecognitionListener',
797                 listener
798             );
799             if (this.native.isFailure(result)) {
800                 throw this.native.getErrorObject(result);
801             }
802         }
803
804         delete this.listeners[watchId];
805         typeCountMap[listener.type]--;
806     }
807
808     if (this.nativeSet && type_.isEmptyObject(this.listeners)) {
809         this.native.removeListener(this.listenerName);
810         this.nativeSet = false;
811     }
812 };
813
814 var GESTURE_RECOGNITION_LISTENER = 'GestureRecognitionListener';
815 var gestureRecognitionListener = new GestureListenerManager(
816     native_,
817     GESTURE_RECOGNITION_LISTENER
818 );
819
820 HumanActivityMonitorManager.prototype.addGestureRecognitionListener = function() {
821     var args = validator_.validateMethod(arguments, [
822         {
823             name: 'type',
824             type: types_.ENUM,
825             values: Object.keys(GestureType)
826         },
827         {
828             name: 'eventCallback',
829             type: types_.FUNCTION
830         },
831         {
832             name: 'errorCallback',
833             type: types_.FUNCTION,
834             optional: true,
835             nullable: true
836         },
837         {
838             name: 'alwaysOn',
839             type: types_.BOOLEAN,
840             optional: true,
841             nullable: true
842         }
843     ]);
844
845     return gestureRecognitionListener.addListener(
846         args.eventCallback,
847         args.errorCallback,
848         args.type,
849         args.alwaysOn
850     );
851 };
852
853 HumanActivityMonitorManager.prototype.removeGestureRecognitionListener = function() {
854     var args = validator_.validateMethod(arguments, [
855         {
856             name: 'watchId',
857             type: types_.LONG
858         }
859     ]);
860
861     gestureRecognitionListener.removeListener(args.watchId);
862 };
863
864 function StressMonitorListenerManager() {
865     this.listeners = {};
866     this.nextId = 1;
867 }
868
869 StressMonitorListenerManager.prototype.onListener = function(data) {
870     if (stressListener) {
871         stressListener(data);
872     }
873     var score = data.stressScore;
874     for (var watchId in stressMonitorListener.listeners) {
875         if (stressMonitorListener.listeners.hasOwnProperty(watchId)) {
876             var _listener = stressMonitorListener.listeners[watchId];
877             var rangeArray = _listener.ranges;
878             for (var id in rangeArray) {
879                 var _min = rangeArray[id].min;
880                 var _max = !type_.isUndefined(rangeArray[id].max)
881                     ? rangeArray[id].max
882                     : Number.MAX_VALUE;
883                 if (
884                     score >= _min &&
885                     score < _max &&
886                     (_listener.lastStressScore < _min ||
887                         _listener.lastStressScore >= _max)
888                 ) {
889                     _listener.listener(rangeArray[id].label);
890                 }
891             }
892             _listener.lastStressScore = score;
893         }
894     }
895 };
896
897 StressMonitorListenerManager.prototype.addListener = function(
898     ranges,
899     listener,
900     errorCallback
901 ) {
902     var id = this.nextId++;
903
904     this.listeners[id] = {
905         ranges: ranges,
906         listener: listener,
907         lastStressScore: -1
908     };
909
910     return id;
911 };
912
913 StressMonitorListenerManager.prototype.removeListener = function(watchId) {
914     if (this.listeners.hasOwnProperty(watchId)) {
915         delete this.listeners[watchId];
916     }
917 };
918
919 var stressMonitorListener = new StressMonitorListenerManager();
920
921 HumanActivityMonitorManager.prototype.addStressMonitorChangeListener = function() {
922     utils_.checkPrivilegeAccess(privilege_.HEALTHINFO);
923     var args = validator_.validateMethod(arguments, [
924         {
925             name: 'ranges',
926             type: types_.ARRAY,
927             values: StressMonitorDataRange
928         },
929         {
930             name: 'listener',
931             type: types_.FUNCTION
932         }
933     ]);
934
935     return stressMonitorListener.addListener(args.ranges, args.listener);
936 };
937
938 HumanActivityMonitorManager.prototype.removeStressMonitorChangeListener = function() {
939     var args = validator_.validateMethod(arguments, [
940         {
941             name: 'watchId',
942             type: types_.LONG
943         }
944     ]);
945
946     stressMonitorListener.removeListener(args.watchId);
947 };
948
949 function StepDifference(data) {
950     SetReadOnlyProperty(this, 'stepCountDifference', data.stepCountDifference);
951     SetReadOnlyProperty(this, 'timestamp', data.timestamp);
952 }
953
954 function HumanActivityData() {}
955
956 function HumanActivityPedometerData(data) {
957     SetReadOnlyProperty(this, 'stepStatus', data.stepStatus);
958     SetReadOnlyProperty(this, 'speed', data.speed);
959     SetReadOnlyProperty(this, 'walkingFrequency', data.walkingFrequency);
960     SetReadOnlyProperty(this, 'cumulativeDistance', data.cumulativeDistance);
961     SetReadOnlyProperty(this, 'cumulativeCalorie', data.cumulativeCalorie);
962     SetReadOnlyProperty(this, 'cumulativeTotalStepCount', data.cumulativeTotalStepCount);
963     SetReadOnlyProperty(this, 'cumulativeWalkStepCount', data.cumulativeWalkStepCount);
964     SetReadOnlyProperty(this, 'cumulativeRunStepCount', data.cumulativeRunStepCount);
965
966     var steps = [];
967     for (var i = 0; i < data.stepCountDifferences.length; ++i) {
968         steps.push(new StepDifference(data.stepCountDifferences[i]));
969     }
970     SetReadOnlyProperty(this, 'stepCountDifferences', steps);
971 }
972
973 HumanActivityPedometerData.prototype = new HumanActivityData();
974 HumanActivityPedometerData.prototype.constructor = HumanActivityPedometerData;
975
976 function HumanActivityAccumulativePedometerData(data) {
977     SetReadOnlyProperty(this, 'stepStatus', data.stepStatus);
978     SetReadOnlyProperty(this, 'speed', data.speed);
979     SetReadOnlyProperty(this, 'walkingFrequency', data.walkingFrequency);
980     SetReadOnlyProperty(this, 'accumulativeDistance', data.accumulativeDistance);
981     SetReadOnlyProperty(this, 'accumulativeCalorie', data.accumulativeCalorie);
982     SetReadOnlyProperty(
983         this,
984         'accumulativeTotalStepCount',
985         data.accumulativeTotalStepCount
986     );
987     SetReadOnlyProperty(
988         this,
989         'accumulativeWalkStepCount',
990         data.accumulativeWalkStepCount
991     );
992     SetReadOnlyProperty(this, 'accumulativeRunStepCount', data.accumulativeRunStepCount);
993
994     var steps = [];
995     for (var i = 0; i < data.stepCountDifferences.length; ++i) {
996         steps.push(new StepDifference(data.stepCountDifferences[i]));
997     }
998     SetReadOnlyProperty(this, 'stepCountDifferences', steps);
999 }
1000
1001 HumanActivityAccumulativePedometerData.prototype = new HumanActivityData();
1002 // prettier-ignore
1003 HumanActivityAccumulativePedometerData.prototype.constructor =
1004 HumanActivityAccumulativePedometerData;
1005
1006 function HumanActivityHRMData(data) {
1007     SetReadOnlyProperty(this, 'heartRate', data.heartRate);
1008     SetReadOnlyProperty(this, 'rRInterval', data.rRInterval);
1009 }
1010
1011 HumanActivityHRMData.prototype = new HumanActivityData();
1012 HumanActivityHRMData.prototype.constructor = HumanActivityHRMData;
1013
1014 function HumanActivityRecognitionData(data) {
1015     SetReadOnlyProperty(this, 'type', data.type);
1016     SetReadOnlyProperty(this, 'timestamp', data.timestamp);
1017     SetReadOnlyProperty(this, 'accuracy', data.accuracy);
1018 }
1019
1020 HumanActivityRecognitionData.prototype = new HumanActivityData();
1021 HumanActivityRecognitionData.prototype.constructor = HumanActivityRecognitionData;
1022
1023 function HumanActivityGPSInfo(data) {
1024     SetReadOnlyProperty(this, 'latitude', data.latitude);
1025     SetReadOnlyProperty(this, 'longitude', data.longitude);
1026     SetReadOnlyProperty(this, 'altitude', data.altitude);
1027     SetReadOnlyProperty(this, 'speed', data.speed);
1028     SetReadOnlyProperty(this, 'errorRange', data.errorRange);
1029     SetReadOnlyProperty(this, 'timestamp', data.timestamp);
1030 }
1031
1032 function HumanActivityGPSInfoArray(data) {
1033     SetReadOnlyProperty(this, 'gpsInfo', data);
1034 }
1035
1036 HumanActivityGPSInfoArray.prototype = new HumanActivityData();
1037 HumanActivityGPSInfoArray.prototype.constructor = HumanActivityGPSInfoArray;
1038
1039 function HumanActivitySleepMonitorData(data) {
1040     SetReadOnlyProperty(this, 'status', data.status);
1041     SetReadOnlyProperty(this, 'timestamp', data.timestamp);
1042 }
1043
1044 HumanActivitySleepMonitorData.prototype = new HumanActivityData();
1045 HumanActivitySleepMonitorData.prototype.constructor = HumanActivitySleepMonitorData;
1046
1047 function HumanActivitySleepDetectorData(data) {
1048     SetReadOnlyProperty(this, 'status', data.status);
1049 }
1050
1051 HumanActivitySleepDetectorData.prototype = new HumanActivityData();
1052 HumanActivitySleepDetectorData.prototype.constructor = HumanActivitySleepMonitorData;
1053
1054 function HumanActivityStressMonitorData(data) {
1055     SetReadOnlyProperty(this, 'stressScore', data.stressScore);
1056 }
1057
1058 HumanActivityStressMonitorData.prototype = new HumanActivityData();
1059 HumanActivityStressMonitorData.prototype.constructor = HumanActivityStressMonitorData;
1060
1061 //Recorded data
1062 function HumanActivityRecorderData(data) {
1063     if (data) {
1064         SetReadOnlyProperty(this, 'startTime', data.startTime);
1065         SetReadOnlyProperty(this, 'endTime', data.endTime);
1066     }
1067 }
1068
1069 function HumanActivityRecorderPedometerData(data) {
1070     HumanActivityRecorderData.call(this, data);
1071     SetReadOnlyProperty(this, 'distance', data.distance);
1072     SetReadOnlyProperty(this, 'calorie', data.calorie);
1073     SetReadOnlyProperty(this, 'totalStepCount', data.totalStepCount);
1074     SetReadOnlyProperty(this, 'walkStepCount', data.walkStepCount);
1075     SetReadOnlyProperty(this, 'runStepCount', data.runStepCount);
1076 }
1077
1078 HumanActivityRecorderPedometerData.prototype = new HumanActivityRecorderData();
1079 // prettier-ignore
1080 HumanActivityRecorderPedometerData.prototype.constructor =
1081 HumanActivityRecorderPedometerData;
1082
1083 function HumanActivityRecorderHRMData(data) {
1084     HumanActivityRecorderData.call(this, data);
1085     SetReadOnlyProperty(this, 'heartRate', data.heartRate);
1086 }
1087
1088 HumanActivityRecorderHRMData.prototype = new HumanActivityRecorderData();
1089 HumanActivityRecorderHRMData.prototype.constructor = HumanActivityRecorderHRMData;
1090
1091 function HumanActivityRecorderSleepMonitorData(data) {
1092     HumanActivityRecorderData.call(this, data);
1093     SetReadOnlyProperty(this, 'status', data.status);
1094 }
1095
1096 HumanActivityRecorderSleepMonitorData.prototype = new HumanActivityRecorderData();
1097 // prettier-ignore
1098 HumanActivityRecorderSleepMonitorData.prototype.constructor =
1099 HumanActivityRecorderSleepMonitorData;
1100
1101 function HumanActivityRecorderPressureData(data) {
1102     HumanActivityRecorderData.call(this, data);
1103     SetReadOnlyProperty(this, 'max', data.max);
1104     SetReadOnlyProperty(this, 'min', data.min);
1105     SetReadOnlyProperty(this, 'average', data.average);
1106 }
1107
1108 function GestureData(data) {
1109     if (data) {
1110         SetReadOnlyProperty(this, 'type', data.type);
1111         SetReadOnlyProperty(this, 'event', data.event);
1112         SetReadOnlyProperty(this, 'timestamp', data.timestamp);
1113
1114         if (data.type === 'GESTURE_TILT') {
1115             SetReadOnlyProperty(this, 'x', data.x);
1116             SetReadOnlyProperty(this, 'y', data.y);
1117         } else {
1118             SetReadOnlyProperty(this, 'x', null);
1119             SetReadOnlyProperty(this, 'y', null);
1120         }
1121     }
1122 }
1123
1124 HumanActivityRecorderPressureData.prototype = new HumanActivityRecorderData();
1125 // prettier-ignore
1126 HumanActivityRecorderPressureData.prototype.constructor =
1127 HumanActivityRecorderPressureData;
1128
1129 tizen.StressMonitorDataRange = StressMonitorDataRange;
1130
1131 exports = new HumanActivityMonitorManager();