Re-organizing the code so that the players are object based.
[profile/ivi/MediaPlayer.git] / js / main.js
1 /*
2  * Copyright (c) 2013, Intel Corporation.
3  *
4  * This program is licensed under the terms and conditions of the
5  * Apache License, version 2.0.  The full text of the Apache License is at
6  * http://www.apache.org/licenses/LICENSE-2.0
7  *
8  */
9
10 var currentMenu = "main";
11 var mouseDownEvent;
12 var resizeCB = resizeMainMenu;
13 var screenWidth;
14 var screenHeight;
15 var screenOrientation;
16 var iconWidth;
17 var padding;
18 var contentType = "AUDIO";
19 var currentPlayer;
20 var currentMediaList = $("#audioMediaList");
21 var audioMediaListLoaded = false;
22 var videoMediaListLoaded = false;
23 var imageMediaListLoaded = false;
24 var audioPlayer;
25 var videoPlayer;
26 var imagePlayer;
27 var vidIcon = new Image();
28 var imgIcon = new Image();
29 var musicIcon = new Image();
30 var imagesLoaded = false;
31 var mediaListItemW;
32 var mediaListItemH;
33 var playOnConnect = false;
34 var mediaNameCanvas;
35 var mediaNameCTX;
36 var speechObj;
37 var nightMode = false;
38 var waitingToResumeVideo = false;               //Media has been paused by exterior forces.  If set to true, resume previous media when given the signal.
39 var stopServerSearch;
40
41 var mainMenuTitleTemplateLandscape = {"font" : "oblique bolder 30pt arial", "lineWidth" : 9.5, "fillStyle" : "black", "strokeStyle" : "white", "textAlign" : "left",
42                 "largeShadow" : 8, "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 45, "shadowColor" : "rgba(255, 187, 0, 0.4)"};
43
44 var mainMenuTitleTemplate = {"font" : "oblique bolder 40pt arial", "lineWidth" : 9.5, "fillStyle" : "black", "strokeStyle" : "white", "textAlign" : "left",
45                 "largeShadow" : 8, "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 45, "shadowColor" : "rgba(255, 187, 0, 0.4)"};
46
47 var mainTrackTemplateLandscape = {"font" : "bold 20pt Arial", "lineWidth" : 7.5, "fillStyle" : "black", "strokeStyle" : "white", "textAlign" : "left",
48                 "largeShadow" : 8, "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 45, "shadowColor" : "rgba(255, 187, 0, 0.5))"};
49
50 var mainTrackTemplate = {"font" : "bold 30pt Arial", "lineWidth" : 7.5, "fillStyle" : "black", "strokeStyle" : "white", "textAlign" : "left",
51                 "largeShadow" : 8, "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 45, "shadowColor" : "rgba(255, 187, 0, 0.5))"};
52
53 var mediaTextTemplate2 = {"font" : "bold 20pt Arial", "lineWidth" : 10.0, "fillStyle" : "white", "strokeStyle" : "rgba(0, 0, 0, 1.0)", "textAlign" : "left",
54                 "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 0, "shadowColor" : "rgba(0, 0, 0, 1.0)"};
55
56 var mediaTextTemplate3 = {"font" : "bold 20pt Arial", "lineWidth" : 3.0, "fillStyle" : "white", "strokeStyle" : "rgba(100, 0, 0, 1.0)", "textAlign" : "left"};
57
58 var trackTextTemplate  = {"font" : "bold 16pt Arial", "lineWidth" : 3.0, "fillStyle" : "white", "strokeStyle" : "black", "textAlign" : "left"};
59
60 var mediaTextTemplate2Landscape = {"font" : "bold 15pt Arial", "lineWidth" : 10.0, "fillStyle" : "white", "strokeStyle" : "rgba(0, 0, 0, 1.0)", "textAlign" : "left",
61                 "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 0, "shadowColor" : "rgba(0, 0, 0, 1.0)"};
62
63 var mediaTextTemplate3Landscape = {"font" : "bold 15pt Arial", "lineWidth" : 3.0, "fillStyle" : "white", "strokeStyle" : "rgba(100, 0, 0, 1.0)", "textAlign" : "left"};
64
65 var trackTextTemplateLandscape  = {"font" : "bold 11pt Arial", "lineWidth" : 3.0, "fillStyle" : "white", "strokeStyle" : "black", "textAlign" : "left"};
66
67
68 var mediaTextTemplate = {"font" : "bold 20pt Arial", "fillStyle" : "", "textAlign" : "left",
69                 "shadowOffsetX" : 0, "shadowOffsetY" : 0, "shadowBlur" : 12, "shadowColor" : "red"};
70
71 function onError()
72 {
73         console.log("Content discovery failed");
74 }
75
76 function setupSpeech()
77 {
78         console.log("MediaPlayer in setupSpeech");
79
80         speechObj = tizen.speech;
81         speechObj.vocalizeString(" ");
82
83         try
84         {
85                 var speechEventListener = {
86                         onaudiostart: function(){console.log("MediaPlayer: onaudiostart received");},
87                         onsoundstart: function(){console.log("MediaPlayer: onsoundstart received");},
88                         onspeechstart: function(){console.log("MediaPlayer: onspeechstart received");},
89                         onspeechend: function(){console.log("MediaPlayer: onspeechend received");},
90                         onsoundend: function(){console.log("MediaPlayer: onsoundend received");},
91                         onaudioend: function(){console.log("MediaPlayer: onaudioend received");},
92                         onresult: function(result){
93
94                                 console.log("MediaPlayer: onresult received");
95
96                                 for (var i = 0; i < result.length; i++)
97                                 {
98                                         console.log("MediaPlayer: forloop, command = " + result[i]);
99                                         var commandFound = false;
100
101                                         switch(result[i])
102                                         {
103                                                 case "play":
104                                                         playButtonClick();
105                                                         commandFound = true;
106                                                 break;
107                                                 case "next":
108                                                         nextButtonClick();
109                                                         commandFound = true;
110                                                 break;
111                                                 case "previous":
112                                                         backButtonClick();
113                                                         commandFound = true;
114                                                 break;
115                                                 case "stop":
116                                                 case "pause":
117                                                         pauseButtonClick();
118                                                         commandFound = true;
119                                                 break;
120                                                 default:
121                                                 break;
122                                         }
123
124                                         if (commandFound)
125                                                 break;
126                                 }
127                         },
128                         onnomatch: function(result){console.log("MediaPlayer: onnomatch received ");},
129                         onerror: function(error){console.log("MediaPlayer: onerror received");},
130                         onstart: function(){console.log("MediaPlayer: onstart received");},
131                         onend: function(){console.log("MediaPlayer: onend received");}
132                 }
133
134                 speechObj.setCBListener(speechEventListener);
135
136         }
137         catch(err)
138         {
139                 console.log("MediaPlayer setupSpeech FAILED + " + err.message);
140         }
141
142 }
143
144 function onAudioContentLoaded(newContent)
145 {
146         if (!newContent || newContent.length === 0)
147         {
148                 console.log("MediaPlayer: some content failed to load, trying again for " + audioPlayer.type);
149                 audioPlayer.clearContent();
150                 setTimeout(function(){getMedia(onAudioContentLoaded, "AUDIO");}, 3000);
151                 return;
152         }
153         else
154         {
155                 audioPlayer.updateContent(newContent, true);
156                 audioPlayer.onContentLoaded();
157         }
158 }
159
160 function onVideoContentLoaded(newContent)
161 {
162         if (!newContent || newContent.length === 0)
163         {
164                 console.log("MediaPlayer: some content failed to load, trying again for " + videoPlayer.type);
165                 videoPlayer.clearContent();
166                 setTimeout(function(){getMedia(onVideoContentLoaded, "VIDEO");}, 3000);
167                 return;
168         }
169         else
170         {
171                 videoPlayer.updateContent(newContent, true);
172                 videoPlayer.onContentLoaded();
173         }
174 }
175
176 function onImageContentLoaded(newContent)
177 {
178         if (!newContent || newContent.length === 0)
179         {
180                 console.log("MediaPlayer: some content failed to load, trying again for " + imagePlayer.type);
181                 imagePlayer.clearContent();
182                 setTimeout(function(){getMedia(onImageContentLoaded, "IMAGE");}, 3000);
183                 return;
184         }
185         else
186         {
187                 imagePlayer.updateContent(newContent, true);
188                 imagePlayer.onContentLoaded();
189         }
190 }
191
192 function getMedia(callback, filterType)
193 {
194         console.log("MediaPlayer in getMedia");
195         var manager = tizen.content;
196         var filter = new tizen.AttributeFilter("type", "EXACTLY", filterType);
197         manager.find(callback, onError, null, filter);
198 }
199
200 function showMediaMenu()
201 {
202         console.log("MediaPlayer in showMediaMenu");
203         $(".navButton").show();
204
205         switch(currentPlayer.type)
206         {
207                 case "audio":
208                         $("#mediaName").show();
209                         $(".sortButton").show();
210                         currentPlayer.show();
211
212                         if (screenOrientation === "landscape" )
213                                 $("#audioMediaList").addClass("landscape");
214                         else
215                                 $("#audioMediaList").removeClass("landscape");
216
217                         $("#audioMediaList").show();
218                         break;
219
220                 case "video":
221                         //show video
222                         currentPlayer.show();
223                         //setTimeout(function(){currentPlayer.show()}, 800);            //The video element can't be faded, so wait a moment before showing
224                         break;
225
226                 case "image":
227                         //show image
228                         currentPlayer.show();
229                         break;
230
231                 default:
232                         console.log("Invalid player type");
233                 break;
234         }
235 }
236
237 function showMediaList()
238 {
239         console.log("MediaPlayer in showMediaList");
240
241         if (currentMediaList.is(":visible"))
242                 currentMediaList.hide();
243         else
244         {
245                 currentMediaList.show();
246         }
247 }
248
249 function changeMenu(menuButtonId)
250 {
251         console.log("MediaPlayer in changeMenu");
252         var clickedButton  = $("#" + menuButtonId);
253         var buttonWidth  = clickedButton.width();
254         var buttonHeight = clickedButton.height();
255         var buttonBottom = clickedButton.css("bottom");
256         var buttonRight  = clickedButton.css("right");
257
258 /*
259         //Animate the clicked button slightly
260         clickedButton.animate({
261                 bottom: screenOrientation === "portrait" ? "+=0" : "+=50",
262                                 right: screenOrientation === "portrait" ? "+=50" : "+=0",
263                                                 opacity: "0"
264         }, 300 ,
265
266         //Reset the size once done
267         function() {
268                 clickedButton.width(buttonWidth);
269                 clickedButton.height(buttonHeight);
270
271                 if (screenOrientation === "portrait")
272                         clickedButton.css({"right": buttonRight});
273                 else
274                         clickedButton.css({"bottom": buttonBottom});
275         }
276         );
277 */
278         $(".mainButton").hide();
279
280         switch(menuButtonId)
281         {
282                 case ("mainMusicButton"):
283                         speechObj.vocalizeString("Music player");
284                         currentMenu = "audio";
285                         currentPlayer = audioPlayer;
286                         currentMediaList = $("#audioMediaList");
287                         localStorage.prevMenu = "mainMusicButton";
288
289                         if (audioPlayer.currentAudioContent)
290                         {
291                                 localStorage.prevAudioTrack = audioPlayer.currentAudioContent.contentURI
292                                 localStorage.prevAudioTime = audioPlayer.playerControls.currentTime;
293                         }
294
295                         localStorage.prevVideo = undefined;
296                         localStorage.prevVideoTime = undefined;
297
298                         if (!audioMediaListLoaded)
299                                 audioPlayer.fillMediaList();
300
301                         showMediaMenu();
302                 break;
303
304                 case ("mainVideoButton"):
305                         speechObj.vocalizeString("Video player");
306                         currentMenu = "video";
307                         currentPlayer = videoPlayer;
308                         currentMediaList = $("#videoMediaList");
309                         localStorage.prevMenu = "mainVideoButton";
310                         localStorage.prevAudioTime = undefined;
311                         localStorage.prevAudioTrack = undefined;
312
313                         if (!videoMediaListLoaded)
314                                 videoPlayer.fillMediaList();
315
316                         showMediaMenu();
317                 break;
318
319                 case ("mainImageButton"):
320                         speechObj.vocalizeString("Picture viewer");
321                         currentMenu = "image";
322                         currentPlayer = imagePlayer;
323                         currentMediaList = $("#imageMediaList");
324                         localStorage.prevMenu = "mainImageButton";
325
326                         if (!imageMediaListLoaded)
327                                 imagePlayer.fillMediaList();
328
329                         showMediaMenu();
330                 break;
331                 default:
332                         console.log("Error: No menu by that name");
333                 break;
334         }
335 }
336
337 function showMainMenu()
338 {
339         console.log("MediaPlayer in showMainMenu");
340         currentMenu = "main";
341         localStorage.prevMenu = "MAIN";
342         localStorage.prevAudioTime = undefined;
343         localStorage.prevAudioTrack = undefined;
344         localStorage.prevVideo = undefined;
345         localStorage.prevVideoTime = undefined;
346         currentPlayer.pause();
347
348         //If the media list is showing, hide it.  Remove the mediaListAudioList class if it exists so that it will resize properly
349         if (currentMediaList.is(":visible"))
350                 currentMediaList.hide();
351
352         $(".navButton").hide();
353         $(".sortButton").hide();
354         $("#mediaName").hide();
355         $(".player").hide();
356         $(".mainButton").css({"opacity": "100"});
357         $(".mainButton").show();
358 }
359
360 function sortByAlpha(contentToSort)
361 {
362         currentPlayer.sortByAlpha();
363 }
364
365 function sortByArtist(contentToSort)
366 {
367         currentPlayer.sortByArtist();
368 }
369
370 function sortByAlbum(contentToSort)
371 {
372         currentPlayer.sortByAlbum();
373 }
374
375 /**************************************** NAVIGATION FUNCTIONS *******************************************/
376
377 // This function is called once a file being loaded is ready to play
378 function playLoadedMedia()
379 {
380         audioPlayer.playLoadedMedia();
381 }
382
383 function videoLoaded()
384 {
385     videoPlayer.videoLoaded();
386 }
387
388 function playButtonClick()
389 {
390         console.log("MediaPlayer in playButtonClick");
391         if (!currentPlayer.playing())
392                 currentPlayer.play();
393         else
394                 currentPlayer.pause();
395 }
396
397 function pauseButtonClick()
398 {
399         console.log("MediaPlayer in pauseButtonClick");
400         if (currentPlayer.playing())
401         {
402                 currentPlayer.pause();
403         }
404 }
405
406 function backButtonClick()
407 {
408         console.log("MediaPlayer in backButtonClick");
409         currentPlayer.previous();
410 }
411
412
413 function nextButtonClick()
414 {
415         console.log("MediaPlayer in nextButtonClick");
416         currentPlayer.next();
417 }
418
419 /**************************************** END NAVIGATION FUNCTIONS *******************************************/
420
421 function resizeMainMenu()
422 {
423         console.log("MediaPlayer in resizeMainMenu");
424
425         currentMediaList.hide();
426         screenWidth = window.innerWidth;
427         screenHeight = window.innerHeight;
428         screenOrientation = screenWidth < screenHeight ? "portrait" : "landscape";
429         iconWidth = screenOrientation === "portrait" ? screenHeight / 4 : screenWidth / 4;
430         var padding = 20;
431
432         $(".mainButton").width(iconWidth + "px");
433         $(".mainButton").height(iconWidth + "px");
434
435         var iconsTop  = screenOrientation === "portrait" ? ((screenHeight - (iconWidth * 3)) / 2) - (padding * 3) : (screenHeight - iconWidth) / 2;
436
437         if (screenOrientation === "portrait")
438                 $("#mainMenuButtons").css({"top": iconsTop + "px", "left" : (screenWidth / 2) - (iconWidth /2) - padding + "px", "width" : "50%"});
439         else
440         {
441                 $("#mainMenuButtons").css({"top": iconsTop + "px", "left" : ((iconWidth / 2) - (padding * 3)) + "px", "width" : "100%"});
442                 $("#sortButtons").addClass("landscape");
443         }
444 }
445
446 function resizePlayerPage()
447 {
448         console.log("MediaPlayer in resizePlayerPage");
449
450         screenWidth = window.innerWidth;
451         screenHeight = window.innerHeight;
452
453         var padding = 15;
454         var buttonWidth = screenOrientation === "portrait" ? screenHeight * 0.05 : screenWidth * 0.05;
455
456         $("#backButton").css({"width": buttonWidth + "px", "height": buttonWidth + "px", "top": padding + "px", "left": ((screenWidth / 2) - (buttonWidth *3)) + "px"});
457         $("#nextButton").css({"width": buttonWidth + "px", "height": buttonWidth + "px", "top": padding + "px", "left": ((screenWidth / 2) + (buttonWidth *2)) + "px"});
458         $("#returnButton").css({"width": buttonWidth + "px", "height": buttonWidth + "px", "top": padding + "px", "left": padding + "px"});
459         $("#listButton").css({"width": buttonWidth + "px", "height": buttonWidth + "px", "top": padding + "px", "left": (screenWidth - buttonWidth - padding * 2) + "px"});
460         $("#playButton").css({"width": buttonWidth * 1.15 + "px", "height": buttonWidth * 1.15 + "px", "top": padding * 0.3 + "px", "left": (screenWidth / 2) - (buttonWidth / 2) + "px"});
461
462         mediaNameCanvas.width = (screenWidth);
463         mediaNameCanvas.height = (screenHeight * 0.34) - (buttonWidth + (2 * padding));
464         mediaNameCanvas.style.top = (buttonWidth + (2 * padding) ) + "px";
465         mediaNameCanvas.style.left = "0px";
466
467         var sortButtonTop = (buttonWidth + (2 * padding) ) + mediaNameCanvas.height - (buttonWidth * 1.3);
468         var sortButtonWidth = buttonWidth * 2.5;
469         var buttonSpacing = screenWidth / 5;
470
471         mediaListItemW = $("#videoMediaList").width() * 0.92;
472         mediaListItemH = $("#videoMediaList").height() / 10;
473 }
474
475 function resizeAll()
476 {
477         console.log("MediaPlayer in resizeAll");
478         resizeMainMenu();
479         resizePlayerPage();
480 }
481
482 /*
483  * swipe - Handles swipe events.  Currently it just acts as another way to hit the next / back buttons, but
484  * it could be expanded to other things in the future.
485  */
486
487 function swipe(direction, object)
488 {
489         console.log("MediaPlayer in swipe");
490         switch (object)
491         {
492         case "mediaName":
493                 if (direction === "right")
494                         nextButtonClick();
495                 else if (direction === "left")
496                         backButtonClick();
497                 break;
498
499         default:
500                 break;
501         }
502 }
503
504 function toggleNightMode(nightModeValue)
505 {
506         if (nightMode !== nightModeValue)
507         {
508                 Array.prototype.forEach.call (document.querySelectorAll ('*'), function (el) {el.classList.toggle('night');});
509                 nightMode = nightModeValue;
510         }
511 }
512
513 function init()
514 {
515         console.log("MediaPlayer in init");
516
517         musicIcon.src = "images/musicIcon.png";
518         vidIcon.src = "images/videoIcon.png";
519         imgIcon.src = "images/imageIcon.png";
520         mediaNameCanvas = document.getElementById("mediaName");
521         mediaNameCTX = mediaNameCanvas.getContext("2d");
522
523         var vehicle = tizen.vehicle;
524
525
526         /* Subscribe to AMB NightMode signal, and switch colors
527          * upon receipt of the signal
528          */
529
530         if (vehicle && vehicle !== undefined)
531         {
532                 var getVal = vehicle.get("NightMode");
533
534                 //Check that NightMode returned a value before trying to hook up to it
535                 if (getVal)
536                 {
537                         toggleNightMode(getVal.nightMode);
538                 }
539
540                 /* Subscribe to AMB NightMode signal, and switch colors
541                 * upon receipt of the signal
542                 */
543
544                 vehicle.subscribe("NightMode",function(value) {
545                                 console.log("MediaPlayer: Day / Night mode changed to " + value.nightMode);
546                                 toggleNightMode(value.nightMode);
547                 });
548
549
550                 /* Subscribe to AMB DrivingMode signal, and pause video
551                  * upon receipt of the signal
552                  */
553
554
555                 vehicle.subscribe("DrivingMode",function(value) {
556                         console.log("MediaPlayer: DrivingMode changed to " + value.drivingMode);
557
558                         if (value.drivingMode > 0 && currentPlayer.type === "video" && currentPlayer.playing())
559                         {
560                                 console.log("MediaPlayer: pausing video due to vehicle motion");
561                                 currentPlayer.pause();
562                                 waitingToResumeVideo = true;
563                         }
564                         else if (value.drivingMode === 0 && currentPlayer.type === "video" && waitingToResumeVideo)
565                         {
566                                 console.log("MediaPlayer: vehicle has stopped, resuming video");
567                                 currentPlayer.play();
568                                 waitingToResumeVideo = false;
569                         }
570                 });
571         }
572
573         //Setup voice control
574         if (tizen.speech)
575                 setupSpeech();
576         else
577                 console.log("MediaPlayer: Speech Recognition not running, voice control will be unavailable");
578
579         audioPlayer = new MediaPlayer("audio");
580         videoPlayer = new MediaPlayer("video");
581         imagePlayer = new MediaPlayer("image");
582         currentPlayer = audioPlayer;
583
584         //Resize all items and search for local media
585         resizeAll();
586
587         if (localStorage.prevMenu && localStorage.prevMenu !== "MAIN")
588                 changeMenu(localStorage.prevMenu)
589         else
590                 showMainMenu();
591
592         getMedia(onAudioContentLoaded, "AUDIO");
593         getMedia(onVideoContentLoaded, "VIDEO");
594         getMedia(onImageContentLoaded, "IMAGE");
595
596         //Check if DLNA plugin is installed.  If so, scan for media.
597         if (tizen.mediaserver)
598         {
599                 //Currently no success signal, so continue trying until a server is found.  Once that
600                 //happens, clear the stopServerSearch interval
601
602                 stopServerSearch = setInterval(function(){console.log("MediaPlayer searching for remote media..."); tizen.mediaserver.scanNetwork(foundMediaServer);}, 5000);
603         }
604         else
605                 console.log("MediaPlayer: No DLNA server running, using local media only...");
606
607         //Prevent highlighting
608         window.ondragstart = function() { return false; }
609
610         $(window).bind('resize', resizeAll);
611
612         //Simple swipe detection
613         $("#mediaName").mousedown(function(e){mouseDownEvent = e;});
614         $("#mediaName").mouseup(function(e){
615                 if (Math.abs(mouseDownEvent.clientY - e.clientY) < 100)
616                 {
617                         if (Math.abs(mouseDownEvent.clientX - e.clientX) > 100)
618                         {
619                                 if (mouseDownEvent.clientX > e.clientX)
620                                         swipe("left", "mediaName");
621                                 else
622                                         swipe("right", "mediaName");
623                         }
624                 }
625         });
626
627     document.getElementById('videoPlayer').addEventListener("playing", function() {
628         $("#playButton").toggleClass('playing', true);
629         $("#navigationButtons").hide();
630     }, false);
631     document.getElementById('videoPlayer').addEventListener("pause", function() {
632         $("#playButton").toggleClass('playing', false);
633         $("#navigationButtons").show();
634     }, false);
635     document.getElementById('videoPlayer').addEventListener("ended", function() {
636         $("#playButton").toggleClass('playing', false);
637         $("#navigationButtons").show();
638     }, false);
639     document.getElementById('audioPlayer').addEventListener("playing", function() {
640         $("#playButton").toggleClass('playing', true);
641     }, false);
642     document.getElementById('audioPlayer').addEventListener("pause", function() {
643         $("#playButton").toggleClass('playing', false);
644     }, false);
645     document.getElementById('audioPlayer').addEventListener("ended", function() {
646         $("#playButton").toggleClass('playing', false);
647     }, false);
648     document.getElementById('imagePlayer').addEventListener("click", function() {
649         if (!imagePlayer.playing)
650         {
651             // next picture
652             nextButtonClick();
653         }
654         else
655         {
656             // pause the slideshow and fade in the controls
657             imagePlayer.pause();
658             $("#navigationButtons").show();
659         }
660     }, false);
661 }
662
663 $(document).ready(function () {
664         init();
665 });