VulpesOS/gaia/apps/callscreen/js/calls_handler.js
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

974 lines
29 KiB
JavaScript
Executable file

/* 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
};
})();