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);
480 CallDuration.startWatch();
481 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
483 var callNumber = contact.phoneNumbers[0] && contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : "";
484 tizen.phone.invokeCall(callNumber, function(result) {
485 console.log(result.message);
490 } else if (callStatus === "ACTIVE".toLowerCase()) {
491 CallDuration.startWatch();
497 * Class which provides disconnect call.
499 * @method disconnectCall
502 function disconnectCall() {
504 $("#callButton").removeClass("callingTrue");
505 $("#callButton").addClass("callingFalse");
506 if (acceptPhoneCallFromOtherWidget !== true) {
507 $("#callBox").removeClass("callBoxShow");
508 $("#callBox").addClass("callBoxHidden");
509 $('#contactsCarouselBox').removeClass("contactsCarouselBoxHide");
510 $('#contactsCarouselBox').addClass("contactsCarouselBoxShow");
512 CallDuration.resetIt();
514 tizen.phone.hangupCall(function(result) {
515 console.log(result.message);
518 $("#inputPhoneNumber").val('');
524 setTimeout(function() {
525 /* initialize phone widget by remote device status */
527 tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDevice){
528 if (selectedRemoteDevice !== "") {
529 $("#noPairedDevice").hide();
530 $("#loadingHistorySpinnerWrapper").show();
532 $("#noPairedDevice").show();
535 /* initialize phone widget by active call status */
536 var activeCall = tizen.phone.activeCall();
537 var callStatus = activeCall.state.toLowerCase();
538 if (callStatus === "INCOMING".toLowerCase() || callStatus === "DIALING".toLowerCase() || callStatus === "ACTIVE".toLowerCase()) {
540 if (tizen.phone.callState) {
541 contact = activeCall.contact;
543 acceptPhoneCallFromOtherWidget = true;
545 } else if (callStatus === "DISCONNECTED".toLowerCase()) {
549 /* start keyboard timer */
550 keyboard.startTimer();
551 /* initialize bootstrap */
552 bootstrap = new Bootstrap(function(status) {
553 telInput = $("#inputPhoneNumber");
554 $("#clockElement").ClockPlugin('init', 5);
555 $("#clockElement").ClockPlugin('startTimer');
556 $("#topBarIcons").topBarIconsPlugin('init', 'phone');
557 $('#bottomPanel').bottomPanel('init');
558 if (!callHistoryCarousel) {
560 callHistoryCarousel = new Carousel();
563 if (typeof Phone !== "undefined") {
564 /* add listener to selected remote device */
565 tizen.phone.addRemoteDeviceSelectedListener(function(returnID) {
566 if ((!!returnID && !!returnID.error) || (!!returnID && !!returnID.value && returnID.value === "")) {
567 $("#loadingHistorySpinnerWrapper").hide();
568 $(".caroufredsel_wrapper").hide();
569 $("#noPairedDevice").show();
571 $("#noPairedDevice").hide();
572 $(".caroufredsel_wrapper").show();
575 /* initialize contacts and call history, if not accept phone call from another widget */
576 if (acceptPhoneCallFromOtherWidget !== true) {
577 window.setTimeout(function() {
578 Phone.loadContacts(function(err) {
580 ContactsLibrary.init();
581 $("#loadingHistorySpinnerWrapper").show();
582 Phone.loadCallHistory(function(err) {
583 $("#loadingHistorySpinnerWrapper").hide();
585 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
593 /* add listener to change contacts list */
594 tizen.phone.addContactsChangedListener(function() {
595 if (acceptPhoneCallFromOtherWidget !== true) {
596 window.setTimeout(function() {
597 Phone.loadContacts(function(err) {
599 ContactsLibrary.init();
605 /* add listener to change call history */
606 tizen.phone.addCallHistoryChangedListener(function() {
607 $("#loadingHistorySpinnerWrapper").show();
608 if (acceptPhoneCallFromOtherWidget !== true) {
609 window.setTimeout(function() {
610 Phone.loadCallHistory(function(err) {
611 $("#loadingHistorySpinnerWrapper").hide();
613 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
623 $("#contactsLibraryButton").bind('click', function() {
625 ContactsLibrary.show();
629 $(".numbersBox").delegate("#numberButton", "click", function() {
630 var pressTime = new Date(),
631 number, oneCharPX = 32;
632 if (keyboard.intervalExpired(pressTime)) {
633 number = telInput.attr("value") + $(this).data("id");
634 telInput.attr("value", number);
635 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
636 keyboard.pressedKey = $(this).data("id").toString();
639 if (keyboard.pressedKey === "-1" || keyboard.pressedKey !== $(this).data("id").toString()) {
640 number = telInput.attr("value") + $(this).data("id");
641 telInput.attr("value", number);
642 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
643 keyboard.pressedKey = $(this).data("id").toString();
645 var phoneNumText = telInput.attr("value");
646 if (keyboard.pressedKey === $(this).data("id").toString() && keyboard.selectedInput !== null && keyboard.selectedInput.values.length === 1) {
647 number = telInput.attr("value") + $(this).data("id");
648 telInput.attr("value", number);
649 $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX);
651 var numToUpdate = phoneNumText.slice(0, phoneNumText.length - 1);
652 numToUpdate += keyboard.nextKey();
653 telInput.attr("value", numToUpdate);
658 keyboard.selectInput();
659 keyboard.startTimer();
663 $(".inputPhoneNumberBox").delegate("#deleteButton", "click", function() {
664 var number = telInput.attr("value");
665 number = number.slice(0, number.length - 1);
666 telInput.attr("value", number);
670 $('#callButton').bind('click', function() {
671 var phoneNumber = $("#inputPhoneNumber").val();
672 if ($("#callBox").hasClass("callBoxShow")) {
674 } else if (phoneNumber !== "") {
675 var contact = Phone.getContactByPhoneNumber(phoneNumber);
676 if (contact === null) {
688 $('.muteButton').bind('click', function() {
692 /* add listener to change call history entry, because if call is ended tizen.phone give back only last history object */
693 tizen.phone.addCallHistoryEntryAddedListener(function(contact) {
694 if (acceptPhoneCallFromOtherWidget !== true) {
697 var tmpCallHistory = Phone.callHistory();
699 tmpContact.push(contact);
700 tmpContact = Phone.formatCallHistory(tmpContact);
701 tmpCallHistory.unshift(tmpContact[0]);
702 Phone.callHistory(tmpCallHistory);
704 callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0);
708 /* add listener to change call state */
709 tizen.phone.addCallChangedListener(function(result) {
711 var activeCall = tizen.phone.activeCall();
712 if ( !! result.contact.name) {
713 contact = result.contact;
717 /* jshint camelcase: false */
718 number: activeCall.line_id
719 /* jshint camelcase: true */
725 console.log("result.state " + result.state);
727 switch (result.state.toLowerCase()) {
728 case "DISCONNECTED".toLowerCase():
730 disconnectCall(contact);
732 if (acceptPhoneCallFromOtherWidget === true) {
734 window.setTimeout(function() {
735 if (typeof (tizen.application.getCurrentApplication) !== "undefined") {
736 tizen.application.getCurrentApplication().exit();
741 Configuration.set("acceptedCall", "false");
744 case "ACTIVE".toLowerCase():
745 if (Configuration._values.acceptedCall !== "true") {
747 self.incomingCall.acceptIncommingCall();
748 CallDuration.startWatch();
749 console.log("phone active");
750 Configuration.set("acceptedCall", "true");
753 case "DIALING".toLowerCase():
760 if (typeof(Speech) !== 'undefined') {
761 /* add listener to voice recognition */
762 Speech.addVoiceRecognitionListener({
764 if (ContactsLibrary.currentSelectedContact !== "" && $('#library').library("isVisible")) {
765 acceptCall(ContactsLibrary.currentSelectedContact);
770 console.warn("Speech API is not available.");
779 * Class which provides call contact carousel.
781 * @method callContactCarousel
782 * @param contact {Object} Contact object.
787 function callContactCarousel(contact) {
794 * Class which provides call by contact ID.
796 * @method callContactById
797 * @param contactId {String} Contact id.
800 function callContactById(contactId) {
802 $("#contactDetailMobile").addClass("fontColorSelected ");
803 if (contactId !== "" && typeof contactId !== undefined) {
805 var contactObject = Phone.getContactById(contactId);
806 if ( !! contactObject) {
807 window.setTimeout(function() {
808 acceptCall(contactObject);
809 $("#contactDetailMobile").removeClass("fontColorSelected ");
812 console.log("contact not found");
813 $("#contactDetailMobile").removeClass("fontColorSelected ");