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 hystory 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 callStatus = tizen.phone.activeCall.state.toLowerCase();
375 if (callStatus === "DIALING".toLowerCase()) {
376 $("#callDuration").html("DIALING");
377 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
378 $("#callDuration").html("ENDED");
380 $("#callDuration").html(
381 ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec));
386 * Method provides call stop watch.
390 stopwatch: function() {
393 var callStatus = tizen.phone.activeCall.state.toLowerCase();
394 if (callStatus === "DIALING".toLowerCase()) {
395 $("#callDuration").html("DIALING");
396 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
397 $("#callDuration").html("ENDED");
401 if (CallDuration.sec === 60) {
402 CallDuration.sec = 0;
406 if (CallDuration.min === 60) {
407 CallDuration.min = 0;
410 $("#callDuration").html(
411 ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec));
417 * This property holds information about mute of call. If is true, phone call is mute.
419 * @property VolumeMuteStatus
422 var VolumeMuteStatus = false;
425 * Class provides a muting of a call
430 function muteCall() {
432 VolumeMuteStatus = VolumeMuteStatus ? false : true;
434 // Not working due to TIVI-2448
436 //tizen.phone.muteCall(VolumeMuteStatus);
438 if (VolumeMuteStatus) {
439 changeCssBgImageColor(".muteButton", ThemeKeyColorSelected);
440 $(".muteButton").addClass("fontColorSelected");
442 changeCssBgImageColor(".muteButton", "#FFFFFF");
443 $(".muteButton").removeClass("fontColorSelected");
448 * Class which provides methods to call contact.
451 * @param contact {Object} Contact object.
454 function acceptCall(contact) {
457 ContactsLibrary.hide();
458 if ($("#settingsTabs").tabs) {
459 $("#settingsTabs").tabs("hidePage");
461 $("#callBox").removeClass("callBoxHidden");
462 $("#callBox").addClass("callBoxShow");
463 $('#contactsCarouselBox').removeClass("contactsCarouselBoxShow");
464 $('#contactsCarouselBox').addClass("contactsCarouselBoxHide");
466 CallDuration.resetIt();
468 initializeCallInfo(contact);
469 var callStatus = tizen.phone.activeCall.state.toLowerCase();
470 if (callStatus !== "ACTIVE".toLowerCase() && callStatus !== "DIALING".toLowerCase()) {
472 if (callStatus === "INCOMING".toLowerCase()) {
473 tizen.phone.answerCall(function(result) {
474 console.log(result.message);
476 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
478 var callNumber = contact.phoneNumbers[0] && contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : "";
479 tizen.phone.invokeCall(callNumber, function(result) {
480 console.log(result.message);
485 } else if (callStatus === "ACTIVE".toLowerCase()) {
486 CallDuration.startWatch();
492 * Class which provides disconnect call.
494 * @method disconnectCall
497 function disconnectCall() {
499 $("#callButton").removeClass("callingTrue");
500 $("#callButton").addClass("callingFalse");
501 if (acceptPhoneCallFromOtherWidget !== true) {
502 $("#callBox").removeClass("callBoxShow");
503 $("#callBox").addClass("callBoxHidden");
504 $('#contactsCarouselBox').removeClass("contactsCarouselBoxHide");
505 $('#contactsCarouselBox').addClass("contactsCarouselBoxShow");
507 CallDuration.resetIt();
509 tizen.phone.hangupCall(function(result) {
510 console.log(result.message);
513 $("#inputPhoneNumber").val('');
519 setTimeout(function() {
520 /* initialize phone widget by remote device status */
522 tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDevice){
523 if (selectedRemoteDevice !== "") {
524 $("#noPairedDevice").hide();
525 $("#loadingHistorySpinnerWrapper").show();
527 $("#noPairedDevice").show();
530 /* initialize phone widget by active call status */
531 var callStatus = tizen.phone.activeCall.state.toLowerCase();
532 if (callStatus === "INCOMING".toLowerCase() || callStatus === "DIALING".toLowerCase() || callStatus === "ACTIVE".toLowerCase()) {
534 if (tizen.phone.callState) {
535 contact = tizen.phone.activeCall.contact;
537 acceptPhoneCallFromOtherWidget = true;
539 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
543 /* start keyboard timer */
544 keyboard.startTimer();
545 /* initialize bootstrap */
546 bootstrap = new Bootstrap(function(status) {
547 telInput = $("#inputPhoneNumber");
548 $("#clockElement").ClockPlugin('init', 5);
549 $("#clockElement").ClockPlugin('startTimer');
550 $("#topBarIcons").topBarIconsPlugin('init', 'phone');
551 $('#bottomPanel').bottomPanel('init');
552 if (!callHistoryCarousel) {
554 callHistoryCarousel = new Carousel();
557 if (typeof Phone !== "undefined") {
558 /* add listener to selected remote device */
559 tizen.phone.addRemoteDeviceSelectedListener(function(returnID) {
560 if ((!!returnID && !!returnID.error) || (!!returnID && !!returnID.value && returnID.value === "")) {
561 $("#loadingHistorySpinnerWrapper").hide();
562 $(".caroufredsel_wrapper").hide();
563 $("#noPairedDevice").show();
565 $("#noPairedDevice").hide();
566 $("#loadingHistorySpinnerWrapper").show();
567 $(".caroufredsel_wrapper").show();
570 /* initialize contacts and call history, if not accept phone call from another widget */
571 if (acceptPhoneCallFromOtherWidget !== true) {
572 window.setTimeout(function() {
573 Phone.loadContacts(function(err) {
575 ContactsLibrary.init();
576 Phone.loadCallHistory(function(err) {
578 $("#loadingHistorySpinnerWrapper").hide();
579 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
587 /* add listener to change contacts list */
588 tizen.phone.addContactsChangedListener(function() {
589 if (acceptPhoneCallFromOtherWidget !== true) {
590 window.setTimeout(function() {
591 Phone.loadContacts(function(err) {
593 ContactsLibrary.init();
599 /* add listener to change call history */
600 tizen.phone.addCallHistoryChangedListener(function() {
601 $("#loadingHistorySpinnerWrapper").show();
602 if (acceptPhoneCallFromOtherWidget !== true) {
603 window.setTimeout(function() {
604 Phone.loadCallHistory(function(err) {
606 $("#loadingHistorySpinnerWrapper").hide();
607 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
617 $("#contactsLibraryButton").bind('click', function() {
619 ContactsLibrary.show();
623 $(".numbersBox").delegate("#numberButton", "click", function() {
624 var pressTime = new Date(),
625 number, oneCharPX = 32;
626 if (keyboard.intervalExpired(pressTime)) {
627 number = telInput.attr("value") + $(this).data("id");
628 telInput.attr("value", number);
629 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
630 keyboard.pressedKey = $(this).data("id").toString();
633 if (keyboard.pressedKey === "-1" || keyboard.pressedKey !== $(this).data("id").toString()) {
634 number = telInput.attr("value") + $(this).data("id");
635 telInput.attr("value", number);
636 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
637 keyboard.pressedKey = $(this).data("id").toString();
639 var phoneNumText = telInput.attr("value");
640 if (keyboard.pressedKey === $(this).data("id").toString() && keyboard.selectedInput !== null && keyboard.selectedInput.values.length === 1) {
641 number = telInput.attr("value") + $(this).data("id");
642 telInput.attr("value", number);
643 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
645 var numToUpdate = phoneNumText.slice(0, phoneNumText.length - 1);
646 numToUpdate += keyboard.nextKey();
647 telInput.attr("value", numToUpdate);
652 keyboard.selectInput();
653 keyboard.startTimer();
657 $(".inputPhoneNumberBox").delegate("#deleteButton", "click", function() {
658 var number = telInput.attr("value");
659 number = number.slice(0, number.length - 1);
660 telInput.attr("value", number);
664 $('#callButton').bind('click', function() {
665 var phoneNumber = $("#inputPhoneNumber").val();
666 if ($("#callBox").hasClass("callBoxShow")) {
668 } else if (phoneNumber !== "") {
669 var contact = Phone.getContactByPhoneNumber(phoneNumber);
670 if (contact === null) {
682 $('.muteButton').bind('click', function() {
686 /* add listener to change call history entry, because if call is ended tizen.phone give back only last history object */
687 tizen.phone.addCallHistoryEntryAddedListener(function(contact) {
688 if (acceptPhoneCallFromOtherWidget !== true) {
691 var tmpCallHistory = Phone.callHistory();
693 tmpContact.push(contact);
694 tmpContact = Phone.formatCallHistory(tmpContact);
695 tmpCallHistory.unshift(tmpContact[0]);
696 Phone.callHistory(tmpCallHistory);
698 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
702 /* add listener to change call state */
703 tizen.phone.addCallChangedListener(function(result) {
705 if ( !! result.contact.name) {
706 contact = result.contact;
710 /* jshint camelcase: false */
711 number: tizen.phone.activeCall.line_id
712 /* jshint camelcase: true */
718 console.log("result.state " + result.state);
720 switch (result.state.toLowerCase()) {
721 case "DISCONNECTED".toLowerCase():
723 disconnectCall(contact);
725 if (acceptPhoneCallFromOtherWidget === true) {
727 window.setTimeout(function() {
728 if (typeof tizen !== "undefined") {
729 tizen.application.getCurrentApplication().exit();
734 Configuration.set("acceptedCall", "false");
737 case "ACTIVE".toLowerCase():
738 if (Configuration._values.acceptedCall !== "true") {
740 self.incomingCall.acceptIncommingCall();
741 CallDuration.startWatch();
742 console.log("phone active");
743 Configuration.set("acceptedCall", "true");
746 case "DIALING".toLowerCase():
753 if (typeof(Speech) !== 'undefined') {
754 /* add listener to voice recognition */
755 Speech.addVoiceRecognitionListener({
757 if (ContactsLibrary.currentSelectedContact !== "" && $('#library').library("isVisible")) {
758 acceptCall(ContactsLibrary.currentSelectedContact);
763 console.warn("Speech API is not available.");
772 * Class which provides call contact carousel.
774 * @method callContactCarousel
775 * @param contact {Object} Contact object.
780 function callContactCarousel(contact) {
787 * Class which provides call by contact ID.
789 * @method callContactById
790 * @param contactId {String} Contact id.
793 function callContactById(contactId) {
795 $("#contactDetailMobile").addClass("fontColorSelected ");
796 if (contactId !== "" && typeof contactId !== undefined) {
798 var contactObject = Phone.getContactById(contactId);
799 if ( !! contactObject) {
800 window.setTimeout(function() {
801 acceptCall(contactObject);
802 $("#contactDetailMobile").removeClass("fontColorSelected ");
805 console.log("contact not found");
806 $("#contactDetailMobile").removeClass("fontColorSelected ");