[Mediacontroller] Fixed issue with invalid name of enum
[platform/core/api/webapi-plugins.git] / src / mediacontroller / mediacontroller_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 _global = window || global || {};
18
19 var utils_ = xwalk.utils;
20 var type_ = utils_.type;
21 var converter_ = utils_.converter;
22 var validator_ = utils_.validator;
23 var types_ = validator_.Types;
24 var native_ = new xwalk.utils.NativeManager(extension);
25
26 function ListenerManager(native, listenerName, handle) {
27     this.listeners = {};
28     this.listenerNameToIds = {};
29     this.listenerIdToName = {};
30     this.nextId = 1;
31     this.nativeSet = false;
32     this.native = native;
33     this.listenerName = listenerName;
34     this.handle = handle || function(msg, listener, watchId) {};
35     this.requestIdToListenerId = {};
36 }
37
38 ListenerManager.prototype.addListener = function(callback) {
39     var id = this.nextId;
40     if (!this.nativeSet) {
41         this.native.addListener(
42             this.listenerName,
43             function(msg) {
44                 for (var watchId in this.listeners) {
45                     if (this.listeners.hasOwnProperty(watchId)) {
46                         var stop = this.handle(msg, this.listeners[watchId], watchId);
47                         if (stop) {
48                             break;
49                         }
50                     }
51                 }
52             }.bind(this)
53         );
54
55         this.nativeSet = true;
56     }
57
58     this.listeners[id] = callback;
59     ++this.nextId;
60     return id;
61 };
62
63 ListenerManager.prototype.addServerInfoListener = function(callback, name) {
64     var id = this.nextId;
65     if (!this.nativeSet) {
66         this.native.addListener(
67             this.listenerName,
68             function(msg) {
69                 if (this.listenerNameToIds.hasOwnProperty(msg.name)) {
70                     var cbArray = this.listenerNameToIds[msg.name];
71                     for (var i = 0; i < cbArray.length; ++i) {
72                         var watchId = cbArray[i];
73                         this.handle(msg, this.listeners[watchId], watchId);
74                     }
75                 }
76             }.bind(this)
77         );
78
79         this.nativeSet = true;
80     }
81
82     this.listenerIdToName[id] = name;
83     if (this.listenerNameToIds[name]) {
84         this.listenerNameToIds[name].push(id);
85     } else {
86         this.listenerNameToIds[name] = [id];
87     }
88     this.listeners[id] = callback;
89     ++this.nextId;
90     return id;
91 };
92
93 ListenerManager.prototype.removeListener = function(watchId) {
94     if (this.listeners.hasOwnProperty(watchId)) {
95         delete this.listeners[watchId];
96     }
97 };
98
99 function removeArrayElement(arr, elem) {
100     var index = arr.indexOf(elem);
101     if (index !== -1) {
102         arr.splice(index, 1);
103     }
104 }
105
106 ListenerManager.prototype.removeServerInfoListener = function(watchId) {
107     this.removeListener(watchId);
108     if (this.listenerIdToName.hasOwnProperty(watchId)) {
109         var name = this.listenerIdToName[watchId];
110         removeArrayElement(this.listenerNameToIds[name], watchId);
111         delete this.listenerIdToName[watchId];
112     }
113 };
114
115 var ServerCommandListener = new ListenerManager(
116     native_,
117     '_ServerCommandListener',
118     function(msg, listener) {
119         // passing raw msg.data object instead of tizen.Bundle
120         // due to backward compatibility issue.
121         var reply = listener(msg.clientName, msg.command, msg.data);
122
123         if (!(reply instanceof RequestReply)) {
124             reply = new RequestReply(
125                 xwalk.utils.type.isNullOrUndefined(reply) ? null : reply,
126                 0
127             );
128         }
129
130         var nativeData = {
131             clientName: msg.clientName,
132             requestId: msg.requestId,
133             reply: reply
134         };
135
136         var result = native_.callSync('MediaControllerServer_replyCommand', nativeData);
137         if (native_.isFailure(result)) {
138             throw native_.getErrorObject(result);
139         }
140     }
141 );
142
143 var ReplyCommandListener = new ListenerManager(native_, '_ReplyCommandListener', function(
144     msg,
145     listener,
146     watchId
147 ) {
148     if (this.requestIdToListenerId[watchId] === msg.requestId) {
149         // this listener is a wrapper around user callback which is neccessary
150         // due to difference between sendCommand() and new API methods.
151         listener(msg);
152
153         this.removeListener(watchId);
154         delete this.requestIdToListenerId[watchId];
155
156         return true;
157     }
158     return false;
159 });
160
161 var ServerPlaybackInfoListener = new ListenerManager(
162     native_,
163     '_ServerPlaybackInfoListener',
164     function(msg, listener) {
165         if (msg.action === 'onplaybackstaterequest') {
166             native_.callIfPossible(listener[msg.action], msg.state, msg.clientName);
167         }
168         if (msg.action === 'onplaybackpositionrequest') {
169             native_.callIfPossible(listener[msg.action], msg.position, msg.clientName);
170         }
171         if (msg.action === 'onshufflemoderequest') {
172             native_.callIfPossible(listener[msg.action], msg.mode, msg.clientName);
173         }
174         if (msg.action === 'onrepeatmoderequest') {
175             utils_.printDeprecationWarningFor(
176                 'onrepeatmoderequest',
177                 'onrepeatstaterequest'
178             );
179             native_.callIfPossible(listener[msg.action], msg.mode, msg.clientName);
180         }
181         if (msg.action === 'onrepeatstaterequest') {
182             native_.callIfPossible(listener[msg.action], msg.state, msg.clientName);
183         }
184         if (msg.action === 'onplaybackitemrequest') {
185             native_.callIfPossible(
186                 listener[msg.action],
187                 msg.playlistName,
188                 msg.index,
189                 msg.state,
190                 msg.position,
191                 msg.clientName
192             );
193         }
194     }
195 );
196
197 var ServerInfoStatusListener = new ListenerManager(
198     native_,
199     '_ServerInfoStatusListener',
200     function(msg, listener) {
201         listener(msg.state);
202     }
203 );
204
205 var ServerInfoPlaybackInfoListener = new ListenerManager(
206     native_,
207     '_ServerInfoPlaybackInfoListener',
208     function(msg, listener) {
209         if (msg.action === 'onplaybackchanged') {
210             listener[msg.action](msg.state, msg.position);
211         }
212         if (msg.action === 'onshufflemodechanged') {
213             listener[msg.action](msg.mode);
214         }
215         if (msg.action === 'onrepeatmodechanged') {
216             utils_.printDeprecationWarningFor(
217                 'onrepeatmodechanged',
218                 'onrepeatstatechanged'
219             );
220             listener[msg.action](msg.mode);
221         }
222         if (msg.action === 'onrepeatstatechanged') {
223             listener[msg.action](msg.state);
224         }
225         if (msg.action === 'onmetadatachanged') {
226             listener[msg.action](new MediaControllerMetadata(msg.metadata));
227         }
228     }
229 );
230
231 var ServerInfoPlaylistUpdatedListener = new ListenerManager(
232     native_,
233     '_ServerInfoPlaylistUpdatedListener',
234     function(msg, listener) {
235         if (msg.action === 'onplaylistupdated') {
236             listener[msg.action](msg.serverName, new MediaControllerPlaylist(msg));
237         }
238         if (msg.action === 'onplaylistdeleted') {
239             listener[msg.action](msg.serverName, msg.name);
240         }
241     }
242 );
243
244 var EditManager = function() {
245     this.isAllowed = false;
246 };
247
248 EditManager.prototype.allow = function() {
249     this.isAllowed = true;
250 };
251
252 EditManager.prototype.disallow = function() {
253     this.isAllowed = false;
254 };
255
256 var edit_ = new EditManager();
257
258 var MediaControllerServerState = {
259     ACTIVE: 'ACTIVE',
260     INACTIVE: 'INACTIVE'
261 };
262
263 var MediaControllerPlaybackState = {
264     PLAY: 'PLAY',
265     PAUSE: 'PAUSE',
266     STOP: 'STOP',
267     NEXT: 'NEXT',
268     PREV: 'PREV',
269     FORWARD: 'FORWARD',
270     REWIND: 'REWIND'
271 };
272
273 var MediaControllerRepeatState = {
274     REPEAT_OFF: 'REPEAT_OFF',
275     REPEAT_ONE: 'REPEAT_ONE',
276     REPEAT_ALL: 'REPEAT_ALL'
277 };
278
279 var MediaControllerContentAgeRating = {
280     ALL: 'ALL',
281     ONE: '1',
282     TWO: '2',
283     THREE: '3',
284     FOUR: '4',
285     FIVE: '5',
286     SIX: '6',
287     SEVEN: '7',
288     EIGHT: '8',
289     NINE: '9',
290     TEN: '10',
291     ELEVEN: '11',
292     TWELVE: '12',
293     THIRTEEN: '13',
294     FOURTEEN: '14',
295     FIFTEEN: '15',
296     SIXTEEN: '16',
297     SEVENTEEN: '17',
298     EIGHTEEN: '18',
299     NINETEEN: '19'
300 };
301
302 var MediaControllerSearchCategory = {
303     NO_CATEGORY: 'NO_CATEGORY',
304     TITLE: 'TITLE',
305     ARTIST: 'ARTIST',
306     ALBUM: 'ALBUM',
307     GENRE: 'GENRE',
308     TPO: 'TPO'
309 };
310
311 var MediaControllerContentType = {
312     IMAGE: 'IMAGE',
313     MUSIC: 'MUSIC',
314     VIDEO: 'VIDEO',
315     OTHER: 'OTHER',
316     UNDECIDED: 'UNDECIDED'
317 };
318
319 function MediaControllerManager() {}
320
321 MediaControllerManager.prototype.getClient = function() {
322     var result = native_.callSync('MediaControllerManager_getClient', {});
323
324     if (native_.isFailure(result)) {
325         throw native_.getErrorObject(result);
326     }
327
328     return new MediaControllerClient(native_.getResultObject(result));
329 };
330
331 MediaControllerManager.prototype.createServer = function() {
332     var result = native_.callSync('MediaControllerManager_createServer', {});
333     if (native_.isFailure(result)) {
334         throw native_.getErrorObject(result);
335     }
336
337     return new MediaControllerServer(native_.getResultObject(result));
338 };
339
340 var MediaControllerMetadata = function(data) {
341     var _title = '';
342     var _artist = '';
343     var _album = '';
344     var _author = '';
345     var _genre = '';
346     var _duration = '';
347     var _date = '';
348     var _copyright = '';
349     var _description = '';
350     var _trackNum = '';
351     var _picture = '';
352     Object.defineProperties(this, {
353         title: {
354             get: function() {
355                 return _title;
356             },
357             set: function(v) {
358                 _title = converter_.toString(v);
359             },
360             enumerable: true
361         },
362         artist: {
363             get: function() {
364                 return _artist;
365             },
366             set: function(v) {
367                 _artist = converter_.toString(v);
368             },
369             enumerable: true
370         },
371         album: {
372             get: function() {
373                 return _album;
374             },
375             set: function(v) {
376                 _album = converter_.toString(v);
377             },
378             enumerable: true
379         },
380         author: {
381             get: function() {
382                 return _author;
383             },
384             set: function(v) {
385                 _author = converter_.toString(v);
386             },
387             enumerable: true
388         },
389         genre: {
390             get: function() {
391                 return _genre;
392             },
393             set: function(v) {
394                 _genre = converter_.toString(v);
395             },
396             enumerable: true
397         },
398         duration: {
399             get: function() {
400                 return _duration;
401             },
402             set: function(v) {
403                 _duration = converter_.toString(v);
404             },
405             enumerable: true
406         },
407         date: {
408             get: function() {
409                 return _date;
410             },
411             set: function(v) {
412                 _date = converter_.toString(v);
413             },
414             enumerable: true
415         },
416         copyright: {
417             get: function() {
418                 return _copyright;
419             },
420             set: function(v) {
421                 _copyright = converter_.toString(v);
422             },
423             enumerable: true
424         },
425         description: {
426             get: function() {
427                 return _description;
428             },
429             set: function(v) {
430                 _description = converter_.toString(v);
431             },
432             enumerable: true
433         },
434         trackNum: {
435             get: function() {
436                 return _trackNum;
437             },
438             set: function(v) {
439                 _trackNum = converter_.toString(v);
440             },
441             enumerable: true
442         },
443         picture: {
444             get: function() {
445                 return _picture;
446             },
447             set: function(v) {
448                 _picture = converter_.toString(v);
449             },
450             enumerable: true
451         }
452     });
453
454     if (data instanceof _global.Object) {
455         for (var prop in data) {
456             if (data.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
457                 this[prop] = data[prop];
458             }
459         }
460     }
461 };
462
463 var MediaControllerPlaybackInfo = function(data) {
464     var _state = 'STOP';
465     var _position = 0;
466     var _shuffleMode = false;
467     var _repeatMode = false;
468     var _contentType = MediaControllerContentType.UNDECIDED;
469     var _repeatState = MediaControllerRepeatState.REPEAT_OFF;
470     var _ageRating = MediaControllerContentAgeRating.ALL;
471     var _metadata = new MediaControllerMetadata();
472     var _index = null;
473     var _playlistName = null;
474     Object.defineProperties(this, {
475         state: {
476             get: function() {
477                 return _state;
478             },
479             set: function(v) {
480                 _state = edit_.isAllowed && v ? v : _state;
481             },
482             enumerable: true
483         },
484         position: {
485             get: function() {
486                 return _position;
487             },
488             set: function(v) {
489                 _position = edit_.isAllowed ? converter_.toLong(v) : _position;
490             },
491             enumerable: true
492         },
493         ageRating: {
494             get: function() {
495                 return _ageRating;
496             },
497             set: function(v) {
498                 _ageRating = edit_.isAllowed && v ? v : _ageRating;
499             },
500             enumerable: true
501         },
502         shuffleMode: {
503             get: function() {
504                 return _shuffleMode;
505             },
506             set: function(v) {
507                 _shuffleMode = edit_.isAllowed ? converter_.toBoolean(v) : _shuffleMode;
508             },
509             enumerable: true
510         },
511         repeatMode: {
512             get: function() {
513                 utils_.printDeprecationWarningFor('repeatMode', 'repeatState');
514                 return _repeatMode;
515             },
516             set: function(v) {
517                 _repeatMode = edit_.isAllowed ? converter_.toBoolean(v) : _repeatMode;
518             },
519             enumerable: true
520         },
521         repeatState: {
522             get: function() {
523                 return _repeatState;
524             },
525             set: function(v) {
526                 _repeatState = edit_.isAllowed && v ? v : _repeatState;
527             },
528             enumerable: true
529         },
530         contentType: {
531             get: function() {
532                 return _contentType;
533             },
534             set: function(v) {
535                 _contentType = edit_.isAllowed && v ? v : _contentType;
536             },
537             enumerable: true
538         },
539         metadata: {
540             get: function() {
541                 return _metadata;
542             },
543             set: function(v) {
544                 _metadata =
545                     edit_.isAllowed && v ? new MediaControllerMetadata(v) : _metadata;
546             },
547             enumerable: true
548         },
549         index: {
550             get: function() {
551                 return _index;
552             },
553             set: function(v) {
554                 _index = edit_.isAllowed && v ? v : _index;
555             },
556             enumerable: true
557         },
558         playlistName: {
559             get: function() {
560                 return _playlistName;
561             },
562             set: function(v) {
563                 _playlistName = edit_.isAllowed && v ? v : _playlistName;
564             },
565             enumerable: true
566         }
567     });
568
569     if (data instanceof _global.Object) {
570         for (var prop in data) {
571             if (data.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
572                 this[prop] = data[prop];
573             }
574         }
575     }
576 };
577
578 function MediaControllerServer(data) {
579     var _iconURI = null;
580
581     Object.defineProperties(this, {
582         playbackInfo: {
583             value: new MediaControllerPlaybackInfo(data),
584             writable: false,
585             enumerable: true
586         },
587         iconURI: {
588             get: function() {
589                 return _iconURI;
590             },
591             set: function(v) {
592                 _iconURI = edit_.isAllowed ? (v ? v : null) : _iconURI;
593             },
594             enumerable: true
595         }
596     });
597 }
598
599 MediaControllerServer.prototype.updatePlaybackState = function(state) {
600     var args = validator_.validateArgs(arguments, [
601         {
602             name: 'state',
603             type: types_.ENUM,
604             values: Object.keys(MediaControllerPlaybackState)
605         }
606     ]);
607
608     var data = {
609         state: args.state
610     };
611
612     var result = native_.callSync('MediaControllerServer_updatePlaybackState', data);
613
614     if (native_.isFailure(result)) {
615         throw native_.getErrorObject(result);
616     }
617
618     edit_.allow();
619     this.playbackInfo.state = args.state;
620     edit_.disallow();
621 };
622
623 MediaControllerServer.prototype.updateIconURI = function(iconURI) {
624     var args = validator_.validateArgs(arguments, [
625         { name: 'iconURI', type: types_.STRING, nullable: true }
626     ]);
627
628     var data = {
629         iconURI: args.iconURI
630     };
631
632     var result = native_.callSync('MediaControllerServer_updateIconURI', data);
633     if (native_.isFailure(result)) {
634         throw new WebAPIException(
635             WebAPIException.UNKNOWN_ERR,
636             native_.getErrorObject(result).message
637         );
638     }
639
640     edit_.allow();
641     this.iconURI = args.iconURI;
642     edit_.disallow();
643 };
644
645 MediaControllerServer.prototype.updatePlaybackPosition = function(position) {
646     var args = validator_.validateArgs(arguments, [
647         { name: 'position', type: types_.UNSIGNED_LONG_LONG }
648     ]);
649
650     var data = {
651         position: args.position
652     };
653
654     var result = native_.callSync('MediaControllerServer_updatePlaybackPosition', data);
655
656     if (native_.isFailure(result)) {
657         throw native_.getErrorObject(result);
658     }
659
660     edit_.allow();
661     this.playbackInfo.position = args.position;
662     edit_.disallow();
663 };
664
665 MediaControllerServer.prototype.updatePlaybackAgeRating = function(rating) {
666     var args = validator_.validateArgs(arguments, [
667         {
668             name: 'rating',
669             type: types_.ENUM,
670             values: Object.values(MediaControllerContentAgeRating)
671         }
672     ]);
673
674     var data = {
675         rating: args.rating
676     };
677
678     var result = native_.callSync('MediaControllerServer_updatePlaybackAgeRating', data);
679     if (native_.isFailure(result)) {
680         throw native_.getErrorObject(result);
681     }
682
683     edit_.allow();
684     this.playbackInfo.ageRating = args.rating;
685     edit_.disallow();
686 };
687
688 MediaControllerServer.prototype.updatePlaybackContentType = function(contentType) {
689     var args = validator_.validateArgs(arguments, [
690         {
691             name: 'contentType',
692             type: types_.ENUM,
693             values: Object.values(MediaControllerContentType)
694         }
695     ]);
696
697     var data = {
698         contentType: args.contentType
699     };
700
701     var result = native_.callSync(
702         'MediaControllerServer_updatePlaybackContentType',
703         data
704     );
705     if (native_.isFailure(result)) {
706         throw native_.getErrorObject(result);
707     }
708
709     edit_.allow();
710     this.playbackInfo.contentType = args.contentType;
711     edit_.disallow();
712 };
713
714 MediaControllerServer.prototype.updateShuffleMode = function(mode) {
715     var args = validator_.validateArgs(arguments, [
716         { name: 'mode', type: types_.BOOLEAN }
717     ]);
718
719     var data = {
720         mode: args.mode
721     };
722
723     var result = native_.callSync('MediaControllerServer_updateShuffleMode', data);
724
725     if (native_.isFailure(result)) {
726         throw native_.getErrorObject(result);
727     }
728
729     edit_.allow();
730     this.playbackInfo.shuffleMode = args.mode;
731     edit_.disallow();
732 };
733
734 MediaControllerServer.prototype.updateRepeatMode = function(mode) {
735     utils_.printDeprecationWarningFor('updateRepeatMode()', 'updateRepeatState()');
736     var args = validator_.validateArgs(arguments, [
737         { name: 'mode', type: types_.BOOLEAN }
738     ]);
739
740     var data = {
741         mode: args.mode
742     };
743
744     var result = native_.callSync('MediaControllerServer_updateRepeatMode', data);
745
746     if (native_.isFailure(result)) {
747         throw native_.getErrorObject(result);
748     }
749
750     edit_.allow();
751     this.playbackInfo.repeatMode = args.mode;
752     this.playbackInfo.repeatState = args.mode
753         ? MediaControllerRepeatState.REPEAT_ALL
754         : MediaControllerRepeatState.REPEAT_OFF;
755     edit_.disallow();
756 };
757
758 MediaControllerServer.prototype.updateRepeatState = function() {
759     var args = validator_.validateArgs(arguments, [
760         {
761             name: 'state',
762             type: types_.ENUM,
763             values: Object.keys(MediaControllerRepeatState)
764         }
765     ]);
766
767     var data = {
768         state: args.state
769     };
770
771     var result = native_.callSync('MediaControllerServer_updateRepeatState', data);
772
773     if (native_.isFailure(result)) {
774         throw native_.getErrorObject(result);
775     }
776
777     edit_.allow();
778     this.playbackInfo.repeatState = args.state;
779     if (MediaControllerRepeatState.REPEAT_ONE !== args.state) {
780         this.playbackInfo.repeatMode =
781             args.state === MediaControllerRepeatState.REPEAT_ALL;
782     }
783     edit_.disallow();
784 };
785
786 MediaControllerServer.prototype.updateMetadata = function(metadata) {
787     var args = validator_.validateArgs(arguments, [
788         {
789             name: 'metadata',
790             type: types_.PLATFORM_OBJECT,
791             values: MediaControllerMetadata
792         }
793     ]);
794
795     var data = {
796         metadata: args.metadata
797     };
798
799     var result = native_.callSync('MediaControllerServer_updateMetadata', data);
800
801     if (native_.isFailure(result)) {
802         throw native_.getErrorObject(result);
803     }
804
805     edit_.allow();
806     this.playbackInfo.metadata = args.metadata;
807     edit_.disallow();
808 };
809
810 MediaControllerServer.prototype.addChangeRequestPlaybackInfoListener = function(
811     listener
812 ) {
813     var args = validator_.validateArgs(arguments, [
814         {
815             name: 'listener',
816             type: types_.LISTENER,
817             values: [
818                 'onplaybackstaterequest',
819                 'onplaybackpositionrequest',
820                 'onshufflemoderequest',
821                 'onrepeatmoderequest',
822                 'onrepeatstaterequest',
823                 'onplaybackitemrequest'
824             ]
825         }
826     ]);
827
828     if (type_.isEmptyObject(ServerPlaybackInfoListener.listeners)) {
829         var result = native_.callSync(
830             'MediaControllerServer_addChangeRequestPlaybackInfoListener',
831             {
832                 listenerId: ServerPlaybackInfoListener.listenerName
833             }
834         );
835         if (native_.isFailure(result)) {
836             throw native_.getErrorObject(result);
837         }
838     }
839
840     return ServerPlaybackInfoListener.addListener(args.listener);
841 };
842
843 MediaControllerServer.prototype.removeChangeRequestPlaybackInfoListener = function(
844     watchId
845 ) {
846     var args = validator_.validateArgs(arguments, [
847         { name: 'watchId', type: types_.LONG }
848     ]);
849
850     ServerPlaybackInfoListener.removeListener(args.watchId);
851
852     if (type_.isEmptyObject(ServerPlaybackInfoListener.listeners)) {
853         native_.callSync('MediaControllerServer_removeCommandListener');
854     }
855 };
856
857 MediaControllerServer.prototype.addCommandListener = function(listener) {
858     var args = validator_.validateArgs(arguments, [
859         { name: 'listener', type: types_.FUNCTION }
860     ]);
861
862     if (type_.isEmptyObject(ServerCommandListener.listeners)) {
863         var result = native_.callSync('MediaControllerServer_addCommandListener', {
864             listenerId: ServerCommandListener.listenerName
865         });
866         if (native_.isFailure(result)) {
867             throw native_.getErrorObject(result);
868         }
869     }
870
871     return ServerCommandListener.addListener(args.listener);
872 };
873
874 MediaControllerServer.prototype.removeCommandListener = function(watchId) {
875     var args = validator_.validateArgs(arguments, [
876         { name: 'watchId', type: types_.LONG }
877     ]);
878
879     ServerCommandListener.removeListener(args.watchId);
880
881     if (type_.isEmptyObject(ServerCommandListener.listeners)) {
882         native_.callSync('MediaControllerServer_removeCommandListener');
883     }
884 };
885
886 MediaControllerServer.prototype.createPlaylist = function(name) {
887     var args = validator_.validateArgs(arguments, [
888         { name: 'name', type: types_.STRING }
889     ]);
890
891     var data = {
892         name: args.name
893     };
894
895     var result = native_.callSync('MediaControllerServer_createPlaylist', data);
896
897     if (native_.isFailure(result)) {
898         throw native_.getErrorObject(result);
899     }
900
901     return new MediaControllerPlaylist(native_.getResultObject(result));
902 };
903
904 MediaControllerServer.prototype.savePlaylist = function(
905     playlist,
906     successCallback,
907     errorCallback
908 ) {
909     var args = validator_.validateArgs(arguments, [
910         {
911             name: 'playlist',
912             type: types_.PLATFORM_OBJECT,
913             values: MediaControllerPlaylist
914         },
915         {
916             name: 'successCallback',
917             type: types_.FUNCTION,
918             optional: true,
919             nullable: true
920         },
921         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
922     ]);
923
924     var data = {
925         name: args.playlist.name
926     };
927
928     var callback = function(result) {
929         if (native_.isFailure(result)) {
930             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
931             return;
932         }
933         native_.callIfPossible(args.successCallback);
934     };
935
936     var result = native_.call('MediaControllerServer_savePlaylist', data, callback);
937
938     if (native_.isFailure(result)) {
939         throw native_.getErrorObject(result);
940     }
941 };
942
943 MediaControllerServer.prototype.deletePlaylist = function(
944     name,
945     successCallback,
946     errorCallback
947 ) {
948     var args = validator_.validateArgs(arguments, [
949         { name: 'name', type: types_.STRING },
950         {
951             name: 'successCallback',
952             type: types_.FUNCTION,
953             optional: true,
954             nullable: true
955         },
956         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
957     ]);
958
959     var data = {
960         name: args.name
961     };
962
963     var callback = function(result) {
964         if (native_.isFailure(result)) {
965             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
966             return;
967         }
968         native_.callIfPossible(args.successCallback);
969     };
970
971     var result = native_.call('MediaControllerServer_deletePlaylist', data, callback);
972
973     if (native_.isFailure(result)) {
974         throw native_.getErrorObject(result);
975     }
976 };
977
978 MediaControllerServer.prototype.updatePlaybackItem = function(playlistName, index) {
979     var args = validator_.validateArgs(arguments, [
980         { name: 'playlistName', type: types_.STRING },
981         { name: 'index', type: types_.STRING }
982     ]);
983
984     var data = {
985         playlistName: args.playlistName,
986         index: args.index
987     };
988
989     var result = native_.callSync('MediaControllerServer_updatePlaybackItem', data);
990
991     if (native_.isFailure(result)) {
992         throw native_.getErrorObject(result);
993     }
994
995     edit_.allow();
996     this.playbackInfo.index = args.index;
997     this.playbackInfo.playlistName = args.playlistName;
998     edit_.disallow();
999 };
1000
1001 MediaControllerServer.prototype.getAllPlaylists = function(
1002     successCallback,
1003     errorCallback
1004 ) {
1005     var args = validator_.validateArgs(arguments, [
1006         {
1007             name: 'successCallback',
1008             type: types_.FUNCTION,
1009             optional: false,
1010             nullable: false
1011         },
1012         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1013     ]);
1014
1015     var callback = function(result) {
1016         if (native_.isFailure(result)) {
1017             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1018             return;
1019         }
1020         var data = native_.getResultObject(result);
1021         var playlists = [];
1022
1023         for (var i = 0; i < data.length; i++) {
1024             playlists.push(new MediaControllerPlaylist(data[i]));
1025         }
1026
1027         native_.callIfPossible(args.successCallback, playlists);
1028     };
1029     var result = native_.call('MediaControllerServer_getAllPlaylists', {}, callback);
1030
1031     if (native_.isFailure(result)) {
1032         throw native_.getErrorObject(result);
1033     }
1034 };
1035
1036 function RequestReply(data, code) {
1037     xwalk.utils.validator.isConstructorCall(this, RequestReply);
1038     this.code = xwalk.utils.converter.toLong(code);
1039     this.data = xwalk.utils.type.isNullOrUndefined(data) ? null : new tizen.Bundle(data);
1040 }
1041
1042 MediaControllerServer.prototype.setSearchRequestListener = function(listener) {
1043     var args = validator_.validateArgs(arguments, [
1044         { name: 'listener', type: types_.FUNCTION }
1045     ]);
1046
1047     if (!native_.isListenerSet('SearchRequestListener')) {
1048         var result = native_.callSync('MediaControllerServer_addSearchRequestListener', {
1049             listenerId: 'SearchRequestListener'
1050         });
1051         if (native_.isFailure(result)) {
1052             throw native_.getErrorObject(result);
1053         }
1054     }
1055
1056     var callback = function(msg) {
1057         var request = [];
1058         msg.request.forEach(function(filter) {
1059             request.push(
1060                 new SearchFilter(
1061                     filter.contentType,
1062                     filter.category,
1063                     filter.keyword,
1064                     filter.extraData
1065                 )
1066             );
1067         });
1068
1069         var reply = args.listener(msg.clientName, request);
1070         if (type_.isUndefined(reply)) {
1071             reply = new RequestReply(null, 0);
1072         }
1073
1074         var nativeData = {
1075             clientName: msg.clientName,
1076             requestId: msg.requestId,
1077             reply: reply
1078         };
1079
1080         var result = native_.callSync('MediaControllerServer_replyCommand', nativeData);
1081         if (native_.isFailure(result)) {
1082             throw native_.getErrorObject(result);
1083         }
1084     };
1085
1086     native_.addListener('SearchRequestListener', callback);
1087 };
1088
1089 MediaControllerServer.prototype.unsetSearchRequestListener = function() {
1090     var result = native_.callSync('MediaControllerServer_removeSearchRequestListener');
1091     if (native_.isFailure(result)) {
1092         throw native_.getErrorObject(result);
1093     }
1094     native_.removeListener('SearchRequestListener');
1095 };
1096
1097 function MediaControllerClient() {}
1098
1099 MediaControllerClient.prototype.findServers = function(successCallback, errorCallback) {
1100     var args = validator_.validateArgs(arguments, [
1101         { name: 'successCallback', type: types_.FUNCTION },
1102         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1103     ]);
1104
1105     var callback = function(result) {
1106         if (native_.isFailure(result)) {
1107             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1108             return;
1109         }
1110         var info = [];
1111         var data = native_.getResultObject(result);
1112         for (var i = 0; i < data.length; i++) {
1113             info.push(new MediaControllerServerInfo(data[i]));
1114         }
1115         native_.callIfPossible(args.successCallback, info);
1116     };
1117
1118     native_.call('MediaControllerClient_findServers', {}, callback);
1119 };
1120
1121 MediaControllerClient.prototype.getLatestServerInfo = function() {
1122     var result = native_.callSync('MediaControllerClient_getLatestServerInfo', {});
1123
1124     if (native_.isFailure(result)) {
1125         throw native_.getErrorObject(result);
1126     }
1127
1128     var serverInfo = native_.getResultObject(result);
1129     if (serverInfo) {
1130         if ('NONE' === serverInfo.state) {
1131             serverInfo.state = 'INACTIVE';
1132         }
1133         serverInfo = new MediaControllerServerInfo(serverInfo);
1134     }
1135     return serverInfo;
1136 };
1137
1138 function SearchFilter(contentType, category, keyword, extraData) {
1139     validator_.isConstructorCall(this, SearchFilter);
1140     var args = validator_.validateArgs(arguments, [
1141         {
1142             name: 'contentType',
1143             type: types_.ENUM,
1144             values: Object.values(MediaControllerContentType)
1145         },
1146         {
1147             name: 'category',
1148             type: types_.ENUM,
1149             values: Object.values(MediaControllerSearchCategory),
1150             optional: true
1151         },
1152         { name: 'keyword', type: types_.STRING, nullable: true, optional: true }
1153     ]);
1154
1155     if (!args.hasOwnProperty('category')) {
1156         args.category = 'NO_CATEGORY';
1157     }
1158     if (!args.hasOwnProperty('keyword')) {
1159         args.keyword = null;
1160     }
1161
1162     extraData = xwalk.utils.type.isUndefined(extraData) ? null : extraData;
1163     args.extraData = xwalk.utils.type.isNull(extraData)
1164         ? null
1165         : new tizen.Bundle(extraData);
1166
1167     var extraData_ = args.extraData;
1168     var contentType_ = args.contentType;
1169     var category_ = args.category;
1170     var keyword_ = args.keyword;
1171
1172     // keyword can be null only when category is NO_CATEGORY
1173     if (
1174         xwalk.utils.type.isNull(keyword_) &&
1175         category_ != MediaControllerSearchCategory.NO_CATEGORY
1176     ) {
1177         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR);
1178     }
1179
1180     Object.defineProperties(this, {
1181         contentType: {
1182             enumerable: true,
1183             get: function() {
1184                 return contentType_;
1185             },
1186             set: function(newContentType) {
1187                 var args = validator_.validateArgs(arguments, [
1188                     {
1189                         name: 'newContentType',
1190                         type: types_.ENUM,
1191                         values: Object.values(MediaControllerContentType)
1192                     }
1193                 ]);
1194                 contentType_ = args.newContentType;
1195             }
1196         },
1197         category: {
1198             enumerable: true,
1199             get: function() {
1200                 return category_;
1201             },
1202             set: function(newCategory) {
1203                 var args = validator_.validateArgs(arguments, [
1204                     {
1205                         name: 'newCategory',
1206                         type: types_.ENUM,
1207                         values: Object.values(MediaControllerSearchCategory)
1208                     }
1209                 ]);
1210
1211                 // Keyword can be null only if category is NO_CATEGORY.
1212                 if (
1213                     xwalk.utils.type.isNull(keyword_) &&
1214                     args.newCategory != MediaControllerSearchCategory.NO_CATEGORY
1215                 ) {
1216                     throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR);
1217                 }
1218
1219                 category_ = args.newCategory;
1220             }
1221         },
1222         keyword: {
1223             enumerable: true,
1224             get: function() {
1225                 return keyword_;
1226             },
1227             set: function(newKeyword) {
1228                 var args = validator_.validateArgs(arguments, [
1229                     {
1230                         name: 'newKeyword',
1231                         type: types_.STRING,
1232                         nullable: true
1233                     }
1234                 ]);
1235
1236                 // Keyword can only be null if category is NO_CATEGORY.
1237                 if (
1238                     xwalk.utils.type.isNull(args.newKeyword) &&
1239                     this.category != MediaControllerSearchCategory.NO_CATEGORY
1240                 ) {
1241                     throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR);
1242                 }
1243
1244                 keyword_ = args.newKeyword;
1245             }
1246         },
1247         extraData: {
1248             enumerable: true,
1249             get: function() {
1250                 return extraData_;
1251             },
1252             set: function(newData) {
1253                 if (xwalk.utils.type.isUndefined(newData)) {
1254                     newData = null;
1255                 }
1256                 extraData_ = xwalk.utils.type.isNull(newData)
1257                     ? null
1258                     : new tizen.Bundle(newData);
1259             }
1260         }
1261     });
1262 }
1263
1264 function MediaControllerServerInfo(data) {
1265     Object.defineProperties(this, {
1266         name: {
1267             value: data.name,
1268             writable: false,
1269             enumerable: true
1270         },
1271         state: {
1272             value: data.state,
1273             writable: false,
1274             enumerable: true
1275         },
1276         playbackInfo: {
1277             get: function() {
1278                 var result = native_.callSync('MediaControllerClient_getPlaybackInfo', {
1279                     name: this.name
1280                 });
1281                 if (native_.isFailure(result)) {
1282                     throw new native_.getErrorObject(result);
1283                 }
1284                 edit_.allow();
1285                 var data = native_.getResultObject(result);
1286                 var playbackInfo = new MediaControllerPlaybackInfo(data);
1287                 edit_.disallow();
1288
1289                 return playbackInfo;
1290             }.bind(this),
1291             set: function() {},
1292             enumerable: true
1293         },
1294         iconURI: {
1295             get: function() {
1296                 var result = native_.callSync('MediaControllerServerInfo_getIconURI', {
1297                     name: this.name
1298                 });
1299                 if (native_.isFailure(result)) {
1300                     throw new native_.getErrorObject(result);
1301                 }
1302                 var data = native_.getResultObject(result);
1303                 return data.iconURI;
1304             }.bind(this),
1305             set: function() {},
1306             enumerable: true
1307         }
1308     });
1309 }
1310
1311 MediaControllerServerInfo.prototype.sendPlaybackState = function(
1312     state,
1313     successCallback,
1314     errorCallback
1315 ) {
1316     var args = validator_.validateArgs(arguments, [
1317         {
1318             name: 'state',
1319             type: types_.ENUM,
1320             values: Object.keys(MediaControllerPlaybackState)
1321         },
1322         {
1323             name: 'successCallback',
1324             type: types_.FUNCTION,
1325             optional: true,
1326             nullable: true
1327         },
1328         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1329     ]);
1330
1331     var data = {
1332         name: this.name,
1333         state: args.state
1334     };
1335
1336     var callback = function(result) {
1337         if (native_.isFailure(result)) {
1338             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1339             return;
1340         }
1341         native_.callIfPossible(args.successCallback);
1342     };
1343
1344     native_.call('MediaControllerServerInfo_sendPlaybackState', data, callback);
1345 };
1346
1347 MediaControllerServerInfo.prototype.sendPlaybackPosition = function(
1348     position,
1349     successCallback,
1350     errorCallback
1351 ) {
1352     var args = validator_.validateArgs(arguments, [
1353         { name: 'position', type: types_.LONG_LONG },
1354         {
1355             name: 'successCallback',
1356             type: types_.FUNCTION,
1357             optional: true,
1358             nullable: true
1359         },
1360         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1361     ]);
1362
1363     if (args.position < 0) {
1364         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR);
1365     }
1366
1367     var callback = function(result) {
1368         if (native_.isFailure(result)) {
1369             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1370             return;
1371         }
1372         native_.callIfPossible(args.successCallback);
1373     };
1374
1375     var data = {
1376         position: args.position,
1377         name: this.name
1378     };
1379
1380     native_.call('MediaControllerServerInfo_sendPlaybackPosition', data, callback);
1381 };
1382
1383 MediaControllerServerInfo.prototype.sendShuffleMode = function(
1384     mode,
1385     successCallback,
1386     errorCallback
1387 ) {
1388     var args = validator_.validateArgs(arguments, [
1389         { name: 'mode', type: types_.BOOLEAN },
1390         {
1391             name: 'successCallback',
1392             type: types_.FUNCTION,
1393             optional: true,
1394             nullable: true
1395         },
1396         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1397     ]);
1398
1399     var callback = function(result) {
1400         if (native_.isFailure(result)) {
1401             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1402             return;
1403         }
1404         native_.callIfPossible(args.successCallback);
1405     };
1406
1407     var data = {
1408         mode: args.mode,
1409         name: this.name
1410     };
1411     native_.call('MediaControllerServerInfo_sendShuffleMode', data, callback);
1412 };
1413
1414 MediaControllerServerInfo.prototype.sendRepeatMode = function(
1415     mode,
1416     successCallback,
1417     errorCallback
1418 ) {
1419     utils_.printDeprecationWarningFor('sendRepeatMode()', 'sendRepeatState()');
1420     var args = validator_.validateArgs(arguments, [
1421         { name: 'mode', type: types_.BOOLEAN },
1422         {
1423             name: 'successCallback',
1424             type: types_.FUNCTION,
1425             optional: true,
1426             nullable: true
1427         },
1428         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1429     ]);
1430
1431     var callback = function(result) {
1432         if (native_.isFailure(result)) {
1433             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1434             return;
1435         }
1436         native_.callIfPossible(args.successCallback);
1437     };
1438
1439     var data = {
1440         mode: args.mode,
1441         name: this.name
1442     };
1443     native_.call('MediaControllerServerInfo_sendRepeatMode', data, callback);
1444 };
1445
1446 MediaControllerServerInfo.prototype.sendRepeatState = function() {
1447     var args = validator_.validateArgs(arguments, [
1448         {
1449             name: 'state',
1450             type: types_.ENUM,
1451             values: Object.keys(MediaControllerRepeatState)
1452         },
1453         {
1454             name: 'successCallback',
1455             type: types_.FUNCTION,
1456             optional: true,
1457             nullable: true
1458         },
1459         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1460     ]);
1461
1462     var callback = function(result) {
1463         if (native_.isFailure(result)) {
1464             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1465             return;
1466         }
1467         native_.callIfPossible(args.successCallback);
1468     };
1469
1470     var data = {
1471         state: args.state,
1472         name: this.name
1473     };
1474     native_.call('MediaControllerServerInfo_sendRepeatState', data, callback);
1475 };
1476
1477 MediaControllerServerInfo.prototype.sendCommand = function(
1478     command,
1479     data,
1480     successCallback,
1481     errorCallback
1482 ) {
1483     var args = validator_.validateArgs(arguments, [
1484         { name: 'command', type: types_.STRING },
1485         { name: 'data', type: types_.DICTIONARY, nullable: true },
1486         { name: 'successCallback', type: types_.FUNCTION },
1487         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1488     ]);
1489
1490     var callback = function(result) {
1491         if (native_.isFailure(result)) {
1492             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1493             return;
1494         }
1495         native_.callIfPossible(
1496             args.successCallback,
1497             native_.getResultObject(result).data,
1498             native_.getResultObject(result).code
1499         );
1500     };
1501
1502     var nativeData = {
1503         command: args.command,
1504         data: args.data === null ? null : new tizen.Bundle(args.data),
1505         name: this.name,
1506         listenerId: ReplyCommandListener.listenerName
1507     };
1508
1509     var replyListenerId = ReplyCommandListener.addListener(callback);
1510     var result = native_.callSync('MediaControllerServerInfo_sendCommand', nativeData);
1511
1512     ReplyCommandListener.requestIdToListenerId[replyListenerId] = result.requestId;
1513 };
1514
1515 MediaControllerServerInfo.prototype.sendSearchRequest = function(
1516     request,
1517     successCallback,
1518     errorCallback
1519 ) {
1520     var args = validator_.validateArgs(arguments, [
1521         { name: 'request', type: types_.ARRAY, values: SearchFilter },
1522         { name: 'successCallback', type: types_.FUNCTION },
1523         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1524     ]);
1525
1526     if (args.request.length < 1 || args.request.length > 20) {
1527         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR);
1528     }
1529
1530     var callback = function(result) {
1531         if (native_.isFailure(result)) {
1532             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1533             return;
1534         }
1535         var reply = native_.getResultObject(result);
1536         native_.callIfPossible(
1537             args.successCallback,
1538             new RequestReply(reply.data, reply.code)
1539         );
1540     };
1541
1542     var nativeData = {
1543         request: args.request,
1544         name: this.name,
1545         listenerId: ReplyCommandListener.listenerName
1546     };
1547
1548     var result = native_.callSync(
1549         'MediaControllerServerInfo_sendSearchRequest',
1550         nativeData,
1551         callback
1552     );
1553     if (native_.isFailure(result)) {
1554         throw native_.getErrorObject(result);
1555     }
1556
1557     var replyListenerId = ReplyCommandListener.addListener(callback);
1558     ReplyCommandListener.requestIdToListenerId[replyListenerId] = result.requestId;
1559 };
1560
1561 MediaControllerServerInfo.prototype.addServerStatusChangeListener = function(listener) {
1562     var args = validator_.validateArgs(arguments, [
1563         { name: 'listener', type: types_.FUNCTION }
1564     ]);
1565
1566     if (type_.isEmptyObject(ServerInfoStatusListener.listeners)) {
1567         var result = native_.callSync(
1568             'MediaControllerServerInfo_addServerStatusChangeListener',
1569             {
1570                 listenerId: ServerInfoStatusListener.listenerName
1571             }
1572         );
1573         if (native_.isFailure(result)) {
1574             throw native_.getErrorObject(result);
1575         }
1576     }
1577     return ServerInfoStatusListener.addServerInfoListener(args.listener, this.name);
1578 };
1579
1580 MediaControllerServerInfo.prototype.removeServerStatusChangeListener = function(watchId) {
1581     var args = validator_.validateArgs(arguments, [
1582         { name: 'watchId', type: types_.LONG }
1583     ]);
1584
1585     ServerInfoStatusListener.removeServerInfoListener(args.watchId);
1586
1587     if (type_.isEmptyObject(ServerInfoStatusListener.listeners)) {
1588         native_.callSync('MediaControllerServerInfo_removeServerStatusChangeListener');
1589     }
1590 };
1591
1592 MediaControllerServerInfo.prototype.addPlaybackInfoChangeListener = function(listener) {
1593     var args = validator_.validateArgs(arguments, [
1594         {
1595             name: 'listener',
1596             type: types_.LISTENER,
1597             values: [
1598                 'onplaybackchanged',
1599                 'onshufflemodechanged',
1600                 'onrepeatmodechanged',
1601                 'onrepeatstatechanged',
1602                 'onmetadatachanged'
1603             ]
1604         }
1605     ]);
1606
1607     if (type_.isEmptyObject(ServerInfoPlaybackInfoListener.listeners)) {
1608         var result = native_.callSync(
1609             'MediaControllerServerInfo_addPlaybackInfoChangeListener',
1610             {
1611                 listenerId: ServerInfoPlaybackInfoListener.listenerName
1612             }
1613         );
1614         if (native_.isFailure(result)) {
1615             throw native_.getErrorObject(result);
1616         }
1617     }
1618
1619     return ServerInfoPlaybackInfoListener.addServerInfoListener(args.listener, this.name);
1620 };
1621
1622 MediaControllerServerInfo.prototype.removePlaybackInfoChangeListener = function(watchId) {
1623     var args = validator_.validateArgs(arguments, [
1624         { name: 'watchId', type: types_.LONG }
1625     ]);
1626
1627     ServerInfoPlaybackInfoListener.removeServerInfoListener(args.watchId);
1628
1629     if (type_.isEmptyObject(ServerInfoPlaybackInfoListener.listeners)) {
1630         native_.callSync('MediaControllerServerInfo_removePlaybackInfoChangeListener');
1631     }
1632 };
1633
1634 MediaControllerServerInfo.prototype.getAllPlaylists = function(
1635     successCallback,
1636     errorCallback
1637 ) {
1638     var args = validator_.validateArgs(arguments, [
1639         {
1640             name: 'successCallback',
1641             type: types_.FUNCTION,
1642             optional: false,
1643             nullable: false
1644         },
1645         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1646     ]);
1647
1648     var callback = function(result) {
1649         if (native_.isFailure(result)) {
1650             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1651             return;
1652         }
1653         var data = native_.getResultObject(result);
1654         var playlists = [];
1655
1656         for (var i = 0; i < data.length; i++) {
1657             playlists.push(new MediaControllerPlaylist(data[i]));
1658         }
1659
1660         native_.callIfPossible(args.successCallback, playlists);
1661     };
1662
1663     var result = native_.call(
1664         'MediaControllerServerInfo_getAllPlaylists',
1665         { name: this.name },
1666         callback
1667     );
1668
1669     if (native_.isFailure(result)) {
1670         throw native_.getErrorObject(result);
1671     }
1672 };
1673
1674 MediaControllerServerInfo.prototype.sendPlaybackItem = function(
1675     playlistName,
1676     index,
1677     state,
1678     position
1679 ) {
1680     var args = validator_.validateArgs(arguments, [
1681         { name: 'playlistName', type: types_.STRING },
1682         { name: 'index', type: types_.STRING },
1683         {
1684             name: 'state',
1685             type: types_.ENUM,
1686             values: Object.keys(MediaControllerPlaybackState)
1687         },
1688         { name: 'position', type: types_.UNSIGNED_LONG_LONG }
1689     ]);
1690
1691     var data = {
1692         name: this.name,
1693         playlistName: args.playlistName,
1694         index: args.index,
1695         state: args.state,
1696         position: args.position
1697     };
1698
1699     var result = native_.callSync('MediaControllerServerInfo_sendPlaybackItem', data);
1700
1701     if (native_.isFailure(result)) {
1702         throw native_.getErrorObject(result);
1703     }
1704 };
1705
1706 MediaControllerServerInfo.prototype.addPlaylistUpdatedListener = function(listener) {
1707     var args = validator_.validateArgs(arguments, [
1708         {
1709             name: 'listener',
1710             type: types_.LISTENER,
1711             values: ['onplaylistupdated', 'onplaylistdeleted']
1712         }
1713     ]);
1714
1715     if (type_.isEmptyObject(ServerInfoPlaylistUpdatedListener.listeners)) {
1716         var result = native_.callSync(
1717             'MediaControllerServerInfo_addPlaylistUpdatedListener',
1718             {
1719                 listenerId: ServerInfoPlaylistUpdatedListener.listenerName
1720             }
1721         );
1722         if (native_.isFailure(result)) {
1723             throw native_.getErrorObject(result);
1724         }
1725     }
1726
1727     return ServerInfoPlaylistUpdatedListener.addListener(args.listener, this.name);
1728 };
1729
1730 MediaControllerServerInfo.prototype.removePlaylistUpdatedListener = function(watchId) {
1731     var args = validator_.validateArgs(arguments, [
1732         { name: 'watchId', type: types_.LONG }
1733     ]);
1734
1735     ServerInfoPlaylistUpdatedListener.removeListener(args.watchId);
1736
1737     if (type_.isEmptyObject(ServerInfoPlaylistUpdatedListener.listeners)) {
1738         var result = native_.callSync(
1739             'MediaControllerServerInfo_removePlaylistUpdatedListener'
1740         );
1741
1742         if (native_.isFailure(result)) {
1743             throw native_.getErrorObject(result);
1744         }
1745     }
1746 };
1747
1748 var MediaControllerPlaylistItem = function(data) {
1749     var _index = '';
1750     var _metadata = new MediaControllerMetadata();
1751     Object.defineProperties(this, {
1752         index: {
1753             get: function() {
1754                 return _index;
1755             },
1756             set: function(v) {
1757                 _index = edit_.isAllowed && v ? v : _index;
1758             },
1759             enumerable: true
1760         },
1761         metadata: {
1762             get: function() {
1763                 return _metadata;
1764             },
1765             set: function(v) {
1766                 _metadata =
1767                     edit_.isAllowed && v ? new MediaControllerMetadata(v) : _metadata;
1768             },
1769             enumerable: true
1770         }
1771     });
1772
1773     edit_.allow();
1774     if (data instanceof _global.Object) {
1775         for (var prop in data) {
1776             if (data.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
1777                 this[prop] = data[prop];
1778             }
1779         }
1780     }
1781     edit_.disallow();
1782 };
1783
1784 function MediaControllerPlaylist(data) {
1785     var _name = '';
1786
1787     Object.defineProperties(this, {
1788         name: {
1789             get: function() {
1790                 return _name;
1791             },
1792             set: function(v) {
1793                 _name = edit_.isAllowed && v ? v : _name;
1794             },
1795             enumerable: true
1796         }
1797     });
1798     edit_.allow();
1799     if (data instanceof _global.Object) {
1800         for (var prop in data) {
1801             if (data.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
1802                 this[prop] = data[prop];
1803             }
1804         }
1805     }
1806     edit_.disallow();
1807 }
1808
1809 MediaControllerPlaylist.prototype.addItem = function(index, metadata) {
1810     var args = validator_.validateArgs(arguments, [
1811         { name: 'index', type: types_.STRING },
1812         { name: 'metadata', type: types_.DICTIONARY }
1813     ]);
1814
1815     var data = {
1816         index: args.index,
1817         metadata: args.metadata,
1818         name: this.name
1819     };
1820
1821     var result = native_.callSync('MediaControllerPlaylist_addItem', data);
1822
1823     if (native_.isFailure(result)) {
1824         throw native_.getErrorObject(result);
1825     }
1826 };
1827
1828 MediaControllerPlaylist.prototype.getItems = function(successCallback, errorCallback) {
1829     var args = validator_.validateArgs(arguments, [
1830         {
1831             name: 'successCallback',
1832             type: types_.FUNCTION,
1833             optional: false,
1834             nullable: false
1835         },
1836         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
1837     ]);
1838
1839     var data = {
1840         name: this.name
1841     };
1842
1843     var callback = function(result) {
1844         if (native_.isFailure(result)) {
1845             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
1846             return;
1847         }
1848         var data = native_.getResultObject(result);
1849         var items = [];
1850
1851         for (var i = 0; i < data.length; i++) {
1852             items.push(new MediaControllerPlaylistItem(data[i]));
1853         }
1854
1855         native_.callIfPossible(args.successCallback, items);
1856     };
1857
1858     var result = native_.call('MediaControllerPlaylist_getItems', data, callback);
1859
1860     if (native_.isFailure(result)) {
1861         throw native_.getErrorObject(result);
1862     }
1863 };
1864
1865 exports = new MediaControllerManager();
1866 exports.SearchFilter = SearchFilter;
1867 exports.RequestReply = RequestReply;