1 /*global disconnectCall, Bootstrap, Carousel, ContactsLibrary, getAppByID, disconnectCall, Configuration, Speech, Phone, changeCssBgImageColor, ThemeKeyColorSelected */
4 * This application provides voice call from paired Bluetooth phone. Application uses following APIs:
6 * * {{#crossLink "Phone"}}{{/crossLink}} library
7 * * [tizen.bt]() as replacement of [tizen.bluetooth](https://developer.tizen.org/dev-guide/2.2.0/org.tizen.web.device.apireference/tizen/bluetooth.html) API due to
8 * conficts in underlying framework
10 * Application supports multiple connected devices, however only one of the devices can be selected at the time.
11 * Selection is done from {{#crossLink "Bluetooth"}}{{/crossLink}} UI. In case that phone has active call additional {{#crossLink "Carousel"}}{{/crossLink}}
12 * element is replaced by {{#crossLink "CallDuration"}}{{/crossLink}} element.
14 * Application allows following operations:
16 * * {{#crossLink "Phone/acceptCall:method"}}Place call{{/crossLink}}
17 * * Handles incoming calls passed from {{#crossLink "IncomingCall"}}{{/crossLink}} widget
18 * * Display {{#crossLink "Carousel"}}call history{{/crossLink}}
19 * * Display {{#crossLink "ContactsLibrary"}}contact list{{/crossLink}}
20 * * Mute/unmute call - not working due to [TIVI-2448](https://bugs.tizen.org/jira/browse/TIVI-2448)
22 * Additionaly application can be controlled using speech recognition via {{#crossLink "Speech"}}{{/crossLink}} component.
24 * Hover and click on elements in images below to navigate to components of Phone application.
26 * <img id="Image-Maps_1201312180420487" src="../assets/img/phone.png" usemap="#Image-Maps_1201312180420487" border="0" width="649" height="1152" alt="" />
27 * <map id="_Image-Maps_1201312180420487" name="Image-Maps_1201312180420487">
28 * <area shape="rect" coords="0,0,573,78" href="../classes/TopBarIcons.html" alt="top bar icons" title="Top bar icons" />
29 * <area shape="rect" coords="0,77,644,132" href="../classes/Clock.html" alt="clock" title="Clock" />
30 * <area shape="rect" coords="0,994,644,1147" href="../classes/BottomPanel.html" alt="bottom panel" title="Bottom panel" />
31 * <area shape="rect" coords="573,1,644,76" href="../modules/Settings.html" alt="Settings" title="Settings" />
32 * <area shape="rect" coords="552,136,646,181" href="../classes/ContactsLibrary.html" alt="Contacts library" title="Contacts library" />
33 * <area shape="rect" coords="95,345,164,491" href="../classes/Phone.html#method_acceptCall" alt="Call button" title="Call button" />
34 * <area shape="rect" coords="1,668,644,984" href="../classes/Carousel.html" alt="" title="History carousel" />
35 * <area shape="rect" coords="171,181,471,635" alt="" href="../classes/keyboard.html" alt="Keyboard input" title="Keyboard input" >
38 * @module PhoneApplication
39 * @main PhoneApplication
44 * Holds object of input for dialing phone number.
46 * @property telInput {Object}
50 * Instance of class Bootstrap, this class provides unified way to boot up the HTML applications by loading shared components in proper order.
51 * * {{#crossLink "Bootstrap"}}{{/crossLink}}
53 * @property bootstrap {Object}
58 * Instance of class Carousel, this class provides methods to operate with history carousel.
59 * * {{#crossLink "Carousel"}}{{/crossLink}}
61 * @property callHistoryCarousel {Object}
63 var callHistoryCarousel = null;
66 * This property holds information about accept Phone call from Other widgets.
67 * If is true, phone call came from another widget.
68 * @property acceptPhoneCallFromOtherWidget {Boolean}
70 var acceptPhoneCallFromOtherWidget = false;
73 * Class handling user input from keyboard
80 * property holding Interval within which next click on the same key is considered as rotating associated characters with the given key.
82 * @property clickInterval {int}
87 * property holding info about last pressed key
89 * @property pressedKey {string}
94 * Array of input object holding info about main key character, all associated characters with the given key and index of currently used key
96 * @property inputs {Array of Object}
104 values: ["2", "A", "B", "C"],
108 values: ["3", "D", "E", "F"],
112 values: ["4", "G", "H", "I"],
116 values: ["5", "J", "K", "L"],
120 values: ["6", "M", "N", "O"],
124 values: ["7", "P", "Q", "R", "S"],
128 values: ["8", "T", "U", "V"],
132 values: ["9", "W", "X", "Y", "Z"],
149 * property holding time of clickedInterval started
151 * @property startedTime {Date}
156 * property holding input object associated with last pressed key
158 * @property selectedInput {Object}
163 * function starting clickInterval
167 startTimer: function() {
169 keyboard.startedTime = new Date();
173 * function testing if clickInterval expired
175 * @method intervalExpired
176 * @param currTime {Date}
178 intervalExpired: function(currTime) {
180 if (currTime - keyboard.startedTime > keyboard.clickInterval) {
181 keyboard.resetIndices();
188 * function reseting indices for all input objects to 0
190 * @method resetIndices
192 resetIndices: function() {
194 for (var i in keyboard.inputs) {
195 if (keyboard.inputs.hasOwnProperty(i)) {
196 keyboard.inputs[i].index = 0;
202 * function cycling associated input characters within clickInterval
206 nextKey: function() {
208 for (var i in keyboard.inputs) {
209 if (keyboard.pressedKey === keyboard.inputs[i].key) {
210 if (keyboard.inputs[i].values.length > 1) {
211 if (keyboard.inputs[i].index < keyboard.inputs[i].values.length - 1) {
212 keyboard.inputs[i].index += 1;
214 keyboard.inputs[i].index = 0;
217 keyboard.inputs[i].index = 0;
219 keyboard.selectedInput = keyboard.inputs[i];
220 return keyboard.inputs[i].values[keyboard.inputs[i].index];
223 return keyboard.pressedKey;
227 * function setting selected input object based on last pressed key
229 * @method selectInput
231 selectInput: function() {
233 for (var i in keyboard.inputs) {
234 if (keyboard.pressedKey === keyboard.inputs[i].key) {
235 keyboard.selectedInput = keyboard.inputs[i];
242 * Holds status of calling panel initialization.
244 * @property callingPanelInitialized {Boolean} if is true, calling panel is initialized
247 var callingPanelInitialized = false;
250 * Class which provides initialize call info.
252 * @method initializeCallInfo
253 * @param contact {Object} Contact object.
256 function initializeCallInfo(contact) {
259 console.log(contact);
262 if ( !! contact.name) {
264 if (contact.name.displayName) {
265 nameStr = contact.name.displayName;
267 nameStr = !! contact.name.firstName ? contact.name.firstName : "";
268 nameStr += !! contact.name.lastName ? " " + contact.name.lastName : "";
270 $("#callName").html(nameStr.trim());
272 $("#callName").html("Unknown");
275 if ( !! contact.phoneNumbers && contact.phoneNumbers.length) {
276 callNumber = !! contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : "";
277 $("#callNumber").html(callNumber);
279 $("#callNumber").html("Unknown");
282 if ( !! contact.photoURI) {
283 $("#callPhoto").attr("src", contact.photoURI);
286 $("#callName").html("Unknown");
287 $("#callNumber").html("Unknown");
290 if (!callingPanelInitialized) {
291 $(".noVolumeSlider").noUiSlider({
297 orientation: "horizontal",
299 var VolumeSlider = parseInt($(".noVolumeSlider").val(), 10);
300 console.log("noVolumeSlider" + VolumeSlider);
303 callingPanelInitialized = true;
306 if ($("#callButton").hasClass("callingFalse")) {
307 $("#callButton").removeClass("callingFalse");
308 $("#callButton").addClass("callingTrue");
313 * Class which provides methods to operate with call duration. Component show information about current call (call number or call contact, time duration of call).
315 * @class CallDuration
320 * Holds value of seconds.
322 * @property sec {Integer}
326 * Holds value of minutes.
328 * @property min {Integer}
332 * Holds value of hours.
334 * @property hour {Integer}
338 * Holds object of timer.
340 * @property timeout {Object}
344 * Method provides initialization of call timers.
348 startWatch: function() {
352 this.timeout = window.setInterval(function() {
357 this.timeout = window.setInterval(function() {
364 * Method provides reset call timers.
368 resetIt: function() {
370 CallDuration.sec = 0;
371 CallDuration.min = 0;
372 CallDuration.hour = 0;
373 window.clearTimeout(CallDuration.timeout);
374 var activeCall = tizen.phone.activeCall();
375 var callStatus = activeCall.state.toLowerCase();
376 if (callStatus === "DIALING".toLowerCase()) {
377 $("#callDuration").html("DIALING");
378 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
379 $("#callDuration").html("ENDED");
381 $("#callDuration").html(
382 ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec));
387 * Method provides call stop watch.
391 stopwatch: function() {
394 var activeCall = tizen.phone.activeCall();
395 var callStatus = activeCall.state.toLowerCase();
396 if (callStatus === "DIALING".toLowerCase()) {
397 $("#callDuration").html("DIALING");
398 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
399 $("#callDuration").html("ENDED");
403 if (CallDuration.sec === 60) {
404 CallDuration.sec = 0;
408 if (CallDuration.min === 60) {
409 CallDuration.min = 0;
412 $("#callDuration").html(
413 ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec));
419 * This property holds information about mute of call. If is true, phone call is mute.
421 * @property VolumeMuteStatus
424 var VolumeMuteStatus = false;
427 * Class provides a muting of a call
432 function muteCall() {
434 VolumeMuteStatus = VolumeMuteStatus ? false : true;
436 // Not working due to TIVI-2448
438 //tizen.phone.muteCall(VolumeMuteStatus);
440 if (VolumeMuteStatus) {
441 changeCssBgImageColor(".muteButton", ThemeKeyColorSelected);
442 $(".muteButton").addClass("fontColorSelected");
444 changeCssBgImageColor(".muteButton", "#FFFFFF");
445 $(".muteButton").removeClass("fontColorSelected");
450 * Class which provides methods to call contact.
453 * @param contact {Object} Contact object.
456 function acceptCall(contact) {
459 ContactsLibrary.hide();
460 if ($("#settingsTabs").tabs) {
461 $("#settingsTabs").tabs("hidePage");
463 $("#callBox").removeClass("callBoxHidden");
464 $("#callBox").addClass("callBoxShow");
465 $('#contactsCarouselBox').removeClass("contactsCarouselBoxShow");
466 $('#contactsCarouselBox').addClass("contactsCarouselBoxHide");
468 CallDuration.resetIt();
470 initializeCallInfo(contact);
471 var activeCall = tizen.phone.activeCall();
472 var callStatus = activeCall.state.toLowerCase();
473 if (callStatus !== "ACTIVE".toLowerCase() && callStatus !== "DIALING".toLowerCase()) {
475 if (callStatus === "INCOMING".toLowerCase()) {
476 tizen.phone.answerCall(function(result) {
477 console.log(result.message);
479 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
481 var callNumber = contact.phoneNumbers[0] && contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : "";
482 tizen.phone.invokeCall(callNumber, function(result) {
483 console.log(result.message);
488 } else if (callStatus === "ACTIVE".toLowerCase()) {
489 CallDuration.startWatch();
495 * Class which provides disconnect call.
497 * @method disconnectCall
500 function disconnectCall() {
502 $("#callButton").removeClass("callingTrue");
503 $("#callButton").addClass("callingFalse");
504 if (acceptPhoneCallFromOtherWidget !== true) {
505 $("#callBox").removeClass("callBoxShow");
506 $("#callBox").addClass("callBoxHidden");
507 $('#contactsCarouselBox').removeClass("contactsCarouselBoxHide");
508 $('#contactsCarouselBox').addClass("contactsCarouselBoxShow");
510 CallDuration.resetIt();
512 tizen.phone.hangupCall(function(result) {
513 console.log(result.message);
516 $("#inputPhoneNumber").val('');
522 setTimeout(function() {
523 /* initialize phone widget by remote device status */
525 tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDevice){
526 if (selectedRemoteDevice !== "") {
527 $("#noPairedDevice").hide();
528 $("#loadingHistorySpinnerWrapper").show();
530 $("#noPairedDevice").show();
533 /* initialize phone widget by active call status */
534 var activeCall = tizen.phone.activeCall();
535 var callStatus = activeCall.state.toLowerCase();
536 if (callStatus === "INCOMING".toLowerCase() || callStatus === "DIALING".toLowerCase() || callStatus === "ACTIVE".toLowerCase()) {
538 if (tizen.phone.callState) {
539 contact = activeCall.contact;
541 acceptPhoneCallFromOtherWidget = true;
543 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
547 /* start keyboard timer */
548 keyboard.startTimer();
549 /* initialize bootstrap */
550 bootstrap = new Bootstrap(function(status) {
551 telInput = $("#inputPhoneNumber");
552 $("#clockElement").ClockPlugin('init', 5);
553 $("#clockElement").ClockPlugin('startTimer');
554 $("#topBarIcons").topBarIconsPlugin('init', 'phone');
555 $('#bottomPanel').bottomPanel('init');
556 if (!callHistoryCarousel) {
558 callHistoryCarousel = new Carousel();
561 if (typeof Phone !== "undefined") {
562 /* add listener to selected remote device */
563 tizen.phone.addRemoteDeviceSelectedListener(function(returnID) {
564 if ((!!returnID && !!returnID.error) || (!!returnID && !!returnID.value && returnID.value === "")) {
565 $("#loadingHistorySpinnerWrapper").hide();
566 $(".caroufredsel_wrapper").hide();
567 $("#noPairedDevice").show();
569 $("#noPairedDevice").hide();
570 $("#loadingHistorySpinnerWrapper").show();
571 $(".caroufredsel_wrapper").show();
574 /* initialize contacts and call history, if not accept phone call from another widget */
575 if (acceptPhoneCallFromOtherWidget !== true) {
576 window.setTimeout(function() {
577 Phone.loadContacts(function(err) {
579 ContactsLibrary.init();
580 Phone.loadCallHistory(function(err) {
582 $("#loadingHistorySpinnerWrapper").hide();
583 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
591 /* add listener to change contacts list */
592 tizen.phone.addContactsChangedListener(function() {
593 if (acceptPhoneCallFromOtherWidget !== true) {
594 window.setTimeout(function() {
595 Phone.loadContacts(function(err) {
597 ContactsLibrary.init();
603 /* add listener to change call history */
604 tizen.phone.addCallHistoryChangedListener(function() {
605 $("#loadingHistorySpinnerWrapper").show();
606 if (acceptPhoneCallFromOtherWidget !== true) {
607 window.setTimeout(function() {
608 Phone.loadCallHistory(function(err) {
610 $("#loadingHistorySpinnerWrapper").hide();
611 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
621 $("#contactsLibraryButton").bind('click', function() {
623 ContactsLibrary.show();
627 $(".numbersBox").delegate("#numberButton", "click", function() {
628 var pressTime = new Date(),
629 number, oneCharPX = 32;
630 if (keyboard.intervalExpired(pressTime)) {
631 number = telInput.attr("value") + $(this).data("id");
632 telInput.attr("value", number);
633 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
634 keyboard.pressedKey = $(this).data("id").toString();
637 if (keyboard.pressedKey === "-1" || keyboard.pressedKey !== $(this).data("id").toString()) {
638 number = telInput.attr("value") + $(this).data("id");
639 telInput.attr("value", number);
640 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
641 keyboard.pressedKey = $(this).data("id").toString();
643 var phoneNumText = telInput.attr("value");
644 if (keyboard.pressedKey === $(this).data("id").toString() && keyboard.selectedInput !== null && keyboard.selectedInput.values.length === 1) {
645 number = telInput.attr("value") + $(this).data("id");
646 telInput.attr("value", number);
647 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
649 var numToUpdate = phoneNumText.slice(0, phoneNumText.length - 1);
650 numToUpdate += keyboard.nextKey();
651 telInput.attr("value", numToUpdate);
656 keyboard.selectInput();
657 keyboard.startTimer();
661 $(".inputPhoneNumberBox").delegate("#deleteButton", "click", function() {
662 var number = telInput.attr("value");
663 number = number.slice(0, number.length - 1);
664 telInput.attr("value", number);
668 $('#callButton').bind('click', function() {
669 var phoneNumber = $("#inputPhoneNumber").val();
670 if ($("#callBox").hasClass("callBoxShow")) {
672 } else if (phoneNumber !== "") {
673 var contact = Phone.getContactByPhoneNumber(phoneNumber);
674 if (contact === null) {
686 $('.muteButton').bind('click', function() {
690 /* add listener to change call history entry, because if call is ended tizen.phone give back only last history object */
691 tizen.phone.addCallHistoryEntryAddedListener(function(contact) {
692 if (acceptPhoneCallFromOtherWidget !== true) {
695 var tmpCallHistory = Phone.callHistory();
697 tmpContact.push(contact);
698 tmpContact = Phone.formatCallHistory(tmpContact);
699 tmpCallHistory.unshift(tmpContact[0]);
700 Phone.callHistory(tmpCallHistory);
702 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
706 /* add listener to change call state */
707 tizen.phone.addCallChangedListener(function(result) {
709 var activeCall = tizen.phone.activeCall();
710 if ( !! result.contact.name) {
711 contact = result.contact;
715 /* jshint camelcase: false */
716 number: activeCall.line_id
717 /* jshint camelcase: true */
723 console.log("result.state " + result.state);
725 switch (result.state.toLowerCase()) {
726 case "DISCONNECTED".toLowerCase():
728 disconnectCall(contact);
730 if (acceptPhoneCallFromOtherWidget === true) {
732 window.setTimeout(function() {
733 if (typeof (tizen.application.getCurrentApplication) !== "undefined") {
734 tizen.application.getCurrentApplication().exit();
739 Configuration.set("acceptedCall", "false");
742 case "ACTIVE".toLowerCase():
743 if (Configuration._values.acceptedCall !== "true") {
745 self.incomingCall.acceptIncommingCall();
746 CallDuration.startWatch();
747 console.log("phone active");
748 Configuration.set("acceptedCall", "true");
751 case "DIALING".toLowerCase():
758 if (typeof(Speech) !== 'undefined') {
759 /* add listener to voice recognition */
760 Speech.addVoiceRecognitionListener({
762 if (ContactsLibrary.currentSelectedContact !== "" && $('#library').library("isVisible")) {
763 acceptCall(ContactsLibrary.currentSelectedContact);
768 console.warn("Speech API is not available.");
777 * Class which provides call contact carousel.
779 * @method callContactCarousel
780 * @param contact {Object} Contact object.
785 function callContactCarousel(contact) {
792 * Class which provides call by contact ID.
794 * @method callContactById
795 * @param contactId {String} Contact id.
798 function callContactById(contactId) {
800 $("#contactDetailMobile").addClass("fontColorSelected ");
801 if (contactId !== "" && typeof contactId !== undefined) {
803 var contactObject = Phone.getContactById(contactId);
804 if ( !! contactObject) {
805 window.setTimeout(function() {
806 acceptCall(contactObject);
807 $("#contactDetailMobile").removeClass("fontColorSelected ");
810 console.log("contact not found");
811 $("#contactDetailMobile").removeClass("fontColorSelected ");