Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
597
gaia/apps/callscreen/js/call_screen.js
Executable file
597
gaia/apps/callscreen/js/call_screen.js
Executable file
|
|
@ -0,0 +1,597 @@
|
|||
/* globals CallsHandler, FontSizeManager, KeypadManager,
|
||||
LockScreenSlide, MozActivity, SettingsListener, Utils, mozIntl */
|
||||
/* jshint nonew: false */
|
||||
|
||||
'use strict';
|
||||
|
||||
var CallScreen = {
|
||||
_ticker: null,
|
||||
_screenWakeLock: null,
|
||||
_typedNumber: '',
|
||||
callEndPromptTime: 2000,
|
||||
|
||||
body: document.body,
|
||||
screen: document.getElementById('call-screen'),
|
||||
lockscreenConnStates: document.getElementById('lockscreen-conn-states'),
|
||||
views: document.getElementById('views'),
|
||||
|
||||
calls: document.getElementById('calls'),
|
||||
|
||||
mainContainer: document.getElementById('main-container'),
|
||||
contactBackground: document.getElementById('contact-background'),
|
||||
callOptions: document.getElementById('call-options'),
|
||||
callToolbar: document.getElementById('co-advanced'),
|
||||
|
||||
muteButton: document.getElementById('mute'),
|
||||
speakerButton: document.getElementById('speaker'),
|
||||
bluetoothButton: document.getElementById('bt'),
|
||||
keypadButton: document.getElementById('keypad-visibility'),
|
||||
placeNewCallButton: document.getElementById('place-new-call'),
|
||||
holdButton: document.getElementById('on-hold'),
|
||||
mergeButton: document.getElementById('merge'),
|
||||
holdAndMergeContainer: document.getElementById('hold-and-merge-container'),
|
||||
|
||||
hideBarMuteButton: document.getElementById('keypad-hidebar-mute-action'),
|
||||
|
||||
bluetoothMenu: document.getElementById('bluetooth-menu'),
|
||||
switchToDeviceButton: document.getElementById('btmenu-btdevice'),
|
||||
switchToReceiverButton: document.getElementById('btmenu-receiver'),
|
||||
switchToSpeakerButton: document.getElementById('btmenu-speaker'),
|
||||
bluetoothMenuCancel: document.getElementById('btmenu-cancel'),
|
||||
|
||||
answerButton: document.getElementById('callbar-answer'),
|
||||
rejectButton: document.getElementById('callbar-hang-up'),
|
||||
|
||||
incomingContainer: document.getElementById('incoming-container'),
|
||||
incomingInfo: document.getElementById('incoming-info'),
|
||||
incomingNumber: document.getElementById('incoming-number'),
|
||||
incomingSim: document.getElementById('incoming-sim'),
|
||||
incomingNumberAdditionalTel:
|
||||
document.getElementById('incoming-number-additional-info-tel'),
|
||||
incomingNumberAdditionalTelType:
|
||||
document.getElementById('incoming-number-additional-info-tel-type'),
|
||||
incomingAnswer: document.getElementById('incoming-answer'),
|
||||
incomingEnd: document.getElementById('incoming-end'),
|
||||
incomingIgnore: document.getElementById('incoming-ignore'),
|
||||
lockedClockTime: document.getElementById('lockscreen-clock-time'),
|
||||
lockedDate: document.getElementById('lockscreen-date'),
|
||||
|
||||
statusMessage: document.getElementById('statusMsg'),
|
||||
showStatusMessage: function cs_showStatusMessage(message) {
|
||||
var STATUS_TIME = 2000;
|
||||
var paragraph = this.statusMessage.querySelector('p');
|
||||
var self = this;
|
||||
|
||||
if (typeof(message) === 'string') {
|
||||
navigator.mozL10n.setAttributes(paragraph, message);
|
||||
} else if (message.id) {
|
||||
navigator.mozL10n.setAttributes(paragraph, message.id, message.args);
|
||||
}
|
||||
|
||||
this.statusMessage.classList.add('visible');
|
||||
this.statusMessage.addEventListener('transitionend', function tend(evt) {
|
||||
evt.stopPropagation();
|
||||
self.statusMessage.removeEventListener('transitionend', tend);
|
||||
setTimeout(function hide() {
|
||||
self.statusMessage.classList.remove('visible');
|
||||
}, STATUS_TIME);
|
||||
});
|
||||
},
|
||||
|
||||
updateCallsDisplay: function cs_updateCallsDisplay() {
|
||||
var visibleCalls =
|
||||
this.calls.querySelectorAll('section:not([hidden])').length;
|
||||
this.body.classList.toggle('single-line', visibleCalls <= 1);
|
||||
this.calls.classList.toggle('big-duration', visibleCalls <= 1);
|
||||
CallsHandler.updateAllPhoneNumberDisplays();
|
||||
},
|
||||
|
||||
hidePlaceNewCallButton: function cs_hidePlaceNewCallButton() {
|
||||
this.callToolbar.classList.add('no-add-call');
|
||||
},
|
||||
|
||||
showPlaceNewCallButton: function cs_showPlaceNewCallButton() {
|
||||
this.callToolbar.classList.remove('no-add-call');
|
||||
},
|
||||
|
||||
/**
|
||||
* When enabled hides the end-and-answer button in call waiting mode and
|
||||
* displays only the hold-and-answer one.
|
||||
*
|
||||
* @param {Boolean} enabled Enables hold-and-answer-only operation.
|
||||
*/
|
||||
set holdAndAnswerOnly(enabled) {
|
||||
this.incomingContainer.classList.toggle('hold-and-answer-only', enabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* When enabled displays the CDMA-specific call-waiting UI
|
||||
*
|
||||
* @param {Boolean} enabled Enables the CDMA call-waiting UI.
|
||||
*/
|
||||
set cdmaCallWaiting(enabled) {
|
||||
this.calls.classList.toggle('switch', enabled);
|
||||
this.callToolbar.classList.toggle('no-add-call', enabled);
|
||||
},
|
||||
|
||||
get inStatusBarMode() {
|
||||
return window.innerHeight <= 40;
|
||||
},
|
||||
|
||||
init: function cs_init() {
|
||||
this.muteButton.addEventListener('click', this.toggleMute.bind(this));
|
||||
this.hideBarMuteButton.addEventListener('click',
|
||||
this.toggleMute.bind(this));
|
||||
this.keypadButton.addEventListener('click', this.showKeypad.bind(this));
|
||||
this.placeNewCallButton.addEventListener('click',
|
||||
this.placeNewCall.bind(this));
|
||||
this.speakerButton.addEventListener('click',
|
||||
this.toggleSpeaker.bind(this));
|
||||
this.bluetoothButton.addEventListener('click',
|
||||
this.toggleBluetoothMenu.bind(this));
|
||||
this.holdButton.addEventListener('click',
|
||||
CallsHandler.holdOrResumeSingleCall.bind(this));
|
||||
this.mergeButton.addEventListener('click',
|
||||
CallsHandler.mergeCalls.bind(this));
|
||||
this.answerButton.addEventListener('click',
|
||||
CallsHandler.answer);
|
||||
this.rejectButton.addEventListener('click',
|
||||
CallsHandler.end);
|
||||
|
||||
this.switchToDeviceButton.addEventListener(
|
||||
'click', this.switchToDefaultOut.bind(this, false));
|
||||
this.switchToReceiverButton.addEventListener(
|
||||
'click', this.switchToReceiver.bind(this));
|
||||
this.switchToSpeakerButton.addEventListener(
|
||||
'click', this.switchToSpeaker.bind(this));
|
||||
this.bluetoothMenuCancel.addEventListener(
|
||||
'click', this.toggleBluetoothMenu.bind(this));
|
||||
|
||||
this.incomingAnswer.addEventListener('click',
|
||||
CallsHandler.holdAndAnswer);
|
||||
this.incomingEnd.addEventListener('click',
|
||||
CallsHandler.endAndAnswer);
|
||||
this.incomingIgnore.addEventListener('click',
|
||||
CallsHandler.ignore);
|
||||
|
||||
this.calls.addEventListener('click', CallsHandler.toggleCalls.bind(this));
|
||||
|
||||
window.addEventListener('resize', this.resizeHandler.bind(this));
|
||||
|
||||
SettingsListener.observe('wallpaper.image', null,
|
||||
this._wallpaperImageHandler.bind(this));
|
||||
|
||||
// Interaction with the lockscreen
|
||||
this._lockScreenLayoutInitialized = false;
|
||||
navigator.mozSettings.addObserver(
|
||||
'lockscreen.locked', this._lockscreenStateChange.bind(this)
|
||||
);
|
||||
|
||||
this.syncSpeakerEnabled();
|
||||
},
|
||||
|
||||
initLockScreenConnInfoManager: function cs_initLockScreenConnInfoManager() {
|
||||
/* mobile connection state on lock screen */
|
||||
if (window.navigator.mozMobileConnections) {
|
||||
new window.LockScreenConnInfoManager(CallScreen.lockscreenConnStates);
|
||||
}
|
||||
},
|
||||
|
||||
initLockScreenSlide: function cs_initLockScreenSlide() {
|
||||
// Setup incoming call screen slider
|
||||
this.hangUpIcon = document.getElementById('lockscreen-area-hangup');
|
||||
this.pickUpIcon = document.getElementById('lockscreen-area-pickup');
|
||||
this.initUnlockerEvents();
|
||||
new LockScreenSlide({
|
||||
useNewStyle: true,
|
||||
|
||||
IDs: {
|
||||
overlay: 'main-container',
|
||||
areas: {
|
||||
left: 'lockscreen-area-hangup',
|
||||
right: 'lockscreen-area-pickup'
|
||||
},
|
||||
},
|
||||
|
||||
trackNew: {
|
||||
strokeColorTop: 'rgba(0, 0, 0, 0)',
|
||||
strokeColorBottom: 'rgba(0, 0, 0, 0)',
|
||||
fillColorTop: 'rgba(0, 0, 0, 0.1)',
|
||||
fillColorBottom: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
|
||||
colors: {
|
||||
left: {
|
||||
touchedColor: '224, 0, 0',
|
||||
touchedColorStop: '255, 255, 255'
|
||||
},
|
||||
right: {
|
||||
touchedColor: '0, 173, 173',
|
||||
touchedColorStop: '255, 255, 255'
|
||||
}
|
||||
},
|
||||
|
||||
iconBG: {
|
||||
left: {
|
||||
color: 'rgba(224, 0, 0, 0.80)'
|
||||
},
|
||||
right: {
|
||||
color: 'rgba(0, 173, 173, 0.80)'
|
||||
}
|
||||
},
|
||||
|
||||
resourcesNew: {
|
||||
larrow: '/style/images/lock_screen/lockscreen_toggle_arrow_left.png',
|
||||
rarrow: '/style/images/lock_screen/lockscreen_toggle_arrow_right.png'
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_lockscreenStateChange: function cs_lockscreenStateChange(event) {
|
||||
if (event.settingValue) {
|
||||
if (this.screen.dataset.layout === 'incoming') {
|
||||
this.initLockScreenLayout();
|
||||
this.render('incoming-locked');
|
||||
}
|
||||
|
||||
this.initUnlockerEvents();
|
||||
} else {
|
||||
if (this.screen.dataset.layout === 'incoming-locked') {
|
||||
this.render('incoming');
|
||||
}
|
||||
|
||||
this.suspendUnlockerEvents();
|
||||
}
|
||||
},
|
||||
|
||||
_wallpaperImageHandler: function cs_wallpaperImageHandler(image) {
|
||||
this.mainContainer.style.backgroundImage = 'url(' +
|
||||
(typeof image === 'string' ? image : URL.createObjectURL(image)) + ')';
|
||||
},
|
||||
|
||||
setCallerContactImage: function cs_setCallerContactImage() {
|
||||
var activeCallForContactImage = CallsHandler.activeCallForContactImage;
|
||||
var blob = activeCallForContactImage && activeCallForContactImage.photo;
|
||||
|
||||
var background = blob ? 'url(' + URL.createObjectURL(blob) + ')' : '';
|
||||
this.contactBackground.style.backgroundImage = background;
|
||||
},
|
||||
|
||||
insertCall: function cs_insertCall(node) {
|
||||
this.calls.appendChild(node);
|
||||
this.updateCallsDisplay();
|
||||
},
|
||||
|
||||
removeCall: function cs_removeCall(node) {
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
this.updateCallsDisplay();
|
||||
}
|
||||
},
|
||||
|
||||
resizeHandler: function cs_resizeHandler() {
|
||||
// If a user has the keypad opened, we want to display the number called
|
||||
// while in status bar mode. And restore the digits typed when exiting.
|
||||
if (!this.body.classList.contains('showKeypad')) {
|
||||
this.updateCallsDisplay(this.inStatusBarMode);
|
||||
} else if (this.inStatusBarMode) {
|
||||
this._typedNumber = KeypadManager._phoneNumber;
|
||||
KeypadManager.restorePhoneNumber();
|
||||
} else {
|
||||
KeypadManager.updatePhoneNumber(this._typedNumber, 'begin', true);
|
||||
}
|
||||
},
|
||||
|
||||
_lockScreenLayoutInitialized: false,
|
||||
initLockScreenLayout: function cs_initLockScreenLayout() {
|
||||
this.showClock(new Date());
|
||||
|
||||
if (!this._lockScreenLayoutInitialized) {
|
||||
this.initLockScreenConnInfoManager();
|
||||
this.initLockScreenSlide();
|
||||
this._lockScreenLayoutInitialized = true;
|
||||
}
|
||||
},
|
||||
|
||||
toggleMute: function cs_toggleMute() {
|
||||
this.muteButton.classList.toggle('active-state');
|
||||
this.hideBarMuteButton.classList.toggle('active-state');
|
||||
this.calls.classList.toggle('muted');
|
||||
CallsHandler.toggleMute();
|
||||
},
|
||||
|
||||
unmute: function cs_unmute() {
|
||||
this.muteButton.classList.remove('active-state');
|
||||
this.hideBarMuteButton.classList.remove('active-state');
|
||||
this.calls.classList.remove('muted');
|
||||
CallsHandler.unmute();
|
||||
},
|
||||
|
||||
toggleSpeaker: function cs_toggleSpeaker() {
|
||||
this.speakerButton.classList.toggle('active-state');
|
||||
CallsHandler.toggleSpeaker();
|
||||
},
|
||||
|
||||
toggleBluetoothMenu: function cs_toggleBluetoothMenu(value) {
|
||||
if (typeof value != 'boolean') {
|
||||
this.bluetoothMenu.classList.toggle('display');
|
||||
} else {
|
||||
this.bluetoothMenu.classList.toggle('display', value);
|
||||
}
|
||||
},
|
||||
|
||||
switchToSpeaker: function cs_switchToReceiver() {
|
||||
this.speakerButton.classList.add('active-state');
|
||||
this.bluetoothButton.classList.add('active-state');
|
||||
CallsHandler.switchToSpeaker();
|
||||
this.toggleBluetoothMenu(false);
|
||||
},
|
||||
|
||||
switchToReceiver: function cs_switchToReceiver() {
|
||||
this.speakerButton.classList.remove('active-state');
|
||||
this.bluetoothButton.classList.remove('active-state');
|
||||
CallsHandler.switchToReceiver();
|
||||
this.toggleBluetoothMenu(false);
|
||||
},
|
||||
|
||||
setShowIsHeld: function cs_setShowIsHeld(enabled) {
|
||||
this.holdButton.classList.toggle('active-state', enabled);
|
||||
},
|
||||
|
||||
// when BT device available: switch to BT
|
||||
// when BT device unavailable: switch to receiver
|
||||
switchToDefaultOut: function cs_switchToDefaultOut(doNotConnect) {
|
||||
this.speakerButton.classList.remove('active-state');
|
||||
this.bluetoothButton.classList.add('active-state');
|
||||
CallsHandler.switchToDefaultOut(doNotConnect);
|
||||
this.toggleBluetoothMenu(false);
|
||||
},
|
||||
|
||||
setBTReceiverIcon: function cs_setBTReceiverIcon(enabled) {
|
||||
if (enabled) {
|
||||
this.speakerButton.classList.add('hide');
|
||||
this.bluetoothButton.classList.remove('hide');
|
||||
} else {
|
||||
this.speakerButton.classList.remove('hide');
|
||||
this.bluetoothButton.classList.add('hide');
|
||||
}
|
||||
},
|
||||
|
||||
showKeypad: function cs_showKeypad() {
|
||||
KeypadManager.render('oncall');
|
||||
this.body.classList.add('showKeypad');
|
||||
},
|
||||
|
||||
hideKeypad: function cs_hideKeypad() {
|
||||
KeypadManager.restorePhoneNumber();
|
||||
KeypadManager.restoreAdditionalContactInfo();
|
||||
this.body.classList.remove('showKeypad');
|
||||
},
|
||||
|
||||
placeNewCall: function cs_placeNewCall() {
|
||||
new MozActivity({
|
||||
name: 'dial',
|
||||
data: {
|
||||
type: 'webtelephony/number',
|
||||
number: ''
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function cs_render(layout_type) {
|
||||
this.screen.dataset.layout = layout_type;
|
||||
},
|
||||
|
||||
showClock: function cs_showClock(now) {
|
||||
var formatter = mozIntl.DateTimeFormat(navigator.languages, {
|
||||
hour12: navigator.mozHour12,
|
||||
dayperiod: false,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
});
|
||||
|
||||
var timeText = formatter.format(now);
|
||||
var dateText = now.toLocaleString(navigator.languages, {
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
|
||||
this.lockedClockTime.textContent = timeText;
|
||||
this.lockedDate.textContent = dateText;
|
||||
},
|
||||
|
||||
showIncoming: function cs_showIncoming() {
|
||||
this.body.classList.remove('showKeypad');
|
||||
|
||||
this.callOptions.classList.add('transparent');
|
||||
this.incomingContainer.classList.add('displayed');
|
||||
|
||||
this._screenWakeLock = navigator.requestWakeLock('screen');
|
||||
},
|
||||
|
||||
hideIncoming: function cs_hideIncoming() {
|
||||
this.callOptions.classList.remove('transparent');
|
||||
this.incomingContainer.classList.remove('displayed');
|
||||
|
||||
if (this._screenWakeLock) {
|
||||
this._screenWakeLock.unlock();
|
||||
this._screenWakeLock = null;
|
||||
}
|
||||
|
||||
this.setCallerContactImage();
|
||||
},
|
||||
|
||||
enableKeypadButton: function cs_enableKeypadButton() {
|
||||
this.keypadButton.removeAttribute('disabled');
|
||||
},
|
||||
|
||||
disableKeypadButton: function cs_disableKeypadButton() {
|
||||
this.keypadButton.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
|
||||
syncSpeakerEnabled: function cs_syncSpeakerEnabled() {
|
||||
if (navigator.mozTelephony.speakerEnabled) {
|
||||
this.speakerButton.classList.add('active-state');
|
||||
} else {
|
||||
this.speakerButton.classList.remove('active-state');
|
||||
}
|
||||
},
|
||||
|
||||
enableMuteButton: function cs_enableMuteButton() {
|
||||
this.muteButton.removeAttribute('disabled');
|
||||
},
|
||||
|
||||
disableMuteButton: function cs_disableMuteButton() {
|
||||
this.muteButton.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
|
||||
enablePlaceNewCallButton: function cs_enablePlaceNewCallButton() {
|
||||
this.placeNewCallButton.removeAttribute('disabled');
|
||||
},
|
||||
|
||||
disablePlaceNewCallButton: function cs_disablePlaceNewCallButton() {
|
||||
this.placeNewCallButton.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
|
||||
enableSpeakerButton: function cs_enableSpeakerButton() {
|
||||
this.speakerButton.removeAttribute('disabled');
|
||||
},
|
||||
|
||||
disableSpeakerButton: function cs_disableSpeakerButton() {
|
||||
this.speakerButton.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
|
||||
showOnHoldButton: function cs_showOnHoldButton() {
|
||||
this.holdButton.classList.remove('hide');
|
||||
},
|
||||
|
||||
hideOnHoldButton: function cs_hideOnHoldButton() {
|
||||
this.holdButton.classList.add('hide');
|
||||
},
|
||||
|
||||
enableOnHoldButton: function cs_enableOnHoldButton() {
|
||||
this.holdButton.removeAttribute('disabled');
|
||||
},
|
||||
|
||||
disableOnHoldButton: function cs_disableOnHoldButton() {
|
||||
this.holdButton.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
|
||||
showMergeButton: function cs_showMergeButton() {
|
||||
this.mergeButton.classList.remove('hide');
|
||||
},
|
||||
|
||||
hideMergeButton: function cs_hideMergeButton() {
|
||||
this.mergeButton.classList.add('hide');
|
||||
},
|
||||
|
||||
showOnHoldAndMergeContainer: function cs_showOnHoldAndMergeContainer() {
|
||||
this.holdAndMergeContainer.style.display = 'block';
|
||||
},
|
||||
|
||||
hideOnHoldAndMergeContainer: function cs_hideOnHoldAndMergeContainer() {
|
||||
this.holdAndMergeContainer.style.display = 'none';
|
||||
},
|
||||
|
||||
createTicker: function(durationNode) {
|
||||
var durationChildNode = durationNode.querySelector('span');
|
||||
|
||||
if (durationNode.dataset.tickerId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
durationChildNode.removeAttribute('data-l10n-id');
|
||||
durationChildNode.textContent = '00:00';
|
||||
durationNode.classList.add('isTimer');
|
||||
|
||||
var ticker = setInterval(function ut_updateTimer(startTime) {
|
||||
// Bug 834334: Ensure that 28.999 -> 29.000
|
||||
var delta = Math.round((Date.now() - startTime) / 1000) * 1000;
|
||||
Utils.prettyDuration(durationChildNode, delta);
|
||||
}, 1000, Date.now());
|
||||
|
||||
durationNode.dataset.tickerId = ticker;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
stopTicker: function(durationNode) {
|
||||
durationNode.classList.remove('isTimer');
|
||||
clearInterval(durationNode.dataset.tickerId);
|
||||
delete durationNode.dataset.tickerId;
|
||||
},
|
||||
|
||||
handleEvent: function cs_handleEvent(evt) {
|
||||
var state = null, statePrev = null;
|
||||
switch (evt.type) {
|
||||
case 'lockscreenslide-unlocker-initializer':
|
||||
break;
|
||||
case 'lockscreenslide-near-left':
|
||||
state = evt.detail.state;
|
||||
statePrev = evt.detail.statePrev;
|
||||
if (state === 'accelerating') {
|
||||
CallScreen.hangUpIcon.classList.add('triggered');
|
||||
} else {
|
||||
CallScreen.hangUpIcon.classList.remove('triggered');
|
||||
}
|
||||
break;
|
||||
case 'lockscreenslide-near-right':
|
||||
state = evt.detail.state;
|
||||
statePrev = evt.detail.statePrev;
|
||||
if (state === 'accelerating') {
|
||||
CallScreen.pickUpIcon.classList.add('triggered');
|
||||
} else {
|
||||
CallScreen.pickUpIcon.classList.remove('triggered');
|
||||
}
|
||||
break;
|
||||
case 'lockscreenslide-unlocking-start':
|
||||
break;
|
||||
case 'lockscreenslide-unlocking-stop':
|
||||
break;
|
||||
case 'lockscreenslide-activate-left':
|
||||
CallsHandler.end();
|
||||
break;
|
||||
case 'lockscreenslide-activate-right':
|
||||
CallsHandler.answer();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
cdmaConferenceCall: function cs_cdmaConferenceCall() {
|
||||
this.hidePlaceNewCallButton();
|
||||
this.calls.classList.add('cdma-conference-call');
|
||||
},
|
||||
|
||||
initUnlockerEvents: function cs_initUnlockerEvents() {
|
||||
window.addEventListener('lockscreenslide-unlocker-initializer', this);
|
||||
window.addEventListener('lockscreenslide-near-left', this);
|
||||
window.addEventListener('lockscreenslide-near-right', this);
|
||||
window.addEventListener('lockscreenslide-unlocking-start', this);
|
||||
window.addEventListener('lockscreenslide-activate-left', this);
|
||||
window.addEventListener('lockscreenslide-activate-right', this);
|
||||
window.addEventListener('lockscreenslide-unlocking-stop', this);
|
||||
},
|
||||
|
||||
suspendUnlockerEvents: function cs_initUnlockerEvents() {
|
||||
window.removeEventListener('lockscreenslide-unlocker-initializer', this);
|
||||
window.removeEventListener('lockscreenslide-near-left', this);
|
||||
window.removeEventListener('lockscreenslide-near-right', this);
|
||||
window.removeEventListener('lockscreenslide-unlocking-start', this);
|
||||
window.removeEventListener('lockscreenslide-activate-left', this);
|
||||
window.removeEventListener('lockscreenslide-activate-right', this);
|
||||
window.removeEventListener('lockscreenslide-unlocking-stop', this);
|
||||
},
|
||||
|
||||
getScenario: function cs_getScenario() {
|
||||
var scenario;
|
||||
if (this.inStatusBarMode) {
|
||||
scenario = FontSizeManager.STATUS_BAR;
|
||||
} else if (this.body.classList.contains('single-line')) {
|
||||
scenario = FontSizeManager.SINGLE_CALL;
|
||||
} else {
|
||||
scenario = FontSizeManager.CALL_WAITING;
|
||||
}
|
||||
return scenario;
|
||||
}
|
||||
};
|
||||
974
gaia/apps/callscreen/js/calls_handler.js
Executable file
974
gaia/apps/callscreen/js/calls_handler.js
Executable file
|
|
@ -0,0 +1,974 @@
|
|||
/* globals BluetoothHelper, CallScreen,
|
||||
ConferenceGroupHandler, Contacts, HandledCall, KeypadManager,
|
||||
SimplePhoneMatcher, TonePlayer, Utils */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* globals BluetoothHelper, CallScreen, Contacts, FontSizeManager, HandledCall,
|
||||
KeypadManager, SimplePhoneMatcher, TonePlayer, Utils */
|
||||
|
||||
var CallsHandler = (function callsHandler() {
|
||||
// Changing this will probably require markup changes
|
||||
var CALLS_LIMIT = 2;
|
||||
var CDMA_CALLS_LIMIT = 2;
|
||||
|
||||
var handledCalls = [];
|
||||
var exitCallScreenTimeout = null;
|
||||
|
||||
var toneInterval = null; // Timer used to play the waiting tone
|
||||
var telephony = window.navigator.mozTelephony;
|
||||
telephony.oncallschanged = onCallsChanged;
|
||||
|
||||
// Setting up the SimplePhoneMatcher
|
||||
var conn = window.navigator.mozMobileConnections &&
|
||||
window.navigator.mozMobileConnections[0];
|
||||
|
||||
if (conn && conn.voice && conn.voice.network && conn.voice.network.mcc) {
|
||||
SimplePhoneMatcher.mcc = conn.voice.network.mcc;
|
||||
}
|
||||
|
||||
var btHelper = new BluetoothHelper();
|
||||
|
||||
var screenLock;
|
||||
|
||||
/* === Setup === */
|
||||
function setup() {
|
||||
if (telephony) {
|
||||
// Somehow the muted property appears to true after initialization.
|
||||
// Set it to false.
|
||||
telephony.muted = false;
|
||||
// Once bug 1129882 and bug 1113086 are landed, the new audio channel
|
||||
// service will manage all the channels from System app, and to call
|
||||
// this api is let the audio channel service know the Callscreen app
|
||||
// is using the telephony audio channel.
|
||||
if (telephony.ownAudioChannel) {
|
||||
telephony.ownAudioChannel();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Use BTManager.isConnected() through btHelper
|
||||
// once bug 929376 is finished.
|
||||
btHelper.getConnectedDevicesByProfile(btHelper.profiles.HFP,
|
||||
function(result) {
|
||||
CallScreen.setBTReceiverIcon(!!(result && result.length));
|
||||
});
|
||||
|
||||
btHelper.onhfpstatuschanged = function(evt) {
|
||||
CallScreen.setBTReceiverIcon(evt.status);
|
||||
};
|
||||
|
||||
var acm = navigator.mozAudioChannelManager;
|
||||
if (acm) {
|
||||
acm.addEventListener('headphoneschange', function onheadphoneschange() {
|
||||
if (acm.headphones) {
|
||||
CallScreen.switchToDefaultOut();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
btHelper.onscostatuschanged = function onscostatuschanged(evt) {
|
||||
if (evt.status) {
|
||||
CallScreen.switchToDefaultOut();
|
||||
}
|
||||
};
|
||||
|
||||
navigator.mozSetMessageHandler('headset-button', handleHSCommand);
|
||||
navigator.mozSetMessageHandler('bluetooth-dialer-command', handleBTCommand);
|
||||
}
|
||||
|
||||
/* === Handled calls === */
|
||||
var highPriorityWakeLock = null;
|
||||
function onCallsChanged() {
|
||||
// Acquire or release the high-priority wake lock, as necessary. This
|
||||
// (mostly) prevents this process from being killed while we're on a call.
|
||||
if (!highPriorityWakeLock && telephony.calls.length > 0) {
|
||||
highPriorityWakeLock = navigator.requestWakeLock('high-priority');
|
||||
}
|
||||
if (highPriorityWakeLock && telephony.calls.length === 0) {
|
||||
highPriorityWakeLock.unlock();
|
||||
highPriorityWakeLock = null;
|
||||
}
|
||||
|
||||
// Make sure we play the busy tone when appropriate
|
||||
if (telephony.active) {
|
||||
telephony.active.addEventListener(
|
||||
'disconnected', handleDisconnectAndPlayBusyTone
|
||||
);
|
||||
}
|
||||
|
||||
// Adding any new calls to handledCalls
|
||||
telephony.calls.forEach(function callIterator(call) {
|
||||
var alreadyAdded = handledCalls.some(function hcIterator(hc) {
|
||||
return (hc.call === call);
|
||||
});
|
||||
|
||||
if (!alreadyAdded) {
|
||||
addCall(call);
|
||||
}
|
||||
});
|
||||
|
||||
// Removing any ended calls to handledCalls
|
||||
function hcIterator(call) {
|
||||
return (call === hc.call);
|
||||
}
|
||||
|
||||
for (var index = (handledCalls.length - 1); index >= 0; index--) {
|
||||
var hc = handledCalls[index];
|
||||
|
||||
var stillHere = telephony.calls.some(hcIterator) ||
|
||||
telephony.conferenceGroup.calls.some(hcIterator);
|
||||
|
||||
if (!stillHere) {
|
||||
removeCall(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (cdmaCallWaiting()) {
|
||||
handleCallWaiting(telephony.calls[0]);
|
||||
} else {
|
||||
if (isCdma3WayCall()) {
|
||||
CallScreen.hidePlaceNewCallButton();
|
||||
} else if (handledCalls.length !== 0) {
|
||||
CallScreen.showPlaceNewCallButton();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state of the hold/merge button depending on the calls' state
|
||||
updateMergeAndOnHoldStatus();
|
||||
|
||||
// Update mute and speaker buttons.
|
||||
updateMuteAndSpeakerStatus();
|
||||
|
||||
CallScreen.setCallerContactImage();
|
||||
exitCallScreenIfNoCalls(CallScreen.callEndPromptTime);
|
||||
}
|
||||
|
||||
function addCall(call) {
|
||||
// Once we already have 1 call, we need to care about incoming
|
||||
// calls and insert new dialing calls.
|
||||
if (handledCalls.length &&
|
||||
(call.state != 'incoming') && (call.state != 'dialing')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No more room
|
||||
if (telephony.calls.length > CALLS_LIMIT) {
|
||||
HandledCall(call);
|
||||
call.hangUp();
|
||||
return;
|
||||
}
|
||||
|
||||
// First incoming or outgoing call, reset mute and speaker.
|
||||
if (handledCalls.length === 0) {
|
||||
CallScreen.unmute();
|
||||
CallScreen.enableKeypadButton();
|
||||
|
||||
/**
|
||||
* Do not connect bluetooth SCO for first incoming/outgoing call.
|
||||
*
|
||||
* Bluetooth certification test requires SCO be connected after
|
||||
* user answers incoming call. Gecko bluetooth would connect SCO
|
||||
* automatically once 1) user answers incoming call or
|
||||
* 2) user dials outgoing call.
|
||||
*/
|
||||
CallScreen.switchToDefaultOut(true /* do not connect */);
|
||||
}
|
||||
|
||||
// Find an available node for displaying the call
|
||||
var hc = new HandledCall(call);
|
||||
handledCalls.push(hc);
|
||||
CallScreen.insertCall(hc.node);
|
||||
|
||||
if (call.state === 'incoming') {
|
||||
turnScreenOn(call);
|
||||
}
|
||||
|
||||
if (handledCalls.length > 1) {
|
||||
// New incoming call, signaling the user.
|
||||
if (call.state === 'incoming') {
|
||||
hc.hide();
|
||||
handleCallWaiting(call);
|
||||
|
||||
// User performed another outgoing call. show its status.
|
||||
} else {
|
||||
updatePlaceNewCall();
|
||||
hc.show();
|
||||
}
|
||||
} else {
|
||||
if (window.location.hash.startsWith('#locked') &&
|
||||
(call.state === 'incoming')) {
|
||||
CallScreen.initLockScreenLayout();
|
||||
CallScreen.render('incoming-locked');
|
||||
} else {
|
||||
CallScreen.render(call.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeCall(index) {
|
||||
handledCalls.splice(index, 1);
|
||||
|
||||
if (handledCalls.length === 0) {
|
||||
CallScreen.disableKeypadButton();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only hiding the incoming bar if we have another one to display.
|
||||
// Let handledCall catches disconnect event itself.
|
||||
CallScreen.hideIncoming();
|
||||
|
||||
var remainingCall = handledCalls[0];
|
||||
if (remainingCall.call.state === 'incoming') {
|
||||
// The active call ended, showing the incoming call
|
||||
remainingCall.show();
|
||||
|
||||
// This is the difference between an endAndAnswer() and
|
||||
// the active call being disconnected while a call is waiting
|
||||
setTimeout(function nextTick() {
|
||||
if (remainingCall.call.state === 'incoming') {
|
||||
CallScreen.render('incoming');
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function turnScreenOn(call) {
|
||||
screenLock = navigator.requestWakeLock('screen');
|
||||
|
||||
call.addEventListener('statechange', function callStateChange() {
|
||||
call.removeEventListener('statechange', callStateChange);
|
||||
|
||||
if (screenLock) {
|
||||
screenLock.unlock();
|
||||
screenLock = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the busy tone in response to a disconnected event with the
|
||||
* appropriate reason set. Once the tone has finished this will also
|
||||
* automatically close the callscreen if no more calls are present.
|
||||
*
|
||||
* @param evt {Object} The event delivered in the
|
||||
* TelephonyCall.ondisconnected event-handler.
|
||||
*/
|
||||
function handleDisconnectAndPlayBusyTone(evt) {
|
||||
if (evt.call.disconnectedReason === 'Busy') {
|
||||
// ANSI call waiting tone for a 3 seconds window.
|
||||
var sequence = [[480, 620, 500], [0, 0, 500],
|
||||
[480, 620, 500], [0, 0, 500],
|
||||
[480, 620, 500], [0, 0, 500]];
|
||||
var sequenceDuration = sequence.reduce(function(prev, curr) {
|
||||
return prev + curr[2];
|
||||
}, 0);
|
||||
|
||||
TonePlayer.playSequence(sequence);
|
||||
exitCallScreenIfNoCalls(sequenceDuration);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCallWaiting(call) {
|
||||
var number = call.secondId ? call.secondId.number : call.id.number;
|
||||
|
||||
if (!number) {
|
||||
navigator.mozL10n.setAttributes(CallScreen.incomingNumber,
|
||||
'withheld-number');
|
||||
// Force a synchronous translation so we can resize the string
|
||||
navigator.mozL10n.translateFragment(CallScreen.incomingNumber);
|
||||
FontSizeManager.adaptToSpace(FontSizeManager.SECOND_INCOMING_CALL,
|
||||
CallScreen.incomingNumber, false, 'end');
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigator.mozIccManager.iccIds.length > 1) {
|
||||
navigator.mozL10n.setAttributes(
|
||||
CallScreen.incomingSim,
|
||||
'sim-number',
|
||||
{ n: call.serviceId + 1 }
|
||||
);
|
||||
} else {
|
||||
CallScreen.incomingSim.hidden = true;
|
||||
}
|
||||
|
||||
Contacts.findByNumber(number,
|
||||
function lookupContact(contact, matchingTel) {
|
||||
if (contact && contact.name) {
|
||||
CallScreen.incomingInfo.classList.add('additionalInfo');
|
||||
CallScreen.incomingNumber.textContent = contact.name;
|
||||
|
||||
var additionalL10n =
|
||||
Utils.getPhoneNumberAdditionalInfo(matchingTel);
|
||||
navigator.mozL10n.setAttributes(
|
||||
CallScreen.incomingNumberAdditionalTelType,
|
||||
additionalL10n.id,
|
||||
additionalL10n.args);
|
||||
CallScreen.incomingNumberAdditionalTel.textContent = number;
|
||||
} else {
|
||||
CallScreen.incomingInfo.classList.remove('additionalInfo');
|
||||
CallScreen.incomingNumber.textContent = number;
|
||||
CallScreen.incomingNumberAdditionalTelType.removeAttribute(
|
||||
'data-l10n-id');
|
||||
CallScreen.incomingNumberAdditionalTelType.textContent = '';
|
||||
CallScreen.incomingNumberAdditionalTel.textContent = '';
|
||||
}
|
||||
|
||||
FontSizeManager.adaptToSpace(
|
||||
FontSizeManager.SECOND_INCOMING_CALL, CallScreen.incomingNumber,
|
||||
false, 'end');
|
||||
if (contact && contact.name) {
|
||||
FontSizeManager.ensureFixedBaseline(
|
||||
FontSizeManager.SECOND_INCOMING_CALL, CallScreen.incomingNumber);
|
||||
}
|
||||
});
|
||||
|
||||
if (cdmaCallWaiting()) {
|
||||
CallScreen.holdAndAnswerOnly = true;
|
||||
}
|
||||
|
||||
CallScreen.showIncoming();
|
||||
playWaitingTone(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks now and also in CallScreen.callEndPromptTime seconds if there
|
||||
* are no currently handled calls, and if not, exits the app. Resets
|
||||
* this timer on each successive invocation.
|
||||
*
|
||||
* @param timeout {Integer} A duration in ms after which the callscreen
|
||||
* should be closed.
|
||||
*/
|
||||
function exitCallScreenIfNoCalls(timeout) {
|
||||
if (handledCalls.length === 0) {
|
||||
// Prevent the user from doing anything while we're waiting for the exit
|
||||
// timer to fire. This prevents them from taking any actions that would
|
||||
// require there to be a handled call.
|
||||
document.body.classList.toggle('no-handled-calls', true);
|
||||
|
||||
if (exitCallScreenTimeout !== null) {
|
||||
clearTimeout(exitCallScreenTimeout);
|
||||
exitCallScreenTimeout = null;
|
||||
}
|
||||
exitCallScreenTimeout = setTimeout(function(evt) {
|
||||
exitCallScreenTimeout = null;
|
||||
if (handledCalls.length === 0) {
|
||||
window.close();
|
||||
} else {
|
||||
document.body.classList.toggle('no-handled-calls', false);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function updateAllPhoneNumberDisplays() {
|
||||
handledCalls.forEach(function(call) {
|
||||
if (!call._leftGroup) {
|
||||
call.restorePhoneNumber();
|
||||
}
|
||||
});
|
||||
}
|
||||
window.addEventListener('resize', updateAllPhoneNumberDisplays);
|
||||
|
||||
/**
|
||||
* Return the number of calls currently present in one state or another.
|
||||
* This includes all regular calls irrespective of their state plus a
|
||||
* conference group call if one is present.
|
||||
*
|
||||
* @returns {Integer} The number of calls currently present.
|
||||
*/
|
||||
function numOpenLines() {
|
||||
return telephony.calls.length +
|
||||
(telephony.conferenceGroup.calls.length ? 1 : 0);
|
||||
}
|
||||
|
||||
/* === Bluetooth Headset support ===*/
|
||||
function handleBTCommand(message) {
|
||||
var command = message.command;
|
||||
switch (command) {
|
||||
case 'CHUP':
|
||||
end();
|
||||
break;
|
||||
case 'ATA':
|
||||
answer();
|
||||
break;
|
||||
case 'CHLD=0':
|
||||
hangupWaitingCalls();
|
||||
break;
|
||||
case 'CHLD=1':
|
||||
// End the active call and answer the other one
|
||||
if ((handledCalls.length === 1) && !cdmaCallWaiting()) {
|
||||
end();
|
||||
} else {
|
||||
endAndAnswer();
|
||||
}
|
||||
break;
|
||||
case 'CHLD=2':
|
||||
// Hold the active call and answer the other one
|
||||
if ((handledCalls.length === 1) && !cdmaCallWaiting()) {
|
||||
holdOrResumeSingleCall();
|
||||
} else {
|
||||
holdAndAnswer();
|
||||
}
|
||||
break;
|
||||
case 'CHLD=3':
|
||||
mergeCalls();
|
||||
break;
|
||||
default:
|
||||
var partialCommand = command.substring(0, 3);
|
||||
if (partialCommand === 'VTS') {
|
||||
KeypadManager.press(command.substring(4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Headset command support */
|
||||
var lastHeadsetPress = 0;
|
||||
|
||||
function handleHSCommand(message) {
|
||||
/**
|
||||
* See bug 853132: plugging / unplugging some headphones might send a
|
||||
* 'headset-button-press' / 'headset-button-release' message
|
||||
* => if these two events happen in the same second, it's a click;
|
||||
* => if these two events are too distant, ignore them.
|
||||
*/
|
||||
switch (message) {
|
||||
case 'headset-button-press':
|
||||
lastHeadsetPress = Date.now();
|
||||
return;
|
||||
case 'headset-button-release':
|
||||
if ((Date.now() - lastHeadsetPress) > 1000) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (telephony.active) {
|
||||
end();
|
||||
} else if ((handledCalls.length > 1) || cdmaCallWaiting()) {
|
||||
holdAndAnswer();
|
||||
} else {
|
||||
answer();
|
||||
}
|
||||
}
|
||||
|
||||
/* === User Actions === */
|
||||
function answer() {
|
||||
// We should always have only 1 call here
|
||||
if (!handledCalls.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
handledCalls[0].call.answer();
|
||||
}
|
||||
|
||||
function holdAndAnswer() {
|
||||
if ((handledCalls.length < 2) && !cdmaCallWaiting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (telephony.active) {
|
||||
// connected, incoming
|
||||
telephony.active.hold(); // the incoming call is answered by gecko
|
||||
|
||||
// Check for CDMA mode before calling bluetooth CDMA-specific functions
|
||||
if (cdmaCallWaiting()) {
|
||||
btHelper.answerWaitingCall();
|
||||
}
|
||||
} else if (handledCalls.length >= 2) {
|
||||
// held, incoming
|
||||
var lastCall = handledCalls[handledCalls.length - 1].call;
|
||||
lastCall.answer(); // the previous call is held by gecko
|
||||
} else {
|
||||
// Held call in CDMA mode, hold to answer to the second call
|
||||
handledCalls[0].call.hold();
|
||||
}
|
||||
|
||||
CallScreen.hideIncoming();
|
||||
|
||||
if (cdmaCallWaiting()) {
|
||||
/* In CDMA mode we need to update the displayed call to reflect the fact
|
||||
* that we're not aware anymore to which number we're connected. We also
|
||||
* need to stop the call waiting tone as the call state doesn't change
|
||||
* after answering and thus doesn't trigger the stop callback. */
|
||||
handledCalls[0].updateCallNumber();
|
||||
CallScreen.cdmaCallWaiting = true;
|
||||
stopWaitingTone();
|
||||
}
|
||||
}
|
||||
|
||||
function endAndAnswer() {
|
||||
if ((handledCalls.length < 2) && !cdmaCallWaiting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (telephony.active === telephony.conferenceGroup) {
|
||||
endConferenceCall().then(function() {
|
||||
CallScreen.hideIncoming();
|
||||
}, function() {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (cdmaCallWaiting()) {
|
||||
/* We're in CDMA mode, there's no way to hang the existing call nor to
|
||||
* know if we're connected to the second call hence we treat this as an
|
||||
* hold-and-answer scenario. */
|
||||
handledCalls[0].call.hold();
|
||||
stopWaitingTone();
|
||||
btHelper.answerWaitingCall();
|
||||
} else {
|
||||
var callToEnd = telephony.active || // connected, incoming
|
||||
handledCalls[handledCalls.length - 2].call; // held, incoming
|
||||
|
||||
var callToAnswer;
|
||||
handledCalls.some(function(handledCall) {
|
||||
if (handledCall.call !== callToEnd) {
|
||||
callToAnswer = handledCall.call;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (callToEnd && callToAnswer) {
|
||||
callToEnd.addEventListener('disconnected', function ondisconnected() {
|
||||
callToEnd.removeEventListener('disconnected', ondisconnected);
|
||||
// Answer the incoming call after hanging up the active call
|
||||
callToAnswer.answer();
|
||||
});
|
||||
|
||||
callToEnd.hangUp(); // hangup the active call
|
||||
} else if (callToEnd) {
|
||||
callToEnd.hangUp(); // hangup the active call
|
||||
} else if (callToAnswer) {
|
||||
callToAnswer.answer(); // answer the incoming call
|
||||
}
|
||||
}
|
||||
|
||||
CallScreen.hideIncoming();
|
||||
|
||||
if (cdmaCallWaiting()) {
|
||||
/* In CDMA mode we need to update the displayed call to reflect the fact
|
||||
* that we're not aware anymore to which number we're connected. We also
|
||||
* need to stop the call waiting tone as the call state doesn't change
|
||||
* after answering and thus doesn't trigger the stop callback. */
|
||||
handledCalls[0].updateCallNumber();
|
||||
CallScreen.cdmaCallWaiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCalls() {
|
||||
if (CallScreen.incomingContainer.classList.contains('displayed') &&
|
||||
!cdmaCallWaiting()) {
|
||||
/* In CDMA call waiting mode only one call is displayed but we can still
|
||||
* switch between calls if a second one is present. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (numOpenLines() < 2 && !cdmaCallWaiting()) {
|
||||
// Putting a call on Hold when there are no other
|
||||
// calls in progress has been disabled until a less
|
||||
// accidental user-interface is implemented.
|
||||
// See bug 894232 and bug 882056 for more background.
|
||||
// We can now hold a call only from BT devices.
|
||||
if (!telephony.active) {
|
||||
holdOrResumeSingleCall();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
telephony.active.hold();
|
||||
btHelper.toggleCalls();
|
||||
}
|
||||
|
||||
function holdOrResumeSingleCall() {
|
||||
if (numOpenLines() !== 1 ||
|
||||
(telephony.calls.length &&
|
||||
(telephony.calls[0].state === 'incoming' ||
|
||||
!telephony.calls[0].switchable))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (telephony.active) {
|
||||
telephony.active.hold();
|
||||
} else {
|
||||
var line = telephony.calls.length ?
|
||||
telephony.calls[0] : telephony.conferenceGroup;
|
||||
|
||||
line.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// Hang up the held call or the second incoming call
|
||||
function hangupWaitingCalls() {
|
||||
handledCalls.forEach(function(handledCall) {
|
||||
var callState = handledCall.call.state;
|
||||
if (callState === 'held' ||
|
||||
(callState === 'incoming' && handledCalls.length > 1)) {
|
||||
handledCall.call.hangUp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function ignore() {
|
||||
/* On CDMA there's no way to hangup an incoming call so we just skip this
|
||||
* step and hide the incoming call. */
|
||||
if (cdmaCallWaiting()) {
|
||||
stopWaitingTone();
|
||||
btHelper.ignoreWaitingCall();
|
||||
} else {
|
||||
var ignoreIndex = handledCalls.length - 1;
|
||||
handledCalls[ignoreIndex].call.hangUp();
|
||||
}
|
||||
|
||||
CallScreen.hideIncoming();
|
||||
}
|
||||
|
||||
function endConferenceCall() {
|
||||
return telephony.conferenceGroup.hangUp().then(function() {
|
||||
ConferenceGroupHandler.signalConferenceEnded();
|
||||
}, function() {
|
||||
console.error('Failed to hangup Conference Call');
|
||||
});
|
||||
}
|
||||
|
||||
function end() {
|
||||
var callToEnd;
|
||||
|
||||
// If there is an active call we end this one
|
||||
if (telephony.active) {
|
||||
callToEnd = telephony.active;
|
||||
} else if (numOpenLines() === 1) {
|
||||
// If there's a single call we end it
|
||||
if (telephony.conferenceGroup.calls.length) {
|
||||
callToEnd = telephony.conferenceGroup;
|
||||
} else {
|
||||
callToEnd = telephony.calls[0];
|
||||
}
|
||||
} else {
|
||||
// If not we're rejecting the last incoming call
|
||||
if (!handledCalls.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lastCallIndex = handledCalls.length - 1;
|
||||
callToEnd = handledCalls[lastCallIndex].call;
|
||||
}
|
||||
|
||||
// If this is a conference call end all the calls in it
|
||||
if (callToEnd.calls) {
|
||||
endConferenceCall();
|
||||
} else {
|
||||
callToEnd.hangUp();
|
||||
}
|
||||
}
|
||||
|
||||
function unmute() {
|
||||
telephony.muted = false;
|
||||
}
|
||||
|
||||
function switchToSpeaker() {
|
||||
// add a btHelper.isConnected() check before calling disconnectSco
|
||||
// once bug 929376 lands.
|
||||
btHelper.disconnectSco();
|
||||
if (!telephony.speakerEnabled) {
|
||||
telephony.speakerEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
function switchToDefaultOut(doNotConnect) {
|
||||
if (telephony.speakerEnabled) {
|
||||
telephony.speakerEnabled = false;
|
||||
}
|
||||
|
||||
if (!doNotConnect && telephony.active && !document.hidden) {
|
||||
// add a btHelper.isConnected() check before calling disconnectSco
|
||||
// once bug 929376 lands.
|
||||
btHelper.connectSco();
|
||||
}
|
||||
}
|
||||
|
||||
function switchToReceiver() {
|
||||
// add a btHelper.isConnected() check before calling disconnectSco
|
||||
// once bug 929376 lands.
|
||||
btHelper.disconnectSco();
|
||||
if (telephony.speakerEnabled) {
|
||||
telephony.speakerEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMute() {
|
||||
telephony.muted = !telephony.muted;
|
||||
}
|
||||
|
||||
function toggleSpeaker() {
|
||||
if (telephony.speakerEnabled) {
|
||||
CallsHandler.switchToDefaultOut();
|
||||
} else {
|
||||
CallsHandler.switchToSpeaker();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the ANSI call waiting tone for a 10 seconds window
|
||||
*
|
||||
* @param {Object} call The call object to which the wait tone is referred to.
|
||||
*/
|
||||
function playWaitingTone(call) {
|
||||
// ANSI call waiting tone for a 10 sec window
|
||||
var sequence = [[440, 440, 100],
|
||||
[0, 0, 100],
|
||||
[440, 440, 100]];
|
||||
|
||||
toneInterval = window.setInterval(function playTone() {
|
||||
TonePlayer.playSequence(sequence);
|
||||
}, 10000);
|
||||
TonePlayer.playSequence(sequence);
|
||||
|
||||
call.addEventListener('statechange', function callStateChange() {
|
||||
call.removeEventListener('statechange', callStateChange);
|
||||
window.clearInterval(toneInterval);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops playing the waiting tone
|
||||
*/
|
||||
function stopWaitingTone() {
|
||||
window.clearInterval(toneInterval);
|
||||
}
|
||||
|
||||
function activeCall() {
|
||||
var telephonyActiveCall = telephony.active;
|
||||
var active = null;
|
||||
for (var i = 0; i < handledCalls.length; i++) {
|
||||
var handledCall = handledCalls[i];
|
||||
if (telephonyActiveCall === handledCall.call) {
|
||||
active = handledCall;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
function activeCallForContactImage() {
|
||||
if (handledCalls.length === 1) {
|
||||
return handledCalls[0];
|
||||
}
|
||||
|
||||
// The active call can be null. We're concatenating the active call with the
|
||||
// list of all handled calls. The active call will appear twice in this
|
||||
// array if it's not null.
|
||||
return [activeCall()].concat(handledCalls).find(function(elem) {
|
||||
return !elem || !elem.call.group;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if we're in CDMA call waiting mode
|
||||
*
|
||||
* @return {Boolean} Returns true if we're in CDMA call waiting mode.
|
||||
*/
|
||||
function cdmaCallWaiting() {
|
||||
return ((telephony.calls.length === 1) &&
|
||||
(telephony.calls[0].state === 'connected') &&
|
||||
(telephony.calls[0].secondId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if we're first call on CDMA network
|
||||
*
|
||||
* @return {Boolean} Return true if we're first call on CDMA network.
|
||||
*/
|
||||
function isFirstCallOnCdmaNetwork() {
|
||||
var cdmaTypes = ['evdo0', 'evdoa', 'evdob', '1xrtt', 'is95a', 'is95b'];
|
||||
if (handledCalls.length !== 0) {
|
||||
var ci = handledCalls[0].call.serviceId;
|
||||
var type = window.navigator.mozMobileConnections[ci].voice.type;
|
||||
|
||||
return (cdmaTypes.indexOf(type) !== -1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isCdma3WayCall() {
|
||||
return isFirstCallOnCdmaNetwork() &&
|
||||
((telephony.calls.length === CDMA_CALLS_LIMIT) ||
|
||||
(telephony.conferenceGroup.calls.length > 0));
|
||||
}
|
||||
|
||||
function mergeCalls() {
|
||||
if (telephony.conferenceGroup.calls.length === 0 &&
|
||||
telephony.calls.length === 2) {
|
||||
telephony.conferenceGroup.add(telephony.calls[0], telephony.calls[1])
|
||||
.catch(function() {
|
||||
CallScreen.showStatusMessage('conferenceAddError');
|
||||
});
|
||||
} else if (telephony.conferenceGroup.calls.length > 0 &&
|
||||
telephony.calls.length === 1) {
|
||||
telephony.conferenceGroup.add(telephony.calls[0])
|
||||
.catch(function() {
|
||||
CallScreen.showStatusMessage('conferenceAddError');
|
||||
});
|
||||
} else {
|
||||
console.warn('Cannot join conference call.');
|
||||
}
|
||||
}
|
||||
|
||||
/* === Telephony audio channel competing functions ===*/
|
||||
|
||||
/**
|
||||
* Check if a call is being established.
|
||||
*
|
||||
* @returns true if a call is being established, false otherwise
|
||||
*/
|
||||
function isEstablishingCall() {
|
||||
return telephony.calls.some(function(call) {
|
||||
return call.state === 'dialing' || call.state === 'alerting';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any of the calls is currently on hold.
|
||||
*
|
||||
* @returns true if a call is on hold, false otherwise
|
||||
*/
|
||||
function isAnyCallOnHold() {
|
||||
return telephony.calls.some(call => call.state === 'held') ||
|
||||
(telephony.conferenceGroup && telephony.conferenceGroup.state === 'held');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any of the calls can be put on hold or resumed.
|
||||
*
|
||||
* @returns true if a call can be put on hold or resumed, false otherwise
|
||||
*/
|
||||
function isAnyCallSwitchable() {
|
||||
return telephony.calls.some(call => call.switchable) ||
|
||||
((telephony.conferenceGroup.calls.length > 0) &&
|
||||
telephony.conferenceGroup.calls.every(call => call.switchable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all non-conference calls are mergeable.
|
||||
*
|
||||
* @returns true if all non-confernece calls can be merged, false otherwise
|
||||
*/
|
||||
function isEveryCallMergeable() {
|
||||
return telephony.calls.every(call => call.mergeable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow placing a new call only when we've not already placed one that isn't
|
||||
* connected yet.
|
||||
*/
|
||||
function updatePlaceNewCall() {
|
||||
if (isEstablishingCall() || (numOpenLines() === 0)) {
|
||||
CallScreen.disablePlaceNewCallButton();
|
||||
} else {
|
||||
CallScreen.enablePlaceNewCallButton();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the state of the hold/merge button to reflect the current calls'
|
||||
* state. If only one call is available the hold button alone will be
|
||||
* displayed if the call's switchable. The state of the button will depend on
|
||||
* the call being on hold or not. If two calls are being handled at the same
|
||||
* time we'll display the merge button if the second call's mergeable. If not
|
||||
* no button will be displayed at all. We don't support cases where more than
|
||||
* two calls are being handled at the same time; this code will need to be
|
||||
* revisited if CALLS_LIMIT is increased above 2.
|
||||
*/
|
||||
function updateMergeAndOnHoldStatus() {
|
||||
var isEstablishing = isEstablishingCall();
|
||||
|
||||
if (numOpenLines() === 0) {
|
||||
/* If no calls are currently present just disable the button hold button
|
||||
* but do not hide it.*/
|
||||
CallScreen.disableOnHoldButton();
|
||||
} else if (numOpenLines() > 1 && !isEstablishing) {
|
||||
/* If more than one call has been established show only the merge
|
||||
* button or no button at all if the calls are not mergeable. */
|
||||
CallScreen.hideOnHoldButton();
|
||||
|
||||
if (isEveryCallMergeable()) {
|
||||
CallScreen.showOnHoldAndMergeContainer();
|
||||
CallScreen.showMergeButton();
|
||||
} else {
|
||||
CallScreen.hideOnHoldAndMergeContainer();
|
||||
}
|
||||
} else {
|
||||
/* If only one call has been established show only the hold button or
|
||||
* no button at all if the calls are not switchable. */
|
||||
CallScreen.hideMergeButton();
|
||||
CallScreen.setShowIsHeld(!telephony.active && isAnyCallOnHold());
|
||||
|
||||
if (isEstablishing) {
|
||||
CallScreen.disableOnHoldButton();
|
||||
} else {
|
||||
CallScreen.enableOnHoldButton();
|
||||
}
|
||||
|
||||
if (isAnyCallSwitchable()) {
|
||||
CallScreen.showOnHoldAndMergeContainer();
|
||||
CallScreen.showOnHoldButton();
|
||||
} else {
|
||||
CallScreen.hideOnHoldAndMergeContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the state of the speaker and mute buttons. Both buttons are active
|
||||
* only if there's an active call.
|
||||
*/
|
||||
function updateMuteAndSpeakerStatus() {
|
||||
if (telephony.active) {
|
||||
CallScreen.enableMuteButton();
|
||||
CallScreen.enableSpeakerButton();
|
||||
} else {
|
||||
CallScreen.disableMuteButton();
|
||||
CallScreen.disableSpeakerButton();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setup: setup,
|
||||
|
||||
answer: answer,
|
||||
holdAndAnswer: holdAndAnswer,
|
||||
endAndAnswer: endAndAnswer,
|
||||
toggleCalls: toggleCalls,
|
||||
ignore: ignore,
|
||||
end: end,
|
||||
toggleMute: toggleMute,
|
||||
toggleSpeaker: toggleSpeaker,
|
||||
unmute: unmute,
|
||||
switchToReceiver: switchToReceiver,
|
||||
switchToSpeaker: switchToSpeaker,
|
||||
switchToDefaultOut: switchToDefaultOut,
|
||||
holdOrResumeSingleCall: holdOrResumeSingleCall,
|
||||
|
||||
checkCalls: onCallsChanged,
|
||||
mergeCalls: mergeCalls,
|
||||
updateAllPhoneNumberDisplays: updateAllPhoneNumberDisplays,
|
||||
updatePlaceNewCall: updatePlaceNewCall,
|
||||
exitCallScreenIfNoCalls: exitCallScreenIfNoCalls,
|
||||
updateMergeAndOnHoldStatus: updateMergeAndOnHoldStatus,
|
||||
updateMuteAndSpeakerStatus: updateMuteAndSpeakerStatus,
|
||||
|
||||
get activeCall() {
|
||||
return activeCall();
|
||||
},
|
||||
|
||||
get activeCallForContactImage() {
|
||||
return activeCallForContactImage();
|
||||
},
|
||||
|
||||
isFirstCallOnCdmaNetwork: isFirstCallOnCdmaNetwork
|
||||
};
|
||||
})();
|
||||
161
gaia/apps/callscreen/js/conference_group_handler.js
Executable file
161
gaia/apps/callscreen/js/conference_group_handler.js
Executable file
|
|
@ -0,0 +1,161 @@
|
|||
/* globals CallsHandler, CallScreen, ConferenceGroupUI, FontSizeManager */
|
||||
|
||||
/* exported ConferenceGroupHandler */
|
||||
|
||||
'use strict';
|
||||
/**
|
||||
* Manages and handles conference call logic.
|
||||
* Exposes the following functionality:
|
||||
* - To add and to remove calls to and from the ongoing conference call details
|
||||
* information overlay.
|
||||
* - To be signaled by other actors that a conference call has ended.
|
||||
* - To check if the conference call details information overlay is shown.
|
||||
* - To get the current conference call duration.
|
||||
*/
|
||||
var ConferenceGroupHandler = (function() {
|
||||
|
||||
/* Observe changes to the node that holds the group call duration, this is
|
||||
* also used for displaying the call ended string and needs to be resized
|
||||
* dynamically to accomodate for certain locales. */
|
||||
function initStringAutoResize() {
|
||||
var mutationObserverConfig = { childList: true };
|
||||
var mutationObserver = new MutationObserver(function(mutations) {
|
||||
if (groupDurationChildNode.hasAttribute('data-l10n-id') &&
|
||||
groupDurationChildNode.getAttribute('data-l10n-id') === 'callEnded') {
|
||||
/* Disable the observer to prevent it from being called recursively
|
||||
* while we modify the DOM tree, re-enable it once we're done. */
|
||||
mutationObserver.disconnect();
|
||||
FontSizeManager.adaptToSpace(
|
||||
CallScreen.getScenario(), groupLabel, false, 'end');
|
||||
mutationObserver.observe(groupDurationChildNode,
|
||||
mutationObserverConfig);
|
||||
}
|
||||
});
|
||||
mutationObserver.observe(groupDurationChildNode, mutationObserverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
var bdiCount = document.createElement('bdi');
|
||||
var groupLine = document.getElementById('group-call');
|
||||
var groupLabel = document.getElementById('group-call-label');
|
||||
groupLabel.appendChild(bdiCount);
|
||||
// FIXME/bug 1007148: Refactor duration element structure
|
||||
var groupDuration = document.querySelector('#group-call > .duration');
|
||||
var groupDurationChildNode = groupDuration.querySelector('span');
|
||||
var groupTotalDurationChildNode =
|
||||
groupDuration.querySelector('.total-duration');
|
||||
|
||||
var telephony = window.navigator.mozTelephony;
|
||||
if (telephony.conferenceGroup) {
|
||||
telephony.conferenceGroup.oncallschanged = onCallsChanged;
|
||||
telephony.conferenceGroup.onstatechange = onStateChange;
|
||||
}
|
||||
|
||||
initStringAutoResize();
|
||||
|
||||
/**
|
||||
* Private helper functions.
|
||||
*/
|
||||
|
||||
function onCallsChanged() {
|
||||
var calls = telephony.conferenceGroup.calls;
|
||||
CallScreen.updateCallsDisplay();
|
||||
// Hide the conference call participant list overlay if the conference call
|
||||
// has ended.
|
||||
if (!calls.length) {
|
||||
ConferenceGroupUI.hideGroupDetails();
|
||||
}
|
||||
|
||||
var id = 'conferenceCall';
|
||||
var args = { n: calls.length };
|
||||
|
||||
navigator.mozL10n.setAttributes(bdiCount, id, args);
|
||||
ConferenceGroupUI.setGroupDetailsHeader({ id: id, args: args });
|
||||
|
||||
// When hanging up phones on conferenceGroup.calls.length >= 2,
|
||||
// we need to update handledCalls here since conferenceGroup.oncallschanged
|
||||
// rather than telephony.oncallschanges raises.
|
||||
// In case of conferenceGroup.calls.length < 2, check should be performed
|
||||
// in onStateChange to ignore transitional oncallschanged events before
|
||||
// exiting.
|
||||
if (telephony.conferenceGroup.calls.length >= 2) {
|
||||
CallsHandler.checkCalls();
|
||||
}
|
||||
|
||||
if (CallsHandler.isFirstCallOnCdmaNetwork()) {
|
||||
CallScreen.cdmaConferenceCall();
|
||||
}
|
||||
}
|
||||
|
||||
function show() {
|
||||
groupLine.hidden = false;
|
||||
groupLine.classList.remove('ended');
|
||||
groupLine.classList.remove('held');
|
||||
CallScreen.createTicker(groupDuration);
|
||||
CallScreen.setCallerContactImage();
|
||||
}
|
||||
|
||||
function end() {
|
||||
groupTotalDurationChildNode.textContent =
|
||||
groupDurationChildNode.textContent;
|
||||
navigator.mozL10n.setAttributes(groupDurationChildNode, 'callEnded');
|
||||
groupLine.classList.add('ended');
|
||||
groupLine.classList.remove('held');
|
||||
CallScreen.stopTicker(groupDuration);
|
||||
|
||||
setTimeout(function(evt) {
|
||||
groupLine.hidden = true;
|
||||
CallScreen.updateCallsDisplay();
|
||||
}, CallScreen.callEndPromptTime);
|
||||
}
|
||||
|
||||
function onStateChange() {
|
||||
switch (telephony.conferenceGroup.state) {
|
||||
case 'connected':
|
||||
show();
|
||||
CallScreen.render('connected');
|
||||
break;
|
||||
case 'held':
|
||||
groupLine.classList.add('held');
|
||||
CallScreen.render('connected-hold');
|
||||
break;
|
||||
case '':
|
||||
// Exiting conference call
|
||||
end();
|
||||
CallsHandler.checkCalls();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publicly exposed API functions.
|
||||
*/
|
||||
|
||||
function signalConferenceEnded() {
|
||||
ConferenceGroupUI.markCallsAsEnded();
|
||||
}
|
||||
|
||||
function addToGroupDetails(node) {
|
||||
ConferenceGroupUI.addCall(node);
|
||||
}
|
||||
|
||||
function removeFromGroupDetails(node) {
|
||||
ConferenceGroupUI.removeCall(node);
|
||||
}
|
||||
|
||||
function isGroupDetailsShown() {
|
||||
return ConferenceGroupUI.isGroupDetailsShown();
|
||||
}
|
||||
|
||||
return {
|
||||
addToGroupDetails: addToGroupDetails,
|
||||
removeFromGroupDetails: removeFromGroupDetails,
|
||||
signalConferenceEnded: signalConferenceEnded,
|
||||
isGroupDetailsShown: isGroupDetailsShown,
|
||||
get currentDuration() {
|
||||
return groupDurationChildNode.textContent;
|
||||
}
|
||||
};
|
||||
})();
|
||||
180
gaia/apps/callscreen/js/conference_group_ui.js
Executable file
180
gaia/apps/callscreen/js/conference_group_ui.js
Executable file
|
|
@ -0,0 +1,180 @@
|
|||
/* globals CallScreen, LazyLoader */
|
||||
|
||||
/* exported ConferenceGroupUI */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This object manages the view (UI) of the details of an ongoing conference
|
||||
* call, this is the presentation of the details of the parties currently
|
||||
* participating on it (phone number, contact name, phone number type, etc.,
|
||||
* if any).
|
||||
* The needed HTML code is lazy loaded during object initialization in the
|
||||
* init() function. The object takes care of the initialization automatically.
|
||||
* The object provides functions to:
|
||||
* - Add and remove calls to the ongoing conference call details information
|
||||
* overlay.
|
||||
* - Show and hide the ongoing conference call details information overlay.
|
||||
* - Mark the calls participating in an ended conference call as hang up.
|
||||
* - Set the title of the ongoing conference call details information overlay.
|
||||
*/
|
||||
var ConferenceGroupUI = (function() {
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
|
||||
var groupCalls = document.getElementById('group-call-details'),
|
||||
groupCallsList,
|
||||
groupCallsHeader,
|
||||
initialized = false,
|
||||
bdiGroupCallsCountElt;
|
||||
|
||||
document.getElementById('group-show').addEventListener(
|
||||
'click', showGroupDetails);
|
||||
|
||||
/**
|
||||
* Private helper functions.
|
||||
*/
|
||||
|
||||
function _init(callback) {
|
||||
if (initialized) {
|
||||
callback();
|
||||
}
|
||||
|
||||
LazyLoader.load([groupCalls], function() {
|
||||
groupCallsHeader = groupCalls.querySelector('header');
|
||||
groupCallsList = document.getElementById('group-call-details-list');
|
||||
document.getElementById('group-hide').addEventListener(
|
||||
'click', _hideGroupDetails);
|
||||
|
||||
if (!initialized) {
|
||||
bdiGroupCallsCountElt = document.createElement('bdi');
|
||||
groupCallsHeader.appendChild(bdiGroupCallsCountElt);
|
||||
}
|
||||
initialized = true;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function _hideGroupDetails(evt) {
|
||||
if (evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
// If the conference call has ended, remove all the nodes from the
|
||||
// conference call participant list overlay.
|
||||
if (!navigator.mozTelephony.conferenceGroup.calls.length) {
|
||||
_removeAllCalls();
|
||||
}
|
||||
|
||||
// Close the overlay.
|
||||
groupCalls.classList.remove('display');
|
||||
}
|
||||
|
||||
function _removeAllCalls() {
|
||||
var callNodes = groupCalls.querySelectorAll('.handled-call');
|
||||
for (var i = 0; i < callNodes.length; i++) {
|
||||
removeCall(callNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public functions exposed by the object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a call node (including information about the phone number, contact
|
||||
* name, phone number type, if any) to an ongoing conference call details
|
||||
* information overlay.
|
||||
*/
|
||||
function addCall(node) {
|
||||
_init(function() {
|
||||
groupCallsList.appendChild(node);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a call node from an ongoing conference call details information
|
||||
* overlay.
|
||||
*/
|
||||
function removeCall(node) {
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the ongoing conference call details information overlay.
|
||||
* @param {MouseEvent} evt The mouse event requesting the showing of the
|
||||
* ongoing conference call details overlay.
|
||||
*/
|
||||
function showGroupDetails(evt) {
|
||||
if (evt) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
groupCalls.classList.add('display');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the ongoing conference call details information overlay. The default
|
||||
* Call Screen app closing delay is applied if the overlay is currently
|
||||
* shown.
|
||||
*/
|
||||
function hideGroupDetails() {
|
||||
setTimeout(_hideGroupDetails,
|
||||
ConferenceGroupUI.isGroupDetailsShown() ?
|
||||
CallScreen.callEndPromptTime : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the visibility status of the ongoing conference call details
|
||||
* information overlay.
|
||||
* @returns {Boolean} Visibility status of the ongoing conference call details
|
||||
* information overlay.
|
||||
*/
|
||||
function isGroupDetailsShown() {
|
||||
return groupCalls.classList.contains('display');
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all the calls in an already ended conference call details
|
||||
* information overlay as hang up.
|
||||
*/
|
||||
function markCallsAsEnded() {
|
||||
_init(function() {
|
||||
var callElems = groupCallsList.getElementsByTagName('SECTION');
|
||||
for (var i = 0; i < callElems.length; i++) {
|
||||
callElems[i].dataset.groupHangup = 'groupHangup';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the ongoing conference call details information overlay.
|
||||
*
|
||||
* @param {String|Object} text The text to display, can be either a string
|
||||
* in which case it's interpreted as an l10n id or an object holding
|
||||
* both the l10n id & args.
|
||||
*/
|
||||
function setGroupDetailsHeader(text) {
|
||||
_init(function() {
|
||||
if (typeof(text) === 'string') {
|
||||
navigator.mozL10n.setAttributes(bdiGroupCallsCountElt, text);
|
||||
} else {
|
||||
navigator.mozL10n.setAttributes(bdiGroupCallsCountElt,
|
||||
text.id, text.args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
addCall: addCall,
|
||||
removeCall: removeCall,
|
||||
showGroupDetails: showGroupDetails,
|
||||
hideGroupDetails: hideGroupDetails,
|
||||
isGroupDetailsShown: isGroupDetailsShown,
|
||||
markCallsAsEnded: markCallsAsEnded,
|
||||
setGroupDetailsHeader: setGroupDetailsHeader
|
||||
};
|
||||
})();
|
||||
40
gaia/apps/callscreen/js/fake-oncall-desktop.js
Executable file
40
gaia/apps/callscreen/js/fake-oncall-desktop.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
'use strict';
|
||||
/* global CallScreen */
|
||||
|
||||
console.error('This file is not to be shipped.');
|
||||
console.error('It just helps to fake a call.');
|
||||
|
||||
(function() {
|
||||
// Don't execute on the phone
|
||||
if (navigator.mozTelephony.conferenceGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// To use it, you need to include at the end of the <head>
|
||||
// <script defer src="/js/fake-oncall-desktop.js"></script>
|
||||
// in index.html
|
||||
// Then access http://callscreen.gaiamobile.org:8080
|
||||
|
||||
var handledCall = document.getElementById('handled-call-template');
|
||||
handledCall.id = '';
|
||||
handledCall.hidden = false;
|
||||
handledCall.classList.add('additionalInfo');
|
||||
handledCall.classList.add('additionalContactInfo');
|
||||
handledCall.classList.add('incoming');
|
||||
handledCall.classList.add('handled-call');
|
||||
|
||||
handledCall.querySelector('.number').innerHTML = 'Joe Smith';
|
||||
handledCall.querySelector('.additionalContactInfo').textContent =
|
||||
'work, 12345';
|
||||
|
||||
var duration = handledCall.querySelector('.duration');
|
||||
var time = duration.querySelector('span');
|
||||
duration.classList.add('isTimer');
|
||||
time.textContent = '9:42';
|
||||
|
||||
CallScreen.updateCallsDisplay();
|
||||
CallScreen.render('connected'); // Change this for various states
|
||||
CallScreen.screen.classList.add('displayed');
|
||||
CallScreen.calls.classList.add('muted');
|
||||
CallScreen.groupCalls.classList.add('held');
|
||||
})();
|
||||
474
gaia/apps/callscreen/js/handled_call.js
Executable file
474
gaia/apps/callscreen/js/handled_call.js
Executable file
|
|
@ -0,0 +1,474 @@
|
|||
/* globals CallsHandler, CallScreen,
|
||||
ConferenceGroupHandler, Contacts, ContactPhotoHelper,
|
||||
FontSizeManager, FontSizeUtils, Utils, Voicemail, TonePlayer */
|
||||
|
||||
'use strict';
|
||||
|
||||
function HandledCall(aCall) {
|
||||
this.photo = null;
|
||||
this._leftGroup = false;
|
||||
this.call = aCall;
|
||||
|
||||
aCall.addEventListener('statechange', this);
|
||||
|
||||
aCall.ongroupchange = (function onGroupChange() {
|
||||
if (this.call.group) {
|
||||
ConferenceGroupHandler.addToGroupDetails(this.node);
|
||||
this._leftGroup = false;
|
||||
} else if (this._wasUnmerged()) {
|
||||
if (ConferenceGroupHandler.isGroupDetailsShown()) {
|
||||
// Since the call has been unmerged and its node will be moved from the
|
||||
// participant list overlay to the main call screen, the call node is
|
||||
// cloned and added to the call node parent so it is kept in the
|
||||
// participant list overlay until it is hidden.
|
||||
this.node.parentNode.insertBefore(this.node.cloneNode(true), this.node);
|
||||
}
|
||||
// Move the call node from the conference call participant list overlay
|
||||
// to the main call screen page.
|
||||
CallScreen.insertCall(this.node);
|
||||
this._leftGroup = false;
|
||||
} else {
|
||||
this._leftGroup = !this.node.dataset.groupHangup;
|
||||
}
|
||||
}).bind(this);
|
||||
|
||||
this._initialState = this.call.state;
|
||||
this._cachedInfo = null;
|
||||
this._cachedAdditionalTel = null;
|
||||
this._cachedAdditionalTelType = null;
|
||||
this._removed = false;
|
||||
this._wasConnected = false;
|
||||
|
||||
this.node = document.getElementById('handled-call-template').cloneNode(true);
|
||||
this.node.id = '';
|
||||
this.node.classList.add('handled-call');
|
||||
this.node.hidden = false;
|
||||
|
||||
// TODO: The structure of the duration related elements will be refactored in
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1007148
|
||||
this.durationNode = this.node.querySelector('.duration');
|
||||
this.durationChildNode = this.node.querySelector('.duration span');
|
||||
this.totalDurationNode = this.node.querySelector('.total-duration');
|
||||
this.viaSimNode = this.node.querySelector('.sim .via-sim');
|
||||
this.simNumberNode = this.node.querySelector('.sim .sim-number');
|
||||
this.numberNode = this.node.querySelector('.numberWrapper .number bdi');
|
||||
this.outerNode = this.node.querySelector('.numberWrapper .number');
|
||||
this.groupCallNumberNode =
|
||||
document.getElementById('group-call-label');
|
||||
this.additionalTelNode =
|
||||
this.node.querySelector('.additionalContactInfo .tel');
|
||||
this.additionalTelTypeNode =
|
||||
this.node.querySelector('.additionalContactInfo .tel-type');
|
||||
this.hangupButton = this.node.querySelector('.hangup-button');
|
||||
this.hangupButton.onclick = (function() {
|
||||
this.call.hangUp();
|
||||
}.bind(this));
|
||||
|
||||
this.updateCallNumber();
|
||||
this.createCustomStyles();
|
||||
|
||||
/* Observe changes to the node that holds the call duration, this is also
|
||||
* used for displaying the call ended string and needs to be resized
|
||||
* dynamically to accomodate for certain locales. */
|
||||
this.mutationObserverConfig = { childList: true };
|
||||
this.mutationObserver =
|
||||
new MutationObserver(this.observeMutation.bind(this));
|
||||
this.mutationObserver.observe(this.durationChildNode,
|
||||
this.mutationObserverConfig);
|
||||
|
||||
navigator.mozL10n.setAttributes(this.durationChildNode,
|
||||
(this.call.state === 'incoming') ? 'incoming' : 'connecting');
|
||||
this.updateDirection();
|
||||
|
||||
if (navigator.mozIccManager.iccIds.length > 1) {
|
||||
this.node.classList.add('sim-info');
|
||||
var n = this.call.serviceId + 1;
|
||||
navigator.mozL10n.setAttributes(this.viaSimNode, 'via-sim', { n: n });
|
||||
navigator.mozL10n.setAttributes(this.simNumberNode, 'sim-number', { n: n });
|
||||
} else {
|
||||
this.viaSimNode.hidden = true;
|
||||
this.simNumberNode.hidden = true;
|
||||
}
|
||||
|
||||
// Some calls might be already connected
|
||||
if (this._initialState === 'connected') {
|
||||
this.connected();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the global custom style used for resizing the call ended string.
|
||||
* Re-uses the existing one it if the style is already present.
|
||||
*/
|
||||
HandledCall.prototype.createCustomStyles = function hc_createCustomStyles() {
|
||||
const CUSTOM_STYLE_ID = 'call-ended-custom-style';
|
||||
|
||||
var style = document.getElementById(CUSTOM_STYLE_ID);
|
||||
|
||||
if (!style) {
|
||||
style = document.createElement('style');
|
||||
style.id = CUSTOM_STYLE_ID;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
this.callEndedStyleSheet = style.sheet;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recomputes the font size rules so that the call ended string fits both in
|
||||
* the callscreen display and in the statusbar.
|
||||
*/
|
||||
HandledCall.prototype.computeCallEndedFontSizeRules =
|
||||
function hc_computeCallEndedFontSizeRules() {
|
||||
// Remove the existing rules
|
||||
while (this.callEndedStyleSheet.cssRules.length > 0) {
|
||||
this.callEndedStyleSheet.deleteRule(0);
|
||||
}
|
||||
|
||||
var computedStyle = window.getComputedStyle(this.durationChildNode);
|
||||
var fontFamily = computedStyle.fontFamily;
|
||||
var allowedSizes = [ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 ];
|
||||
|
||||
/* Compute the size of the font for displaying the string in the callscreen,
|
||||
* this rules only apply when there's a single call, i.e. when the
|
||||
* big-duration class is present in the calls node. */
|
||||
var info = FontSizeUtils.getMaxFontSizeInfo(
|
||||
this.durationChildNode.textContent, allowedSizes, fontFamily, 180);
|
||||
var rule =
|
||||
'@media (min-height: 4.5em) {' +
|
||||
' #calls.big-duration > section.ended .duration > span,' +
|
||||
' #calls.big-duration > section.ended .total-duration {' +
|
||||
' font-size: ' + (info.fontSize / 10.0) + 'rem;' +
|
||||
' }' +
|
||||
'}';
|
||||
this.callEndedStyleSheet.insertRule(rule,
|
||||
this.callEndedStyleSheet.cssRules.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Observe DOM mutations. This will automatically resize the call ended string
|
||||
* once it's displayed at the end of a call.
|
||||
*/
|
||||
HandledCall.prototype.observeMutation =
|
||||
function hc_observeMutation(mutations) {
|
||||
if (this.durationChildNode.hasAttribute('data-l10n-id') &&
|
||||
this.durationChildNode.getAttribute('data-l10n-id') === 'callEnded') {
|
||||
/* Disable the observer to prevent it from being called recursively
|
||||
* while we modify the DOM tree, re-enable it once we're done . */
|
||||
this.mutationObserver.disconnect();
|
||||
this.computeCallEndedFontSizeRules();
|
||||
this.mutationObserver.observe(this.durationChildNode,
|
||||
this.mutationObserverConfig);
|
||||
}
|
||||
};
|
||||
|
||||
HandledCall.prototype._wasUnmerged = function hc_wasUnmerged() {
|
||||
return !this.node.dataset.groupHangup &&
|
||||
this.call.state != 'disconnecting' &&
|
||||
this.call.state != 'disconnected';
|
||||
};
|
||||
|
||||
HandledCall.prototype.handleEvent = function hc_handle(evt) {
|
||||
CallsHandler.updatePlaceNewCall();
|
||||
CallsHandler.updateMergeAndOnHoldStatus();
|
||||
CallsHandler.updateMuteAndSpeakerStatus();
|
||||
|
||||
switch (evt.call.state) {
|
||||
case 'connected':
|
||||
CallScreen.render('connected');
|
||||
this.connected();
|
||||
break;
|
||||
case 'disconnected':
|
||||
this.disconnected();
|
||||
break;
|
||||
case 'held':
|
||||
this.node.classList.add('held');
|
||||
CallScreen.render('connected-hold');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
HandledCall.prototype.updateCallNumber = function hc_updateCallNumber() {
|
||||
var number = this.call.id.number;
|
||||
var self = this;
|
||||
|
||||
CallScreen.setCallerContactImage();
|
||||
|
||||
/* If we have a second call waiting in CDMA mode then we don't know which
|
||||
* number is currently active */
|
||||
if (this.call.secondId) {
|
||||
this.replacePhoneNumber('switch-calls');
|
||||
this.replaceAdditionalContactInfo(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!number) {
|
||||
this.replacePhoneNumber('withheld-number');
|
||||
return;
|
||||
}
|
||||
|
||||
var isEmergencyNumber = this.call.emergency;
|
||||
if (isEmergencyNumber) {
|
||||
this.node.classList.add('emergency');
|
||||
this.replacePhoneNumber({ raw: number }, 'end');
|
||||
this.replaceAdditionalContactInfo('emergencyNumber');
|
||||
return;
|
||||
}
|
||||
|
||||
Voicemail.check(number, this.call.serviceId).then(
|
||||
function(isVoicemailNumber) {
|
||||
if (isVoicemailNumber) {
|
||||
self.replacePhoneNumber('voiceMail');
|
||||
} else {
|
||||
Contacts.findByNumber(number, lookupContact);
|
||||
checkICCMessage();
|
||||
}
|
||||
});
|
||||
|
||||
function checkICCMessage() {
|
||||
var callMessageReq = navigator.mozSettings.createLock().
|
||||
get('icc.callmessage');
|
||||
callMessageReq.onsuccess = function onCallMessageSuccess() {
|
||||
self._iccCallMessage = callMessageReq.result['icc.callmessage'];
|
||||
if (self._iccCallMessage) {
|
||||
self.replacePhoneNumber({ raw: self._iccCallMessage }, 'end');
|
||||
navigator.mozSettings.createLock().set({'icc.callmessage': null});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function lookupContact(contact, matchingTel, contactsWithSameNumber) {
|
||||
if (self._iccCallMessage) {
|
||||
return;
|
||||
}
|
||||
if (contact) {
|
||||
var primaryInfo = Utils.getPhoneNumberPrimaryInfo(matchingTel, contact);
|
||||
if (primaryInfo) {
|
||||
self.replacePhoneNumber({ raw: primaryInfo }, 'end');
|
||||
} else {
|
||||
self.replacePhoneNumber('withheld-number');
|
||||
}
|
||||
self.replaceAdditionalContactInfo({ raw: matchingTel.value },
|
||||
Utils.getPhoneNumberAdditionalInfo(matchingTel));
|
||||
|
||||
var photo = ContactPhotoHelper.getFullResolution(contact);
|
||||
if (photo) {
|
||||
self.photo = photo;
|
||||
CallScreen.setCallerContactImage();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.replacePhoneNumber({ raw: number }, 'end');
|
||||
self.replaceAdditionalContactInfo(self._cachedAdditionalTel,
|
||||
self._cachedAdditionalTelType);
|
||||
}
|
||||
};
|
||||
|
||||
HandledCall.prototype.replaceAdditionalContactInfo =
|
||||
function hc_replaceAdditionalContactInfo(additionalTel, additionalTelType) {
|
||||
if (!additionalTel && !additionalTelType) {
|
||||
this.additionalTelNode.removeAttribute('data-l10n-id');
|
||||
this.additionalTelNode.textContent = '';
|
||||
this.additionalTelTypeNode.removeAttribute('data-l10n-id');
|
||||
this.additionalTelTypeNode.textContent = '';
|
||||
this.node.classList.remove('additionalInfo');
|
||||
} else {
|
||||
if (typeof(additionalTel) === 'string') {
|
||||
navigator.mozL10n.setAttributes(this.additionalTelNode, additionalTel);
|
||||
} else if (additionalTel.id) {
|
||||
navigator.mozL10n.setAttributes(this.additionalTelNode, additionalTel.id,
|
||||
additionalTel.args);
|
||||
} else if (additionalTel.raw) {
|
||||
this.additionalTelNode.removeAttribute('data-l10n-id');
|
||||
this.additionalTelNode.textContent = additionalTel.raw;
|
||||
}
|
||||
|
||||
if (additionalTelType) {
|
||||
navigator.mozL10n.setAttributes(this.additionalTelTypeNode,
|
||||
additionalTelType.id,
|
||||
additionalTelType.args);
|
||||
}
|
||||
|
||||
this.node.classList.add('additionalInfo');
|
||||
}
|
||||
|
||||
this._cachedAdditionalTel = additionalTel;
|
||||
this._cachedAdditionalTelType = additionalTelType || null;
|
||||
};
|
||||
|
||||
HandledCall.prototype.restoreAdditionalContactInfo =
|
||||
function hc_restoreAdditionalContactInfo() {
|
||||
this.replaceAdditionalContactInfo(this._cachedAdditionalTel,
|
||||
this._cachedAdditionalTelType);
|
||||
};
|
||||
|
||||
HandledCall.prototype.formatPhoneNumber =
|
||||
function hc_formatPhoneNumber(ellipsisSide) {
|
||||
if (this._removed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't format if the call is in a conference.
|
||||
if (this.call.group) {
|
||||
this.numberNode.style = '';
|
||||
return;
|
||||
}
|
||||
|
||||
var scenario = CallScreen.getScenario();
|
||||
// To cover the second incoming call sub-scenario of the call waiting one,
|
||||
// we have to check if the current call is in incoming state and if the
|
||||
// incoming lower pane is being shown.
|
||||
if (scenario === FontSizeManager.CALL_WAITING &&
|
||||
this.call.state === 'incoming' &&
|
||||
CallScreen.incomingContainer.classList.contains('displayed')) {
|
||||
scenario = FontSizeManager.SECOND_INCOMING_CALL;
|
||||
}
|
||||
FontSizeManager.adaptToSpace(
|
||||
scenario, this.outerNode, false, ellipsisSide);
|
||||
if (this.node.classList.contains('additionalInfo')) {
|
||||
FontSizeManager.ensureFixedBaseline(scenario, this.numberNode);
|
||||
} else {
|
||||
FontSizeManager.resetFixedBaseline(this.numberNode);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method renders a phone number. It can be used to display numbers typed
|
||||
* during a call without altering the original number stored in the object.
|
||||
*/
|
||||
HandledCall.prototype.renderPhoneNumber =
|
||||
function hc_renderPhoneNumber(number, ellipsisSide) {
|
||||
if (typeof(number) === 'string') {
|
||||
navigator.mozL10n.setAttributes(this.numberNode, number);
|
||||
} else if (number.id) {
|
||||
navigator.mozL10n.setAttributes(this.numberNode, number.id,
|
||||
number.args);
|
||||
this.numberNode.style.fontSize = '';
|
||||
} else if (number.raw) {
|
||||
this.numberNode.removeAttribute('data-l10n-id');
|
||||
this.numberNode.textContent = number.raw;
|
||||
this.formatPhoneNumber(ellipsisSide);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method replaces the phone number in the call with a new number and
|
||||
* renders it.
|
||||
*/
|
||||
HandledCall.prototype.replacePhoneNumber =
|
||||
function hc_replacePhoneNumber(number, ellipsisSide) {
|
||||
this.renderPhoneNumber(number, ellipsisSide);
|
||||
this._cachedInfo = number;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method renders the number that was originally associated with this
|
||||
* object in case it was altered (e.g. by typing numbers during a call).
|
||||
*/
|
||||
HandledCall.prototype.restorePhoneNumber =
|
||||
function hc_restorePhoneNumber() {
|
||||
this._cachedInfo && this.replacePhoneNumber(this._cachedInfo, 'end');
|
||||
};
|
||||
|
||||
HandledCall.prototype.updateDirection = function hc_updateDirection() {
|
||||
var classList = this.node.classList;
|
||||
if (this._initialState === 'incoming') {
|
||||
classList.add('incoming');
|
||||
this.node.setAttribute('data-l10n-id', 'incoming_screen_reader');
|
||||
} else {
|
||||
classList.add('outgoing');
|
||||
this.node.setAttribute('data-l10n-id', 'outgoing_screen_reader');
|
||||
}
|
||||
|
||||
if (this.call.state === 'connected') {
|
||||
classList.add('ongoing');
|
||||
}
|
||||
};
|
||||
|
||||
HandledCall.prototype.remove = function hc_remove() {
|
||||
this._removed = true;
|
||||
this.call.removeEventListener('statechange', this);
|
||||
this.photo = null;
|
||||
|
||||
var self = this;
|
||||
CallScreen.stopTicker(this.durationNode);
|
||||
var currentDuration = ConferenceGroupHandler.isGroupDetailsShown() ?
|
||||
ConferenceGroupHandler.currentDuration : this.durationChildNode.textContent;
|
||||
|
||||
this.node.classList.add('ended');
|
||||
this.durationNode.classList.remove('isTimer');
|
||||
|
||||
/* This string will be resized automatically to fit its container, see
|
||||
* hc_observeMutations() & hc_computeCallEndedFontSizeRules. */
|
||||
navigator.mozL10n.setAttributes(this.durationChildNode, 'callEnded');
|
||||
|
||||
// FIXME/bug 1007148: Refactor duration element structure. No number or ':'
|
||||
// existence checking will be necessary.
|
||||
var totalDuration = !!currentDuration.match(/\d+/g) ? currentDuration : '';
|
||||
this.totalDurationNode.textContent = totalDuration;
|
||||
|
||||
setTimeout(function(evt) {
|
||||
CallScreen.removeCall(self.node);
|
||||
self.node = null;
|
||||
}, CallScreen.callEndPromptTime);
|
||||
};
|
||||
|
||||
HandledCall.prototype.connected = function hc_connected() {
|
||||
this.show();
|
||||
this.node.classList.remove('held');
|
||||
|
||||
this.updateDirection();
|
||||
CallScreen.createTicker(this.durationNode);
|
||||
CallScreen.syncSpeakerEnabled();
|
||||
|
||||
this.updateCallNumber();
|
||||
|
||||
this._wasConnected = true;
|
||||
};
|
||||
|
||||
HandledCall.prototype.disconnected = function hc_disconnected() {
|
||||
if (this._leftGroup) {
|
||||
if (typeof(this._cachedInfo) === 'string') {
|
||||
CallScreen.showStatusMessage(this._cachedInfo);
|
||||
} else if (this._cachedInfo.id) {
|
||||
const disconnectedMessageL10n = {
|
||||
'withheld-number': 'withheld-number-left-call',
|
||||
'voiceMail': 'voicemail-left-call'
|
||||
};
|
||||
var id = disconnectedMessageL10n[this._cachedInfo.id];
|
||||
|
||||
CallScreen.showStatusMessage(id);
|
||||
} else if (this._cachedInfo.raw) {
|
||||
CallScreen.showStatusMessage({
|
||||
id: 'caller-left-call',
|
||||
args: { caller: this._cachedInfo.raw }
|
||||
});
|
||||
}
|
||||
|
||||
this._leftGroup = false;
|
||||
}
|
||||
|
||||
// Play End call tone only if the call was connected.
|
||||
if (this._wasConnected) {
|
||||
TonePlayer.playSequence([[480, 620, 250]]);
|
||||
}
|
||||
this._wasConnected = false;
|
||||
|
||||
this.remove();
|
||||
};
|
||||
|
||||
HandledCall.prototype.show = function hc_show() {
|
||||
if (this.node) {
|
||||
this.node.hidden = false;
|
||||
}
|
||||
CallScreen.updateCallsDisplay();
|
||||
};
|
||||
|
||||
HandledCall.prototype.hide = function hc_hide() {
|
||||
if (this.node) {
|
||||
this.node.hidden = true;
|
||||
}
|
||||
CallScreen.updateCallsDisplay();
|
||||
};
|
||||
24
gaia/apps/callscreen/js/index.js
Executable file
24
gaia/apps/callscreen/js/index.js
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
/* globals CallsHandler, CallScreen,
|
||||
KeypadManager */
|
||||
|
||||
/**
|
||||
* Function invoked at onload time to initialize the application.
|
||||
*/
|
||||
function onLoadCallScreen(evt) {
|
||||
window.removeEventListener('load', onLoadCallScreen);
|
||||
|
||||
CallsHandler.setup();
|
||||
CallScreen.init();
|
||||
KeypadManager.init(/* oncall */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy function introduced in bug 1083729, see below.
|
||||
*/
|
||||
function unloadCallScreen(evt) { }
|
||||
|
||||
window.addEventListener('load', onLoadCallScreen);
|
||||
|
||||
// Bug 1083729: add an empty unload event listener to circumvent bug 1078448.
|
||||
window.addEventListener('unload', unloadCallScreen);
|
||||
Loading…
Reference in a new issue