Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
963 lines
29 KiB
JavaScript
Executable file
963 lines
29 KiB
JavaScript
Executable file
/* exported KeypadManager */
|
|
|
|
/* globals AddContactMenu, CallHandler, CallLogDBManager, CallsHandler,
|
|
CallScreen, ConfirmDialog, CustomDialog, DtmfTone, FontSizeManager,
|
|
LazyLoader, MultiSimActionButton, Promise, SimSettingsHelper,
|
|
SettingsListener, TonePlayer */
|
|
|
|
'use strict';
|
|
|
|
// Frequencies coming from http://en.wikipedia.org/wiki/Telephone_keypad
|
|
var gTonesFrequencies = {
|
|
'1': [697, 1209], '2': [697, 1336], '3': [697, 1477],
|
|
'4': [770, 1209], '5': [770, 1336], '6': [770, 1477],
|
|
'7': [852, 1209], '8': [852, 1336], '9': [852, 1477],
|
|
'*': [941, 1209], '0': [941, 1336], '#': [941, 1477]
|
|
};
|
|
|
|
var KeypadManager = {
|
|
|
|
kMaxDigits: 50,
|
|
|
|
_phoneNumber: '',
|
|
_onCall: false,
|
|
|
|
_keypadSoundIsEnabled: false,
|
|
_shortTone: false,
|
|
_vibrationEnabled: false,
|
|
|
|
// Keep in sync with Lockscreen and keyboard vibration
|
|
kVibrationDuration: 50, // ms
|
|
|
|
// Due to the ellipsis capabilities of the number view, the cursor position
|
|
// in the input element, does not match the point where new digits should be
|
|
// added to the current number or where the deletion operation should take
|
|
// place. This property holds the position of the edition point for the
|
|
// current number (_phoneNumber property).
|
|
_insertPosition: null,
|
|
|
|
onValueChanged: null,
|
|
|
|
get phoneNumberView() {
|
|
delete this.phoneNumberView;
|
|
this.phoneNumberView = document.getElementById('phone-number-view');
|
|
return this.phoneNumberView;
|
|
},
|
|
|
|
get phoneNumberViewContainer() {
|
|
delete this.phoneNumberViewContainer;
|
|
this.phoneNumberViewContainer =
|
|
document.getElementById('phone-number-view-container');
|
|
return this.phoneNumberViewContainer;
|
|
},
|
|
|
|
get keypad() {
|
|
delete this.keypad;
|
|
this.keypad = document.getElementById('keypad');
|
|
return this.keypad;
|
|
},
|
|
|
|
get callBar() {
|
|
delete this.callBar;
|
|
this.callBar =
|
|
document.getElementById('keypad-callbar');
|
|
return this.callBar;
|
|
},
|
|
|
|
get hideBar() {
|
|
delete this.hideBar;
|
|
this.hideBar = document.getElementById('keypad-hidebar');
|
|
return this.hideBar;
|
|
},
|
|
|
|
get callBarAddContact() {
|
|
delete this.callBarAddContact;
|
|
this.callBarAddContact =
|
|
document.getElementById('keypad-callbar-add-contact');
|
|
return this.callBarAddContact;
|
|
},
|
|
|
|
get callBarCallAction() {
|
|
delete this.callBarCallAction;
|
|
this.callBarCallAction =
|
|
document.getElementById('keypad-callbar-call-action');
|
|
return this.callBarCallAction;
|
|
},
|
|
|
|
get callBarCancelAction() {
|
|
delete this.callBarCancelAction;
|
|
this.callBarCancelAction =
|
|
document.getElementById('keypad-callbar-cancel');
|
|
return this.callBarCancelAction;
|
|
},
|
|
|
|
get deleteButton() {
|
|
delete this.deleteButton;
|
|
this.deleteButton = document.getElementById('keypad-delete');
|
|
return this.deleteButton;
|
|
},
|
|
|
|
get hideBarHangUpAction() {
|
|
delete this.hideBarHangUpAction;
|
|
this.hideBarHangUpAction =
|
|
document.getElementById('keypad-hidebar-hang-up-action-wrapper');
|
|
return this.hideBarHangUpAction;
|
|
},
|
|
|
|
get hideBarHideAction() {
|
|
delete this.hideBarHideAction;
|
|
this.hideBarHideAction =
|
|
document.getElementById('keypad-hidebar-hide-keypad-action');
|
|
return this.hideBarHideAction;
|
|
},
|
|
|
|
multiSimActionButton: null,
|
|
|
|
/**
|
|
* Initializes the keypad manager, registers all the appropriate event
|
|
* handlers and instances the necessary sound infrastructure so that the
|
|
* keypad will be fully functional once this method has been called.
|
|
*
|
|
* @param oncall {Boolean} True if the keypad manager will be used during a
|
|
* call and will play sounds on the "telephony" channel. False if it
|
|
* will be used outside of a call and should use the "content" channel
|
|
* instead. We should be using the "notification" channel but we can't
|
|
* due to bug 1092346.
|
|
*/
|
|
init: function kh_init(oncall) {
|
|
|
|
this._onCall = !!oncall;
|
|
this._lastPressedKey = null;
|
|
|
|
this.phoneNumberView.value = '';
|
|
this.phoneNumberView
|
|
.addEventListener('click', this._updateInsertPosition.bind(this));
|
|
this._phoneNumber = '';
|
|
|
|
var keyHandler = this.keyHandler.bind(this);
|
|
this.keypad.addEventListener('contextmenu', keyHandler);
|
|
|
|
this.keypad.addEventListener('touchstart', keyHandler, true);
|
|
this.keypad.addEventListener('touchmove', keyHandler, true);
|
|
this.keypad.addEventListener('touchend', keyHandler, true);
|
|
this.keypad.addEventListener('touchcancel', keyHandler, true);
|
|
|
|
this.deleteButton.addEventListener('touchstart', keyHandler);
|
|
this.deleteButton.addEventListener('touchend', keyHandler);
|
|
// The keypad add contact bar is only included in the normal version of
|
|
// the keypad.
|
|
if (this.callBarAddContact) {
|
|
this.callBarAddContact.addEventListener('click',
|
|
this.addContact.bind(this));
|
|
}
|
|
|
|
// The keypad call bar is only included in the normal version and
|
|
// the emergency call version of the keypad.
|
|
if (this.callBarCallAction) {
|
|
if (typeof MultiSimActionButton !== 'undefined') {
|
|
this.multiSimActionButton =
|
|
new MultiSimActionButton(this.callBarCallAction,
|
|
CallHandler.call,
|
|
'ril.telephony.defaultServiceId',
|
|
this.phoneNumber.bind(this));
|
|
}
|
|
this.callBarCallAction.addEventListener('click',
|
|
this.fetchLastCalled.bind(this));
|
|
}
|
|
|
|
// The keypad cancel bar is only the emergency call version of the keypad.
|
|
if (this.callBarCancelAction) {
|
|
this.callBarCancelAction.addEventListener('click', function() {
|
|
window.parent.LockScreen.switchPanel();
|
|
});
|
|
}
|
|
|
|
// The keypad hide bar is only included in the on call version of the
|
|
// keypad.
|
|
if (this.hideBarHideAction) {
|
|
this.hideBarHideAction.addEventListener('click',
|
|
this.callbarBackAction);
|
|
}
|
|
|
|
if (this.hideBarHangUpAction) {
|
|
this.hideBarHangUpAction.addEventListener('click',
|
|
this.hangUpCallFromKeypad);
|
|
}
|
|
|
|
// According to the UX sound spec, we should use the "system" type for the
|
|
// dialer pad. See the attachment of the bug1068219.
|
|
TonePlayer.init('system');
|
|
|
|
this.render();
|
|
LazyLoader.load(['/shared/style/action_menu.css',
|
|
'/dialer/js/suggestion_bar.js']);
|
|
|
|
this._observePreferences();
|
|
},
|
|
|
|
render: function hk_render(layoutType) {
|
|
if (layoutType == 'oncall') {
|
|
if (CallsHandler.activeCall) {
|
|
var activeCall = CallsHandler.activeCall.call;
|
|
this._phoneNumber = activeCall.id.number;
|
|
}
|
|
this._isKeypadClicked = false;
|
|
this.phoneNumberViewContainer.classList.add('keypad-visible');
|
|
if (this.callBar) {
|
|
this.callBar.classList.add('hide');
|
|
}
|
|
|
|
if (this.hideBar) {
|
|
this.hideBar.classList.remove('hide');
|
|
}
|
|
|
|
this.deleteButton.classList.add('hide');
|
|
} else {
|
|
this.phoneNumberViewContainer.classList.remove('keypad-visible');
|
|
|
|
if (this.callBar) {
|
|
this.callBar.classList.remove('hide');
|
|
}
|
|
|
|
if (this.hideBar) {
|
|
this.hideBar.classList.add('hide');
|
|
}
|
|
|
|
this.deleteButton.classList.remove('hide');
|
|
}
|
|
},
|
|
|
|
phoneNumber: function hk_phoneNumber() {
|
|
return this._phoneNumber;
|
|
},
|
|
|
|
fetchLastCalled: function hk_fetchLastCalled() {
|
|
if (this._phoneNumber !== '') {
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
CallLogDBManager.getGroupAtPosition(1, 'lastEntryDate', true, 'dialing',
|
|
function hk_ggap_callback(result) {
|
|
if (result && (typeof result === 'object') && result.number) {
|
|
self.updatePhoneNumber(result.number);
|
|
}
|
|
}
|
|
);
|
|
},
|
|
|
|
addContact: function hk_addContact(event) {
|
|
var number = this._phoneNumber;
|
|
if (!number) {
|
|
return;
|
|
}
|
|
LazyLoader.load(['/dialer/js/add_contact_menu.js'], function() {
|
|
AddContactMenu.show(number);
|
|
});
|
|
},
|
|
|
|
callbarBackAction: function hk_callbarBackAction(event) {
|
|
CallScreen.hideKeypad();
|
|
},
|
|
|
|
hangUpCallFromKeypad: function hk_hangUpCallFromKeypad(event) {
|
|
CallScreen.hideKeypad();
|
|
CallsHandler.end();
|
|
},
|
|
|
|
_lastPressedKey: null,
|
|
_dtmfTone: null,
|
|
|
|
_playDtmfTone: function kh_playDtmfTone(key) {
|
|
var serviceId = 0;
|
|
|
|
if (!this._onCall) {
|
|
return;
|
|
}
|
|
|
|
if (CallsHandler.activeCall) {
|
|
// Single call
|
|
serviceId = CallsHandler.activeCall.call.serviceId;
|
|
} else {
|
|
// Conference call
|
|
serviceId = navigator.mozTelephony.active.calls[0].serviceId;
|
|
}
|
|
|
|
if (this._dtmfTone) {
|
|
this._dtmfTone.stop();
|
|
this._dtmfTone = null;
|
|
}
|
|
|
|
this._dtmfTone = new DtmfTone(key, this._shortTone, serviceId);
|
|
this._dtmfTone.play();
|
|
},
|
|
|
|
_stopDtmfTone: function kh_stopDtmfTone() {
|
|
if (!this._dtmfTone) {
|
|
return;
|
|
}
|
|
|
|
this._dtmfTone.stop();
|
|
this._dtmfTone = null;
|
|
},
|
|
|
|
/**
|
|
* Function used to respond to touchstart events over the keypad. Reacts to
|
|
* the first key that has been pressed by playing the appropriate tone and
|
|
* sets up the necessary timers to react to long presses.
|
|
*
|
|
* @param {String} key The key that was hit by this touchstart event.
|
|
*/
|
|
_touchStart: function kh_touchStart(key) {
|
|
|
|
this._longPress = false;
|
|
this._lastPressedKey = key;
|
|
|
|
// If user input number more 50 digits, app shouldn't accept.
|
|
// The limit only applies while not on a call - there is no
|
|
// limit while on a call (bug 917630).
|
|
if (key !== 'delete' && !this._onCall &&
|
|
(this._phoneNumber.length >= this.kMaxDigits)) {
|
|
return;
|
|
}
|
|
|
|
if (key != 'delete') {
|
|
if (this._keypadSoundIsEnabled) {
|
|
// We do not support long press if not on a call
|
|
TonePlayer.start(
|
|
gTonesFrequencies[key], !this._onCall || this._shortTone);
|
|
}
|
|
|
|
if (this._vibrationEnabled) {
|
|
navigator.vibrate(this.kVibrationDuration);
|
|
}
|
|
|
|
this._playDtmfTone(key);
|
|
}
|
|
|
|
// Manage long press
|
|
if (((key == '0' || key == '*') && !this._onCall) || key == 'delete') {
|
|
this._holdTimer = setTimeout(function(self) {
|
|
self.restoreCaretPosition();
|
|
|
|
if (key == 'delete') {
|
|
self._clearPhoneNumber();
|
|
} else if (key === '0') {
|
|
self._replaceLastKey('+');
|
|
} else if (key === '*') {
|
|
var isAtTheEnd = self._insertPosition === null;
|
|
var isFirst = self._insertPosition === 1 ||
|
|
(isAtTheEnd && self._phoneNumber.length === 1);
|
|
// ',' DTMF separator can't be the first
|
|
if (!isFirst) {
|
|
self._replaceLastKey(',');
|
|
} else {
|
|
self._deleteAtInsertPosition();
|
|
}
|
|
}
|
|
|
|
self._longPress = true;
|
|
self._updatePhoneNumberView('begin', false);
|
|
}, 400, this);
|
|
}
|
|
|
|
// Voicemail long press (only if first digit pressed)
|
|
if (key === '1' && this._phoneNumber === '') {
|
|
this._holdTimer = setTimeout(function vm_call(self) {
|
|
self._longPress = true;
|
|
self._callVoicemail();
|
|
|
|
self._phoneNumber = '';
|
|
self._updatePhoneNumberView('begin', false);
|
|
}, 400, this);
|
|
}
|
|
|
|
if (key == 'delete') {
|
|
this._deleteAtInsertPosition();
|
|
} else if (this.phoneNumberViewContainer.classList.
|
|
contains('keypad-visible')) {
|
|
|
|
if (!this._isKeypadClicked) {
|
|
this._isKeypadClicked = true;
|
|
this._phoneNumber = key;
|
|
this.replaceAdditionalContactInfo('');
|
|
} else {
|
|
this._insertAtCaret(key);
|
|
}
|
|
} else {
|
|
this._insertAtCaret(key);
|
|
}
|
|
|
|
// Per certification requirements abbreviated dialing codes need to be
|
|
// called immediately after the user enters the '#' key. This covers
|
|
// retrieving the device's IMEI codes as well as speed dialing.
|
|
if (key === '#' && !this._onCall) {
|
|
if (this._phoneNumber === '*#06#') {
|
|
this.multiSimActionButton.performAction();
|
|
return;
|
|
} else if (this._isSpeedDialNumber(this._phoneNumber)) {
|
|
// Remove the trailing '#' to get the index
|
|
this._handleSpeedDial(this._phoneNumber.slice(0, -1));
|
|
return;
|
|
}
|
|
}
|
|
|
|
setTimeout(function(self) {
|
|
self._updatePhoneNumberView('begin', false);
|
|
}, 0, this);
|
|
},
|
|
|
|
/**
|
|
* Function used to respond to touchmove events over the keypad. Stops playing
|
|
* the tone associated with the last pressed key and resets it if the target
|
|
* goes outside its area.
|
|
*
|
|
* @param {Object} touch Touch position object for this move.
|
|
*/
|
|
_touchMove: function kh_touchMove(touch) {
|
|
var target = document.elementFromPoint(touch.pageX, touch.pageY);
|
|
var key = (target && target.dataset) ? target.dataset.value : null;
|
|
|
|
if (key !== this._lastPressedKey || key === 'delete') {
|
|
this._stopDtmfTone();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Function used to respond to touchend events over the keypad. Stops playing
|
|
* tones and resets timers associated with the key press.
|
|
*
|
|
* @param {String} key The key over which the tap finished.
|
|
*/
|
|
_touchEnd: function kh_touchEnd(key) {
|
|
this._lastPressedKey = null;
|
|
|
|
if (key !== 'delete') {
|
|
this._stopDtmfTone();
|
|
}
|
|
|
|
if (this._keypadSoundIsEnabled) {
|
|
TonePlayer.stop();
|
|
}
|
|
|
|
this.restoreCaretPosition();
|
|
|
|
// If it was a long press our work is already done
|
|
if (this._longPress) {
|
|
this._longPress = false;
|
|
this._holdTimer = null;
|
|
return;
|
|
}
|
|
|
|
if (this._holdTimer) {
|
|
clearTimeout(this._holdTimer);
|
|
}
|
|
},
|
|
|
|
keyHandler: function kh_keyHandler(event) {
|
|
// Avoid the keys to get focus.
|
|
event.preventDefault();
|
|
|
|
// When long pressing on the voicemail button, if a menu pops up on top of
|
|
// the 1 button, a click will go through and target that button unless we
|
|
// preventDefault the contextmenu event.
|
|
if (event.type == 'contextmenu') {
|
|
return;
|
|
}
|
|
|
|
var key = event.target.dataset.value;
|
|
|
|
// We could receive this event from an element that
|
|
// doesn't have the dataset value. Got the last key
|
|
// pressed and assing this value to continue with the
|
|
// proccess.
|
|
if (!key) {
|
|
return;
|
|
}
|
|
|
|
event.stopPropagation();
|
|
|
|
switch (event.type) {
|
|
case 'touchstart':
|
|
if (this._lastPressedKey) {
|
|
return; // Ignore multi-taps
|
|
}
|
|
|
|
event.target.classList.add('active');
|
|
this._touchStart(key);
|
|
break;
|
|
case 'touchmove':
|
|
this._touchMove(event.touches[0]);
|
|
break;
|
|
case 'touchend':
|
|
case 'touchcancel':
|
|
if (key !== this._lastPressedKey) {
|
|
return; // Ignore multi-taps
|
|
}
|
|
|
|
event.target.classList.remove('active');
|
|
this._touchEnd(key);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns true if the number is a speed dial code as described in
|
|
* 3GPP TS 22.030 6.6.4. Speed dial codes are in the N(N)(N)# format.
|
|
*/
|
|
_isSpeedDialNumber: function(number) {
|
|
return !!number.match(/^[0-9][0-9]{0,2}\#$/);
|
|
},
|
|
|
|
/**
|
|
* Returns the telephony number corresponding to the specified index. Speed
|
|
* dial numbers are retrieved from the SIM contacts list and not from the
|
|
* regular contacts.
|
|
*
|
|
* @param {Integer} index The index of the speed dial number.
|
|
* @returns {Promise} A promise that resolves to the corresponding number.
|
|
*/
|
|
_getSpeedDialNumber: function(index) {
|
|
var self = this;
|
|
var cardIndex;
|
|
|
|
index--; // Speed dial indexes are 1-based
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
LazyLoader.load(['/shared/js/sim_settings_helper.js'], function() {
|
|
SimSettingsHelper.getCardIndexFrom('outgoingCall',
|
|
function(defaultCardIndex) {
|
|
if (defaultCardIndex == SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE) {
|
|
LazyLoader.load(['/shared/js/component_utils.js',
|
|
'/shared/elements/gaia_sim_picker/script.js'],
|
|
function() {
|
|
var simPicker = document.getElementById('sim-picker');
|
|
simPicker.getOrPick(defaultCardIndex, null,
|
|
function(pickedCardIndex) {
|
|
cardIndex = pickedCardIndex;
|
|
resolve();
|
|
});
|
|
});
|
|
} else {
|
|
cardIndex = defaultCardIndex;
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
}).then(function() {
|
|
return self._getSimContactsList(cardIndex).then(
|
|
function(simContactsList) {
|
|
if ((index >= 0) && (index < simContactsList.length)) {
|
|
return simContactsList[index].number;
|
|
} else {
|
|
return Promise.reject('noContactsWereFound');
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handles a speed dial number, replaces the phone number with the one
|
|
* at the specified index or displays an error message if none was found.
|
|
*
|
|
* @param {Integer} index The index of the speed dial number.
|
|
*/
|
|
_handleSpeedDial: function(index) {
|
|
var self = this;
|
|
|
|
this.updatePhoneNumber('', 'begin', false);
|
|
this._getSpeedDialNumber(+index).then(
|
|
function(number) {
|
|
self.updatePhoneNumber(number, 'begin', false);
|
|
}, function(error) {
|
|
/* Do not display an error message if the user explicitly
|
|
* cancelled the speed dial operation. */
|
|
if (error) {
|
|
ConfirmDialog.show(error, null, {
|
|
title: 'noContactsFoundDialogOk',
|
|
callback: ConfirmDialog.hide
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Creates an array of contacts populated using the ADN contacts retrieved
|
|
* from a SIM card. Every contact will contain only the ID and first
|
|
* telephone number and the array will be sorted by ID. This array is then
|
|
* suitable to be used to pick speed dial numbers.
|
|
*
|
|
* @param {Array} contacts An array of mozContact elements.
|
|
* @returns {Array} An array of telephone numbers / ID couples sorted by ID.
|
|
*/
|
|
_createSimContactList: function(contacts) {
|
|
var numbers = new Array(contacts.length);
|
|
|
|
for (var i = 0; i < contacts.length; i++) {
|
|
numbers[i] = {
|
|
id: contacts[i].id,
|
|
number: contacts[i].tel[0].value,
|
|
};
|
|
}
|
|
|
|
numbers.sort(function(a, b) {
|
|
if (a.id.length == b.id.length) {
|
|
return (a.id > b.id) ? 1 : 0;
|
|
} else {
|
|
return (a.id.length > b.id.length) ? 1 : 0;
|
|
}
|
|
});
|
|
|
|
return numbers;
|
|
},
|
|
|
|
/**
|
|
* Gets the SIM contacts list for the specified SIM card.
|
|
*
|
|
* @param {Integer} cardIndex The SIM card index.
|
|
* @returns {Promise} A promise that is resolved with the contacts list.
|
|
*/
|
|
_getSimContactsList: function(cardIndex) {
|
|
var self = this;
|
|
var canceled = false;
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
LazyLoader.load(['/shared/style/confirm.css',
|
|
'/shared/js/confirm.js',
|
|
document.getElementById('confirmation-message')],
|
|
function() {
|
|
var iccId = navigator.mozIccManager.iccIds[cardIndex];
|
|
var icc = navigator.mozIccManager.getIccById(iccId);
|
|
|
|
if (!icc) {
|
|
reject('noSimCardToLoadContactsFrom');
|
|
return;
|
|
}
|
|
|
|
var req = icc.readContacts('adn');
|
|
|
|
req.onsuccess = function(event) {
|
|
var adnContacts = event.target.result;
|
|
var contacts = self._createSimContactList(adnContacts);
|
|
|
|
if (!canceled) {
|
|
ConfirmDialog.hide();
|
|
}
|
|
|
|
resolve(contacts);
|
|
};
|
|
req.onerror = function(error) {
|
|
console.error('Could not retrieve the ADN contacts from SIM card ' +
|
|
cardIndex + ', got error ' + error.name);
|
|
reject('noContactsWereFound');
|
|
};
|
|
|
|
ConfirmDialog.show('loadingSimContacts', null, {
|
|
title: 'cancel',
|
|
callback: function() {
|
|
canceled = true;
|
|
ConfirmDialog.hide();
|
|
reject();
|
|
}
|
|
});
|
|
}
|
|
);
|
|
});
|
|
},
|
|
|
|
sanitizePhoneNumber: function(number) {
|
|
return number.replace(/\s+/g, '');
|
|
},
|
|
|
|
updatePhoneNumber: function kh_updatePhoneNumber(number, ellipsisSide,
|
|
forceMaxFontSize) {
|
|
number = this.sanitizePhoneNumber(number);
|
|
this._phoneNumber = number;
|
|
this._updatePhoneNumberView(ellipsisSide, forceMaxFontSize);
|
|
},
|
|
|
|
press: function(value) {
|
|
this._playDtmfTone(value);
|
|
TonePlayer.start(gTonesFrequencies[value], true);
|
|
setTimeout((function nextTick() {
|
|
TonePlayer.stop();
|
|
this._stopDtmfTone();
|
|
}).bind(this));
|
|
},
|
|
|
|
_updatePhoneNumberView: function kh_updatePhoneNumberview(ellipsisSide,
|
|
forceMaxFontSize) {
|
|
var phoneNumber = this._phoneNumber;
|
|
|
|
// If there are digits in the phone number, show the delete button
|
|
// and enable the add contact button
|
|
if (this._onCall) {
|
|
this.renderPhoneNumber(phoneNumber, ellipsisSide);
|
|
} else {
|
|
var visibility;
|
|
if (phoneNumber.length > 0) {
|
|
visibility = 'visible';
|
|
this.callBarAddContact.removeAttribute('disabled');
|
|
} else {
|
|
visibility = 'hidden';
|
|
this.callBarAddContact.setAttribute('disabled', 'disabled');
|
|
}
|
|
this.deleteButton.style.visibility = visibility;
|
|
this.phoneNumberView.blur(); // update avoiding caret flickering
|
|
this.phoneNumberView.value = phoneNumber;
|
|
|
|
FontSizeManager.adaptToSpace(
|
|
FontSizeManager.DIAL_PAD, this.phoneNumberView, forceMaxFontSize,
|
|
ellipsisSide);
|
|
}
|
|
|
|
if (this.onValueChanged) {
|
|
this.onValueChanged(this._phoneNumber);
|
|
}
|
|
},
|
|
|
|
renderPhoneNumber:
|
|
function kh_replacePhoneNumber(phoneNumber, ellipsisSide) {
|
|
if (this._onCall && CallsHandler.activeCall) {
|
|
CallsHandler.activeCall.
|
|
renderPhoneNumber({raw: phoneNumber} , ellipsisSide);
|
|
}
|
|
},
|
|
|
|
restorePhoneNumber:
|
|
function kh_restorePhoneNumber() {
|
|
if (this._onCall && CallsHandler.activeCall) {
|
|
CallsHandler.activeCall.restorePhoneNumber();
|
|
}
|
|
},
|
|
|
|
replaceAdditionalContactInfo:
|
|
function kh_replaceAdditionalContactInfo(additionalContactInfo) {
|
|
var call = CallsHandler.activeCall;
|
|
if (this._onCall && call) {
|
|
call.replaceAdditionalContactInfo(additionalContactInfo);
|
|
}
|
|
},
|
|
|
|
restoreAdditionalContactInfo: function kh_restoreAdditionalContactInfo() {
|
|
if (this._onCall && CallsHandler.activeCall) {
|
|
CallsHandler.activeCall.restoreAdditionalContactInfo();
|
|
}
|
|
},
|
|
|
|
_callVoicemail: function() {
|
|
if (navigator.mozIccManager.iccIds.length <= 1) {
|
|
this._callVoicemailForSim(0);
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
var key = 'ril.voicemail.defaultServiceId';
|
|
var req = navigator.mozSettings.createLock().get(key);
|
|
req.onsuccess = function() {
|
|
LazyLoader.load(['/shared/js/component_utils.js',
|
|
'/shared/elements/gaia_sim_picker/script.js'],
|
|
function() {
|
|
var simPicker = document.getElementById('sim-picker');
|
|
navigator.mozL10n.formatValue('voiceMail').then(string => {
|
|
simPicker.getOrPick(req.result[key], string,
|
|
self._callVoicemailForSim.bind(self));
|
|
});
|
|
});
|
|
};
|
|
},
|
|
|
|
_callVoicemailForSim: function(cardIndex) {
|
|
var settings = navigator.mozSettings;
|
|
if (!settings) {
|
|
return;
|
|
}
|
|
var transaction = settings.createLock();
|
|
var request = transaction.get('ril.iccInfo.mbdn');
|
|
request.onsuccess = (function() {
|
|
var numbers = request.result['ril.iccInfo.mbdn'];
|
|
var number;
|
|
if (typeof numbers == 'string') {
|
|
number = numbers;
|
|
} else {
|
|
number = numbers && numbers[cardIndex];
|
|
}
|
|
var voicemail = navigator.mozVoicemail;
|
|
if (!number && voicemail) {
|
|
number = voicemail.getNumber(cardIndex);
|
|
}
|
|
if (number) {
|
|
CallHandler.call(number, cardIndex);
|
|
} else {
|
|
this._showNoVoicemailDialog();
|
|
}
|
|
}).bind(this);
|
|
request.onerror = function() {};
|
|
},
|
|
|
|
_showNoVoicemailDialog: function hk_showNoVoicemailDialog() {
|
|
|
|
var voicemailDialog = {
|
|
title: 'voicemailNoNumberTitle',
|
|
text: 'voicemailNoNumberText',
|
|
confirm: {
|
|
title: 'voicemailNoNumberSettings',
|
|
recommend: true,
|
|
callback: this.showVoicemailSettings
|
|
},
|
|
cancel: {
|
|
title: 'voicemailNoNumberCancel',
|
|
callback: this._hideNoVoicemailDialog
|
|
}
|
|
};
|
|
|
|
LazyLoader.load(['/shared/js/custom_dialog.js'], function() {
|
|
CustomDialog.show(
|
|
voicemailDialog.title, voicemailDialog.text,
|
|
voicemailDialog.cancel, voicemailDialog.confirm);
|
|
});
|
|
},
|
|
|
|
_hideNoVoicemailDialog: function kh_hideNoVoicemailDialog() {
|
|
CustomDialog.hide();
|
|
},
|
|
|
|
showVoicemailSettings: function kh_showVoicemailSettings() {
|
|
var activity = new window.MozActivity({
|
|
name: 'configure',
|
|
data: {
|
|
target: 'device',
|
|
section: 'call'
|
|
}
|
|
});
|
|
|
|
activity.onerror = function() {
|
|
console.warn('Configure activity error:', activity.error.name);
|
|
};
|
|
},
|
|
|
|
_observePreferences: function kh_observePreferences() {
|
|
var self = this;
|
|
LazyLoader.load('/shared/js/settings_listener.js', function() {
|
|
SettingsListener.observe('phone.ring.keypad', false, function(value) {
|
|
self._keypadSoundIsEnabled = !!value;
|
|
});
|
|
|
|
SettingsListener.observe('phone.dtmf.type', 'long', function(value) {
|
|
self._shortTone = (value === 'short');
|
|
});
|
|
|
|
SettingsListener.observe('keyboard.vibration', false, function(value) {
|
|
self._vibrationEnabled = !!value;
|
|
});
|
|
});
|
|
},
|
|
|
|
_updateInsertPosition: function kh_updateInsertPosition() {
|
|
this._insertPosition = this._realStartPosition();
|
|
},
|
|
|
|
_replaceLastKey: function kh_replaceLastKey(newKey) {
|
|
this._deleteAtInsertPosition();
|
|
this.restoreCaretPosition();
|
|
this._insertAtCaret(newKey);
|
|
},
|
|
|
|
_insertAtCaret: function kh_insertAtCaret(key) {
|
|
if (this._insertPosition === null) {
|
|
this._phoneNumber += key;
|
|
} else {
|
|
var start = this._realStartPosition();
|
|
var end = this._realEndPosition();
|
|
this._phoneNumber = this._phoneNumber.substring(0, start) + key +
|
|
this._phoneNumber.substring(end);
|
|
this._insertPosition = start + 1;
|
|
}
|
|
},
|
|
|
|
_deleteAtInsertPosition: function kh_deleteAtInsertPosition() {
|
|
if (this._insertPosition === null) {
|
|
this._phoneNumber = this._phoneNumber.slice(0, -1);
|
|
} else {
|
|
var start = this._realStartPosition();
|
|
var end = this._realEndPosition();
|
|
if (start > 0 && start === end) {
|
|
start = end - 1;
|
|
}
|
|
this._phoneNumber = this._phoneNumber.substring(0, start) +
|
|
this._phoneNumber.substring(end);
|
|
this._insertPosition = this._phoneNumber ? start : null;
|
|
}
|
|
},
|
|
|
|
_clearPhoneNumber: function kh_clearPhoneNumber() {
|
|
if (this._insertPosition === null) {
|
|
this._phoneNumber = '';
|
|
} else {
|
|
var start = this._realStartPosition();
|
|
this._phoneNumber = this._phoneNumber.substring(start);
|
|
this._insertPosition = this._phoneNumber ? 0 : null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets the caret position inside the phone number input of the dialer
|
|
* according to the insert position (which is the real place where the caret
|
|
* should be inside the complete number) taking into account the number of
|
|
* ellipsed characters.
|
|
*/
|
|
restoreCaretPosition: function kh_restoreCaretPosition() {
|
|
if (this._insertPosition !== null) {
|
|
var caretPosition = this._caretPosition(this._insertPosition);
|
|
this.phoneNumberView.selectionStart = caretPosition;
|
|
this.phoneNumberView.selectionEnd = caretPosition;
|
|
this.phoneNumberView.focus();
|
|
} else {
|
|
this.phoneNumberView.blur();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Gets the real start index for a selection in the phone view considering
|
|
* the ellipsed characters.
|
|
*/
|
|
_realStartPosition: function kh_realStartPosition() {
|
|
var start = this.phoneNumberView.selectionStart;
|
|
return this._realPosition(this.phoneNumberView, start);
|
|
},
|
|
|
|
/**
|
|
* Gets the real end index for a selection in the phone view considering
|
|
* the ellipsed characters.
|
|
*/
|
|
_realEndPosition: function kh_realEndPosition() {
|
|
var end = this.phoneNumberView.selectionEnd;
|
|
return this._realPosition(this.phoneNumberView, end);
|
|
},
|
|
|
|
/**
|
|
* Gets the position where the cursor should be placed inside the phone
|
|
* view (considering the ellipsed scenaries) from the real edition point.
|
|
*/
|
|
_caretPosition: function kh_caretPosition(realPosition) {
|
|
return realPosition - this._ellipsisOffset(this.phoneNumberView);
|
|
},
|
|
|
|
/**
|
|
* Utility to calculate the real cursor position given an input with
|
|
* ellipsed characters and a position inside the current value.
|
|
*/
|
|
_realPosition: function kh_realPosition(input, position) {
|
|
return position + this._ellipsisOffset(this.phoneNumberView);
|
|
},
|
|
|
|
/**
|
|
* Calculates the amount of characters that are not visible in the current
|
|
* view. This includes all the ellipsed characters and the ellipsis
|
|
* character itself.
|
|
*/
|
|
_ellipsisOffset: function kh_ellipsisOffset(input) {
|
|
var ellipsedCharacters = +input.dataset.ellipsedCharacters;
|
|
return ellipsedCharacters ? (ellipsedCharacters - 1) : 0;
|
|
}
|
|
};
|