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
151
gaia/apps/emergency-call/index.html
Executable file
151
gaia/apps/emergency-call/index.html
Executable file
|
|
@ -0,0 +1,151 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<meta name="theme-group" content="theme-communications">
|
||||
<meta name="theme-color" content="var(--header-background)">
|
||||
<title>Emergency Call Dialer</title>
|
||||
|
||||
<!-- Localization -->
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="shared/locales/phone_types/phone_types.{locale}.properties">
|
||||
<link rel="localization" href="locales/emergency-call.{locale}.properties">
|
||||
<link rel="localization" href="shared/locales/keypad/keypad.{locale}.properties">
|
||||
<link rel="localization" href="shared/locales/telephony/telephony.{locale}.properties">
|
||||
<link rel="localization" href="shared/elements/gaia_menu/locales/gaia_menu.{locale}.properties">
|
||||
<link rel="localization" href="shared/elements/gaia_sim_picker/locales/gaia_sim_picker.{locale}.properties">
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/animations.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/dialer/dialer.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/dialer/keypad.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/action_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/contacts/contact_in_overlay.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/dialer/overlay.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/ice_contacts.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css">
|
||||
|
||||
<!-- JS -->
|
||||
<script defer type="application/javascript" src="/shared/js/contacts/utilities/ice_store.js"></script>
|
||||
<script defer type="application/javascript" src="/shared/js/dialer/utils.js"></script>
|
||||
<script defer type="application/javascript" src="/shared/js/dialer/dtmf_tone.js"></script>
|
||||
<script defer type="application/javascript" src="/shared/js/dialer/tone_player.js"></script>
|
||||
<script defer type="application/javascript" src="/shared/js/l10n.js"></script>
|
||||
<script defer type="application/javascript" src="/shared/js/lazy_loader.js"></script>
|
||||
<script defer type="application/javascript" src="/shared/js/settings_listener.js"></script>
|
||||
|
||||
<script defer type="application/javascript" src="js/ice_contacts.js"></script>
|
||||
<script defer type="application/javascript" src="js/keypad.js"></script>
|
||||
<script defer type="application/javascript" src="js/dialer.js"></script>
|
||||
|
||||
<script defer src="/shared/elements/config.js"></script>
|
||||
|
||||
<!-- Lazy load all the things:
|
||||
<link rel="stylesheet" type="text/css" href="/shared/style/confirm.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia_menu/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia_sim_picker/style.css">
|
||||
<script defer src="/shared/js/confirm.js"></script>
|
||||
<script defer src="/shared/js/sim_settings_helper.js"></script>
|
||||
<script defer src="/shared/js/dialer/telephony_messages.js"></script>
|
||||
<script defer src="/shared/js/component_utils.js"></script>
|
||||
<script defer src="/shared/elements/gaia_sim_picker/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia_menu/script.js"></script>
|
||||
-->
|
||||
|
||||
<link rel="import" href="/shared/elements/confirmation-message.html">
|
||||
<link rel="import" href="/shared/elements/contacts/contact_in_overlay.html">
|
||||
<link rel="import" href="/shared/elements/contacts/contact_list_overlay.html">
|
||||
</head>
|
||||
<body class="skin-comms theme-communications">
|
||||
<article id="views" class="full-height">
|
||||
<section id="keyboard-view" class="full-height">
|
||||
<div id="phone-number-view-container" dir="ltr">
|
||||
<div class="phone-number-view-wrapper-input">
|
||||
<input id="phone-number-view" type="text" class="phone-number-font font-light textfield" readonly="readonly" data-l10n-id="phoneNumberInput">
|
||||
<div id="fake-phone-number-view"></div>
|
||||
</div>
|
||||
<button role="key" id="keypad-delete" class="reset-button" data-value="delete" data-l10n-id="keypadDelete">
|
||||
</button>
|
||||
</div>
|
||||
<section id="ice-contacts-bar" class="ice-contacts-bar" hidden data-l10n-id="ice-contacts-bar-text">ICE Contacts - In Case of Emergency
|
||||
</section>
|
||||
<article id="keyboard-container" dir="ltr">
|
||||
<section id="keypad">
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="1">
|
||||
<span>1</span>
|
||||
<em class="keypad-subicon"></em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="2">
|
||||
<span>2</span>
|
||||
<em>ABC</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="3">
|
||||
<span>3</span>
|
||||
<em>DEF</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="4">
|
||||
<span>4</span>
|
||||
<em>GHI</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="5">
|
||||
<span>5</span>
|
||||
<em>JKL</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="6">
|
||||
<span>6</span>
|
||||
<em>MNO</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="7">
|
||||
<span>7</span>
|
||||
<em>PQRS</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="8">
|
||||
<span>8</span>
|
||||
<em>TUV</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="9">
|
||||
<span>9</span>
|
||||
<em>WXYZ</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="*" data-l10n-id="asterisk">
|
||||
<div class="asterisk">*</div>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="0">
|
||||
<span>0</span>
|
||||
<em class="font-size-plus">+</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-type="dial" data-value="#" data-l10n-id="hashSign">
|
||||
<div class="sharp">#</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="keypad-callbar">
|
||||
<span role="button" id="keypad-callbar-cancel" class='keypad-callbar-button cancel' data-type="action" data-value="cancel" >
|
||||
<div data-l10n-id="keypadCancel">Cancel</div>
|
||||
</span>
|
||||
<span role="button" id="keypad-callbar-call-action" data-type="action" data-value="make-call">
|
||||
<div>
|
||||
</div>
|
||||
</span>
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
</article>
|
||||
<form is="contact-list-overlay" id="contact-list-overlay" role="dialog" data-type="action" class="overlay">
|
||||
</form>
|
||||
<button is="contact-in-overlay" id="contact-in-overlay" class="contact-item ci--action-menu" hidden></button>
|
||||
<form is="confirmation-message" id="confirmation-message" class="hide no-opacity" role="dialog" data-type="confirm"></form>
|
||||
<gaia-sim-picker id="sim-picker" tabindex="-1"></gaia-sim-picker>
|
||||
</body>
|
||||
</html>
|
||||
90
gaia/apps/emergency-call/js/dialer.js
Executable file
90
gaia/apps/emergency-call/js/dialer.js
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
|
||||
/* globals KeypadManager, ICEContacts, LazyLoader, SimSettingsHelper,
|
||||
TelephonyMessages */
|
||||
/* exported CallHandler */
|
||||
|
||||
var CallHandler = {
|
||||
_telephony: window.navigator.mozTelephony,
|
||||
|
||||
call: function(number) {
|
||||
var sanitizedNumber = number.replace(/-/g, ''),
|
||||
self = this;
|
||||
|
||||
if (ICEContacts.isFromICEContact(number)) {
|
||||
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, number, function(ci) {
|
||||
var callPromise = self._telephony.dial(number, ci);
|
||||
self._handleCallPromise(callPromise, sanitizedNumber);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var callPromise = self._telephony.dial(number, defaultCardIndex);
|
||||
self._handleCallPromise(callPromise, sanitizedNumber);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var callPromise = self._telephony.dialEmergency(sanitizedNumber);
|
||||
self._handleCallPromise(callPromise, sanitizedNumber);
|
||||
}
|
||||
},
|
||||
|
||||
_handleCallPromise: function(callPromise, sanitizedNumber) {
|
||||
var self = this;
|
||||
|
||||
callPromise.then(function(call) {
|
||||
self._installHandlers(call, sanitizedNumber);
|
||||
}).catch(function(errorName) {
|
||||
LazyLoader.load(['/shared/js/dialer/telephony_messages.js'], function() {
|
||||
TelephonyMessages.handleError(
|
||||
errorName, sanitizedNumber, TelephonyMessages.EMERGENCY_ONLY);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_installHandlers: function(call, number) {
|
||||
function clearPhoneView() {
|
||||
KeypadManager.updatePhoneNumber('');
|
||||
}
|
||||
|
||||
if (call) {
|
||||
call.onconnected = clearPhoneView;
|
||||
call.ondisconnected = function callEnded(evt) {
|
||||
LazyLoader.load(['/shared/js/dialer/telephony_messages.js'],
|
||||
function() {
|
||||
TelephonyMessages.handleDisconnect(
|
||||
evt.call.disconnectedReason, number,
|
||||
TelephonyMessages.EMERGENCY_ONLY
|
||||
);
|
||||
});
|
||||
clearPhoneView();
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* CallHandler
|
||||
* @global
|
||||
*/
|
||||
window.CallHandler = CallHandler;
|
||||
|
||||
window.addEventListener('load', function onload() {
|
||||
/* Tell the audio channel manager that we want to adjust the "notification"
|
||||
* channel when the user presses the volumeup/volumedown buttons. */
|
||||
if (navigator.mozAudioChannelManager) {
|
||||
navigator.mozAudioChannelManager.volumeControlChannel = 'notification';
|
||||
}
|
||||
|
||||
window.removeEventListener('load', onload);
|
||||
window.ICEContacts.updateICEContacts();
|
||||
window.KeypadManager.init(/* oncall */ false);
|
||||
});
|
||||
145
gaia/apps/emergency-call/js/ice_contacts.js
Executable file
145
gaia/apps/emergency-call/js/ice_contacts.js
Executable file
|
|
@ -0,0 +1,145 @@
|
|||
/* globals CallHandler, ICEStore, LazyLoader */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
var iceContactsDetails = [],
|
||||
iceContactsBar,
|
||||
contactListOverlay,
|
||||
contactInOverlay,
|
||||
contactList,
|
||||
contactListCancel;
|
||||
|
||||
function init() {
|
||||
if (ICEContacts._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
iceContactsBar = document.getElementById('ice-contacts-bar');
|
||||
contactListOverlay = document.getElementById('contact-list-overlay');
|
||||
contactInOverlay = document.getElementById('contact-in-overlay');
|
||||
|
||||
LazyLoader.load([contactListOverlay], function() {
|
||||
var contactListOverlayHeader = contactListOverlay.querySelector('header');
|
||||
contactListOverlayHeader.dataset.l10nId = 'ice-contacts-overlay-title';
|
||||
|
||||
contactList = document.getElementById('contact-list');
|
||||
contactListCancel =
|
||||
document.getElementById('contact-list-overlay-cancel');
|
||||
|
||||
iceContactsBar.addEventListener('click', showICEContactOverlay);
|
||||
contactListCancel.addEventListener('click', hideICEContactOverlay);
|
||||
|
||||
ICEContacts._initialized = true;
|
||||
});
|
||||
}
|
||||
|
||||
function showICEContactsBar() {
|
||||
iceContactsBar.removeAttribute('hidden');
|
||||
}
|
||||
|
||||
function showICEContactOverlay() {
|
||||
contactListOverlay.classList.add('display');
|
||||
}
|
||||
|
||||
function hideICEContactOverlay() {
|
||||
contactListOverlay.classList.remove('display');
|
||||
}
|
||||
|
||||
function callICEContact(number) {
|
||||
hideICEContactOverlay();
|
||||
CallHandler.call(number);
|
||||
}
|
||||
|
||||
function findContact(iceContact) {
|
||||
return new Promise(function(resolve) {
|
||||
var contactFilter = {
|
||||
filterBy: ['id'],
|
||||
filterValue: iceContact,
|
||||
filterOp: 'equals',
|
||||
filterLimit: 1
|
||||
};
|
||||
var contactRequest = navigator.mozContacts.find(contactFilter);
|
||||
contactRequest.onsuccess = function() {
|
||||
var contact = this.result[0];
|
||||
if (contact && contact.tel) {
|
||||
iceContactsDetails.push(contact);
|
||||
}
|
||||
resolve(contact);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function addContactToOverlay(contact) {
|
||||
return new Promise(function (resolve) {
|
||||
if (!contact || !contact.tel) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
contact.tel.forEach(function(tel) {
|
||||
var iceContactOverlayEntry = contactInOverlay.cloneNode(true);
|
||||
iceContactOverlayEntry.removeAttribute('id');
|
||||
iceContactOverlayEntry.removeAttribute('hidden');
|
||||
iceContactOverlayEntry.querySelector('.js-name').textContent =
|
||||
contact.name[0];
|
||||
iceContactOverlayEntry.querySelector('.js-tel-type').dataset.l10nId =
|
||||
tel.type[0];
|
||||
iceContactOverlayEntry.querySelector('.js-tel').textContent =
|
||||
tel.value;
|
||||
contactList.insertBefore(iceContactOverlayEntry, contactListCancel);
|
||||
iceContactOverlayEntry.addEventListener('click',
|
||||
callICEContact.bind(null, tel.value));
|
||||
// Set the ICE contacts bar visible as soon as there is some
|
||||
// ICE contact to call.
|
||||
showICEContactsBar();
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ICE contacts, show the ICE contacts bar if appropriate and loads
|
||||
* the ICE contacts on the overlay for future calling.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function updateICEContacts() {
|
||||
init();
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
LazyLoader.load([contactInOverlay], function() {
|
||||
ICEStore.getContacts().then(function(iceContacts) {
|
||||
if (!iceContacts || !iceContacts.length) {
|
||||
resolve();
|
||||
} else {
|
||||
var promises = [];
|
||||
iceContacts.forEach(function (iceContact) {
|
||||
promises.push(findContact(iceContact).then(addContactToOverlay));
|
||||
});
|
||||
Promise.all(promises).then(resolve);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Preliminary version of this function which checks if a telephone number
|
||||
* belongs to an ICE contact. A more advanced version of which should
|
||||
* probably consider phone number variants.
|
||||
*/
|
||||
function isFromICEContact(number) {
|
||||
return iceContactsDetails.some(function(iceContact) {
|
||||
return iceContact.tel.some(function(tel) {
|
||||
return number == tel.value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var ICEContacts = {
|
||||
_initialized: false,
|
||||
updateICEContacts: updateICEContacts,
|
||||
isFromICEContact: isFromICEContact
|
||||
};
|
||||
|
||||
exports.ICEContacts = ICEContacts;
|
||||
})(window);
|
||||
684
gaia/apps/emergency-call/js/keypad.js
Executable file
684
gaia/apps/emergency-call/js/keypad.js
Executable file
|
|
@ -0,0 +1,684 @@
|
|||
/* globals DtmfTone, LazyLoader, SettingsListener, telephony, TonePlayer */
|
||||
|
||||
'use strict';
|
||||
|
||||
var kFontStep = 4;
|
||||
var minFontSize = 12;
|
||||
|
||||
// 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 = {
|
||||
_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,
|
||||
|
||||
get phoneNumberView() {
|
||||
delete this.phoneNumberView;
|
||||
this.phoneNumberView = document.getElementById('phone-number-view');
|
||||
return this.phoneNumberView;
|
||||
},
|
||||
|
||||
get fakePhoneNumberView() {
|
||||
delete this.fakePhoneNumberView;
|
||||
this.fakePhoneNumberView =
|
||||
document.getElementById('fake-phone-number-view');
|
||||
return this.fakePhoneNumberView;
|
||||
},
|
||||
|
||||
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.hideBarHangAction;
|
||||
},
|
||||
|
||||
init: function kh_init(oncall) {
|
||||
|
||||
this._onCall = !!oncall;
|
||||
this._lastPressedKey = null;
|
||||
|
||||
// Update the minimum phone number phone size.
|
||||
// The UX team states that the minimum font size should be
|
||||
// 10pt. First off, we convert it to px multiplying it 0.226 times,
|
||||
// then we convert it to rem multiplying it a number of times equal
|
||||
// to the font-size property of the body element.
|
||||
var defaultFontSize = window.getComputedStyle(document.body, null)
|
||||
.getPropertyValue('font-size');
|
||||
minFontSize = parseInt(parseInt(defaultFontSize) * 10 * 0.226);
|
||||
|
||||
this.phoneNumberView.value = '';
|
||||
this.phoneNumberView
|
||||
.addEventListener('click', this._updateInsertPosition.bind(this));
|
||||
this._phoneNumber = '';
|
||||
|
||||
var keyHandler = this.keyHandler.bind(this);
|
||||
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('mouseup',
|
||||
this.addContact.bind(this));
|
||||
}
|
||||
|
||||
// The keypad add contact bar is only included in the normal version and
|
||||
// the emergency call version of the keypad.
|
||||
if (this.callBarCallAction) {
|
||||
this.callBarCallAction.addEventListener('mouseup',
|
||||
this.makeCall.bind(this));
|
||||
}
|
||||
|
||||
// The keypad cancel bar is only the emergency call version of the keypad.
|
||||
if (this.callBarCancelAction) {
|
||||
this.callBarCancelAction.addEventListener('mouseup', function() {
|
||||
window.close();
|
||||
});
|
||||
}
|
||||
|
||||
// The keypad hide bar is only included in the on call version of the
|
||||
// keypad.
|
||||
if (this.hideBarHideAction) {
|
||||
this.hideBarHideAction.addEventListener('mouseup',
|
||||
this.callbarBackAction);
|
||||
}
|
||||
|
||||
if (this.hideBarHangUpAction) {
|
||||
this.hideBarHangUpAction.addEventListener('mouseup',
|
||||
this.hangUpCallFromKeypad);
|
||||
}
|
||||
|
||||
TonePlayer.init('system');
|
||||
|
||||
this.render();
|
||||
|
||||
this._observePreferences();
|
||||
},
|
||||
|
||||
render: function hk_render(layoutType) {
|
||||
if (layoutType == 'oncall') {
|
||||
this._onCall = true;
|
||||
var numberNode =
|
||||
window.CallScreen.activeCall.querySelector('.number');
|
||||
this._phoneNumber = numberNode.textContent;
|
||||
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;
|
||||
},
|
||||
|
||||
makeCall: function hk_makeCall(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
if (this._phoneNumber !== '') {
|
||||
window.CallHandler.call(KeypadManager._phoneNumber);
|
||||
}
|
||||
},
|
||||
|
||||
addContact: function hk_addContact(event) {
|
||||
var number = this._phoneNumber;
|
||||
if (!number) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var activity = new window.MozActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: {
|
||||
'tel': number
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// To prevent the bad habit that new a variable
|
||||
// but not use it.
|
||||
activity.onsuccess = function() {};
|
||||
activity.onerror = function() {};
|
||||
} catch (e) {
|
||||
console.log('WebActivities unavailable? : ' + e);
|
||||
}
|
||||
},
|
||||
|
||||
callbarBackAction: function hk_callbarBackAction(event) {
|
||||
window.CallScreen.hideKeypad();
|
||||
},
|
||||
|
||||
hangUpCallFromKeypad: function hk_hangUpCallFromKeypad(event) {
|
||||
window.CallScreen.views.classList.remove('show');
|
||||
window.OnCallHandler.end();
|
||||
},
|
||||
|
||||
formatPhoneNumber: function kh_formatPhoneNumber(mode) {
|
||||
var fakeView = null,
|
||||
view = null;
|
||||
switch (mode) {
|
||||
case 'dialpad':
|
||||
fakeView = this.fakePhoneNumberView;
|
||||
view = this.phoneNumberView;
|
||||
|
||||
// We consider the case where the delete button may have
|
||||
// been used to delete the whole phone number.
|
||||
if (view.value === '') {
|
||||
view.style.fontSize = view.dataset.size;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'on-call':
|
||||
fakeView =
|
||||
window.CallScreen.activeCall.querySelector('.fake-number');
|
||||
view =
|
||||
window.CallScreen.activeCall.querySelector('.number');
|
||||
break;
|
||||
}
|
||||
|
||||
var computedStyle = window.getComputedStyle(view, null);
|
||||
var currentFontSize = computedStyle.getPropertyValue('font-size');
|
||||
if (!('size' in view.dataset)) {
|
||||
view.dataset.size = currentFontSize;
|
||||
}
|
||||
|
||||
var newFontSize = this.getNextFontSize(view, fakeView,
|
||||
parseInt(view.dataset.size),
|
||||
parseInt(currentFontSize));
|
||||
view.style.fontSize = newFontSize + 'px';
|
||||
this.addEllipsis(view, fakeView, newFontSize);
|
||||
},
|
||||
|
||||
addEllipsis: function kh_addEllipsis(view, fakeView, currentFontSize) {
|
||||
var viewWidth = view.getBoundingClientRect().width;
|
||||
fakeView.style.fontSize = currentFontSize + 'px';
|
||||
fakeView.textContent = view.value;
|
||||
|
||||
var counter = 1;
|
||||
var value = view.value;
|
||||
|
||||
var newPhoneNumber;
|
||||
while (fakeView.getBoundingClientRect().width > viewWidth) {
|
||||
newPhoneNumber = '\u2026' + value.substr(-value.length + counter);
|
||||
fakeView.textContent = newPhoneNumber;
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (newPhoneNumber) {
|
||||
view.value = newPhoneNumber;
|
||||
}
|
||||
},
|
||||
|
||||
getNextFontSize: function kh_getNextFontSize(view, fakeView,
|
||||
fontSize, initialFontSize) {
|
||||
var viewWidth = view.getBoundingClientRect().width;
|
||||
fakeView.style.fontSize = fontSize + 'px';
|
||||
fakeView.textContent = view.value;
|
||||
|
||||
var rect = fakeView.getBoundingClientRect();
|
||||
while ((rect.width > viewWidth) && (fontSize > minFontSize)) {
|
||||
fontSize = Math.max(fontSize - kFontStep, minFontSize);
|
||||
fakeView.style.fontSize = fontSize + 'px';
|
||||
rect = fakeView.getBoundingClientRect();
|
||||
}
|
||||
|
||||
if ((rect.width < viewWidth) && (fontSize < initialFontSize)) {
|
||||
fakeView.style.fontSize = (fontSize + kFontStep) + 'px';
|
||||
rect = fakeView.getBoundingClientRect();
|
||||
if (rect.width <= viewWidth) {
|
||||
fontSize += kFontStep;
|
||||
}
|
||||
}
|
||||
return fontSize;
|
||||
},
|
||||
|
||||
_lastPressedKey: null,
|
||||
_dtmfTone: null,
|
||||
|
||||
_playDtmfTone: function kh_playDtmfTone(key) {
|
||||
var serviceId = 0;
|
||||
|
||||
if (!this._onCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (telephony.active) {
|
||||
// Single call
|
||||
serviceId = telephony.active.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 (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' && !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();
|
||||
}, 400, this);
|
||||
}
|
||||
|
||||
if (key == 'delete') {
|
||||
this._deleteAtInsertPosition();
|
||||
} else if (this.phoneNumberViewContainer.classList.
|
||||
contains('keypad-visible')) {
|
||||
|
||||
if (!this._isKeypadClicked) {
|
||||
this._isKeypadClicked = true;
|
||||
this._phoneNumber = key;
|
||||
} else {
|
||||
this._insertAtCaret(key);
|
||||
}
|
||||
} else {
|
||||
this._insertAtCaret(key);
|
||||
}
|
||||
|
||||
setTimeout(function(self) {
|
||||
self._updatePhoneNumberView();
|
||||
}, 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.dataset ? target.dataset.value : null;
|
||||
|
||||
if (key !== this._lastPressedKey || key === 'delete') {
|
||||
this._stopDtmfTone();
|
||||
this._lastPressedKey = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
this.restoreCaretPosition();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._holdTimer) {
|
||||
clearTimeout(this._holdTimer);
|
||||
}
|
||||
},
|
||||
|
||||
keyHandler: function kh_keyHandler(event) {
|
||||
// Avoid the keys to get focus.
|
||||
event.preventDefault();
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
updatePhoneNumber: function kh_updatePhoneNumber(number) {
|
||||
this._phoneNumber = number;
|
||||
this._updatePhoneNumberView();
|
||||
},
|
||||
|
||||
_updatePhoneNumberView: function kh_updatePhoneNumberview() {
|
||||
var phoneNumber = this._phoneNumber;
|
||||
|
||||
// If there are digits in the phone number, show the delete button.
|
||||
var visibility = (phoneNumber.length > 0) ? 'visible' : 'hidden';
|
||||
this.deleteButton.style.visibility = visibility;
|
||||
this.phoneNumberView.blur();
|
||||
|
||||
if (this._onCall) {
|
||||
var view =
|
||||
window.CallScreen.activeCall.querySelector('.number');
|
||||
view.textContent = phoneNumber;
|
||||
this.formatPhoneNumber('on-call');
|
||||
} else {
|
||||
this.phoneNumberView.value = phoneNumber;
|
||||
this.formatPhoneNumber('dialpad');
|
||||
}
|
||||
},
|
||||
|
||||
_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();
|
||||
},
|
||||
|
||||
_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;
|
||||
},
|
||||
|
||||
_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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
navigator.mozL10n && navigator.mozL10n.once(function onL10nInit() {
|
||||
// <body> children are hidden until the UI is translated
|
||||
document.body.classList.remove('hidden');
|
||||
});
|
||||
18
gaia/apps/emergency-call/jsdoc.json
Executable file
18
gaia/apps/emergency-call/jsdoc.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "emergency-call",
|
||||
"tags": {
|
||||
"dictionaries": ["jsdoc"]
|
||||
},
|
||||
"source": {
|
||||
"include": ["apps/emergency-call/js/**/*.js"]
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"plugins/summarize"
|
||||
],
|
||||
"opts": {
|
||||
"private": true,
|
||||
"recurse": true,
|
||||
"destination": "docs/emergency-call"
|
||||
}
|
||||
}
|
||||
8
gaia/apps/emergency-call/locales/emergency-call.en-US.properties
Executable file
8
gaia/apps/emergency-call/locales/emergency-call.en-US.properties
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
# emergency call on lockscreen
|
||||
error=Error
|
||||
ok=OK
|
||||
cancel=Cancel
|
||||
emergency-call-only=Emergency Call Only
|
||||
emergency-call-error={{ number }} is not an emergency number.
|
||||
ice-contacts-bar-text=ICE Contacts - In Case of Emergency
|
||||
ice-contacts-overlay-title=ICE Contacts
|
||||
48
gaia/apps/emergency-call/manifest.webapp
Executable file
48
gaia/apps/emergency-call/manifest.webapp
Executable file
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "EmergencyCall",
|
||||
"description": "The emergency call pad",
|
||||
"type": "certified",
|
||||
"role": "system",
|
||||
"launch_path": "/index.html",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"orientation": "default",
|
||||
"fullscreen": true,
|
||||
"permissions": {
|
||||
"audio-channel-notification": {},
|
||||
"audio-channel-system": {},
|
||||
"themeable": {},
|
||||
"telephony": {},
|
||||
"mobileconnection": {},
|
||||
"phonenumberservice": {},
|
||||
"settings": {
|
||||
"access": "readonly"
|
||||
},
|
||||
"contacts": {
|
||||
"access": "readonly"
|
||||
},
|
||||
"moz-extremely-unstable-and-will-change-webcomponents": {}
|
||||
},
|
||||
"datastores-access": {
|
||||
"ice_contacts": {
|
||||
"readonly": true,
|
||||
"description": "Contains references to in case of emergency contacts"
|
||||
}
|
||||
},
|
||||
"locales": {
|
||||
"en-US": {
|
||||
"name": "EmergencyCall",
|
||||
"description": "Web app template"
|
||||
},
|
||||
"fr": {
|
||||
"name": "Appel d’urgence",
|
||||
"description": "Modèle d’application web"
|
||||
}
|
||||
},
|
||||
"default_locale": "en-US",
|
||||
"icons": {
|
||||
"126": "/style/icons/dialer_126.png"
|
||||
}
|
||||
}
|
||||
10
gaia/apps/emergency-call/package.json
Executable file
10
gaia/apps/emergency-call/package.json
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"webide": {
|
||||
"prepackage": {
|
||||
"command": "make",
|
||||
"env": ["APP=emergency-call"],
|
||||
"cwd": "../.."
|
||||
},
|
||||
"packageDir": "../../build_stage/emergency-call/"
|
||||
}
|
||||
}
|
||||
30
gaia/apps/emergency-call/style/ice_contacts.css
Executable file
30
gaia/apps/emergency-call/style/ice_contacts.css
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-opacity {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ice-contacts-bar {
|
||||
margin-top: -4.5rem;
|
||||
height: 4.5rem;
|
||||
font-size: 1.6rem;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
background-color: #e2443a;
|
||||
color: #FFF;
|
||||
line-height: 4.5rem;
|
||||
padding: 0 1.6rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.ice-contacts-bar:after {
|
||||
font-family: 'gaia-icons';
|
||||
font-style: normal;
|
||||
font-size: 3.7rem;
|
||||
line-height: 4.5rem;
|
||||
position: absolute;
|
||||
offset-inline-end: 0.2rem;
|
||||
}
|
||||
html[dir="ltr"] .ice-contacts-bar:after { content: 'right-light'; }
|
||||
html[dir="rtl"] .ice-contacts-bar:after { content: 'left-light'; }
|
||||
BIN
gaia/apps/emergency-call/style/icons/dialer_126.png
Executable file
BIN
gaia/apps/emergency-call/style/icons/dialer_126.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
27
gaia/apps/emergency-call/test/build/integration/emergency-call_test.js
Executable file
27
gaia/apps/emergency-call/test/build/integration/emergency-call_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Emergency-call tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=emergency-call make', function(done) {
|
||||
helper.exec('APP=emergency-call make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/emergency-call.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
17
gaia/apps/emergency-call/test/unit/.jshintrc
Executable file
17
gaia/apps/emergency-call/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
174
gaia/apps/emergency-call/test/unit/dialer_test.js
Executable file
174
gaia/apps/emergency-call/test/unit/dialer_test.js
Executable file
|
|
@ -0,0 +1,174 @@
|
|||
'use strict';
|
||||
|
||||
/* global CallHandler, CustomElementsHelper, KeypadManager, MockICEContacts,
|
||||
MockNavigatorMozTelephony, MocksHelper, MockSimSettingsHelper,
|
||||
MockTelephonyMessages, Promise */
|
||||
|
||||
require('/test/unit/mock_keypad.js');
|
||||
require('/test/unit/mock_ice_contacts.js');
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
require('/shared/test/unit/mocks/mock_sim_settings_helper.js');
|
||||
require('/shared/test/unit/mocks/dialer/mock_telephony_messages.js');
|
||||
require(
|
||||
'/shared/test/unit/mocks/elements/gaia_sim_picker/mock_gaia_sim_picker.js');
|
||||
|
||||
require('/js/dialer.js');
|
||||
|
||||
var mocksHelperForDialer = new MocksHelper([
|
||||
'GaiaSimPicker',
|
||||
'KeypadManager',
|
||||
'ICEContacts',
|
||||
'LazyLoader',
|
||||
'SimSettingsHelper',
|
||||
'TelephonyMessages'
|
||||
]).init();
|
||||
|
||||
var customElementsForDialer = new CustomElementsHelper([
|
||||
'GaiaSimPicker'
|
||||
]);
|
||||
|
||||
suite('Emergency Dialer', function() {
|
||||
var realMozTelephony;
|
||||
|
||||
var num = '123';
|
||||
var mockCall;
|
||||
var mockPromise;
|
||||
|
||||
var simPicker;
|
||||
|
||||
mocksHelperForDialer.attachTestHelpers();
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozTelephony = navigator.mozTelephony;
|
||||
navigator.mozTelephony = MockNavigatorMozTelephony;
|
||||
|
||||
CallHandler._telephony = MockNavigatorMozTelephony;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozTelephony = realMozTelephony;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
simPicker = document.createElement('gaia-sim-picker');
|
||||
simPicker.id = 'sim-picker';
|
||||
document.body.appendChild(simPicker);
|
||||
customElementsForDialer.resolve();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.body.removeChild(simPicker);
|
||||
});
|
||||
|
||||
suite('> Telephony API', function() {
|
||||
setup(function() {
|
||||
mockCall = {};
|
||||
mockPromise = Promise.resolve(mockCall);
|
||||
this.sinon.stub(navigator.mozTelephony, 'dialEmergency')
|
||||
.returns(mockPromise);
|
||||
CallHandler.call(num);
|
||||
});
|
||||
|
||||
test('> calls dialEmergency', function() {
|
||||
sinon.assert.calledWith(navigator.mozTelephony.dialEmergency, num);
|
||||
});
|
||||
|
||||
test('> installs onconnected handler', function(done) {
|
||||
mockPromise.then(function() {
|
||||
assert.isFunction(mockCall.onconnected);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('> installs ondisconnected handler', function(done) {
|
||||
mockPromise.then(function() {
|
||||
assert.isFunction(mockCall.ondisconnected);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('> clears the keypad on connected', function(done) {
|
||||
this.sinon.spy(KeypadManager, 'updatePhoneNumber');
|
||||
mockPromise.then(function() {
|
||||
mockCall.onconnected();
|
||||
sinon.assert.calledWith(KeypadManager.updatePhoneNumber, '');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('> clears the keypad on disconnected', function(done) {
|
||||
this.sinon.spy(KeypadManager, 'updatePhoneNumber');
|
||||
mockPromise.then(function() {
|
||||
mockCall.ondisconnected({
|
||||
call: { disconnectedReason: 'NormalCallClearing' }
|
||||
});
|
||||
sinon.assert.calledWith(KeypadManager.updatePhoneNumber, '');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('> displays an appropriate message on disconnected', function(done) {
|
||||
this.sinon.spy(MockTelephonyMessages, 'handleDisconnect');
|
||||
mockPromise.then(function() {
|
||||
mockCall.ondisconnected({
|
||||
call: { disconnectedReason: 'NormalCallClearing' }
|
||||
});
|
||||
sinon.assert.calledOnce(MockTelephonyMessages.handleDisconnect);
|
||||
sinon.assert.calledWith(
|
||||
MockTelephonyMessages.handleDisconnect, 'NormalCallClearing', num
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('> Dialing ICE Contacts Behavior', function() {
|
||||
setup(function() {
|
||||
mockCall = {};
|
||||
mockPromise = Promise.resolve(mockCall);
|
||||
this.sinon.stub(navigator.mozTelephony, 'dialEmergency')
|
||||
.returns(mockPromise);
|
||||
this.sinon.stub(navigator.mozTelephony, 'dial').returns(mockPromise);
|
||||
this.sinon.stub(MockICEContacts, 'isFromICEContact');
|
||||
});
|
||||
|
||||
test('> dials when default outgoing SIM "always ask" and SIM selected',
|
||||
function() {
|
||||
MockSimSettingsHelper._defaultCards.outgoingCall =
|
||||
MockSimSettingsHelper.ALWAYS_ASK_OPTION_VALUE;
|
||||
this.sinon.stub(simPicker, 'getOrPick');
|
||||
MockICEContacts.isFromICEContact.returns(true);
|
||||
|
||||
CallHandler.call(num);
|
||||
simPicker.getOrPick.yield(0);
|
||||
sinon.assert.calledWith(navigator.mozTelephony.dial, num);
|
||||
sinon.assert.calledWith(simPicker.getOrPick,
|
||||
MockSimSettingsHelper.ALWAYS_ASK_OPTION_VALUE,
|
||||
num);
|
||||
});
|
||||
|
||||
[0, 1].forEach(function(cardIndex) {
|
||||
test('> calls dial when ICE dialed with SIM ' + cardIndex, function() {
|
||||
MockSimSettingsHelper._defaultCards.outgoingCall = cardIndex;
|
||||
MockICEContacts.isFromICEContact.returns(true);
|
||||
|
||||
CallHandler.call(num);
|
||||
sinon.assert.calledWith(navigator.mozTelephony.dial, num, cardIndex);
|
||||
});
|
||||
});
|
||||
|
||||
test('> calls dialEmergency when non-ICE Contact is dialed', function() {
|
||||
MockICEContacts.isFromICEContact.returns(false);
|
||||
|
||||
CallHandler.call(num);
|
||||
sinon.assert.calledWith(navigator.mozTelephony.dialEmergency, num);
|
||||
});
|
||||
|
||||
test('> SimPicker NOT called when Emergency number is dialed',
|
||||
function() {
|
||||
MockICEContacts.isFromICEContact.returns(false);
|
||||
this.sinon.spy(simPicker, 'getOrPick');
|
||||
|
||||
CallHandler.call(num);
|
||||
sinon.assert.calledOnce(navigator.mozTelephony.dialEmergency);
|
||||
sinon.assert.notCalled(simPicker.getOrPick);
|
||||
});
|
||||
});
|
||||
});
|
||||
458
gaia/apps/emergency-call/test/unit/ice_contacts_test.js
Executable file
458
gaia/apps/emergency-call/test/unit/ice_contacts_test.js
Executable file
|
|
@ -0,0 +1,458 @@
|
|||
/* globals ICEContacts, ICEStore, MocksHelper, mozContact, MockMozContacts,
|
||||
MockLazyLoader */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
require('/shared/test/unit/mocks/mock_mozContacts.js');
|
||||
require('/shared/js/dialer/utils.js');
|
||||
require('/shared/test/unit/mocks/mock_ice_store.js');
|
||||
require('/test/unit/mock_call_handler.js');
|
||||
require('/js/ice_contacts.js');
|
||||
|
||||
var mocksHelperForDialer = new MocksHelper([
|
||||
'LazyLoader',
|
||||
'ICEStore',
|
||||
'CallHandler'
|
||||
]).init();
|
||||
|
||||
suite('ICE contacts bar', function() {
|
||||
var realMozContacts;
|
||||
var container;
|
||||
var iceContactBar;
|
||||
var iceContactsOverlay;
|
||||
var iceContactList;
|
||||
|
||||
mocksHelperForDialer.attachTestHelpers();
|
||||
|
||||
function createDOM() {
|
||||
document.body.innerHTML = '';
|
||||
|
||||
container = document.createElement('div');
|
||||
iceContactBar = document.createElement('section');
|
||||
iceContactBar.id = 'ice-contacts-bar';
|
||||
iceContactBar.class = 'ice-contacts-bar';
|
||||
iceContactBar.setAttribute('hidden', '');
|
||||
iceContactBar.textContent = 'ICE Contacts - In Case of Emergency';
|
||||
container.appendChild(iceContactBar);
|
||||
|
||||
loadBodyHTML('/shared/elements/contacts/contact_list_overlay.html');
|
||||
var iceContactsOverlayTemplate = document.body.querySelector('template');
|
||||
iceContactsOverlay = document.createElement('form');
|
||||
iceContactsOverlay.id = 'contact-list-overlay';
|
||||
iceContactsOverlay.setAttribute('role', 'dialog');
|
||||
iceContactsOverlay.setAttribute('data-type', 'action');
|
||||
iceContactsOverlay.classList.add('overlay');
|
||||
iceContactsOverlay.appendChild(
|
||||
iceContactsOverlayTemplate.content.cloneNode(true));
|
||||
container.appendChild(iceContactsOverlay);
|
||||
|
||||
loadBodyHTML('/shared/elements/contacts/contact_in_overlay.html');
|
||||
var iceContactsOverlayItemTemplate =
|
||||
document.body.querySelector('template');
|
||||
var iceContactsOverlayItem = document.createElement('button');
|
||||
iceContactsOverlayItem.id = 'contact-in-overlay';
|
||||
iceContactsOverlayItem.setAttribute('hidden', '');
|
||||
iceContactsOverlayItem.appendChild(
|
||||
iceContactsOverlayItemTemplate.content.cloneNode(true));
|
||||
container.appendChild(iceContactsOverlayItem);
|
||||
|
||||
document.body.appendChild(container);
|
||||
|
||||
iceContactList = document.getElementById('contact-list');
|
||||
}
|
||||
|
||||
function shouldNotShowICEContactsBar(done) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.ok(iceContactBar.hasAttribute('hidden'));
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
function shouldIncludeICEContacts(done, length) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.equal(iceContactList.children.length, length);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozContacts = navigator.mozContacts;
|
||||
navigator.mozContacts = MockMozContacts;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozContacts = realMozContacts;
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
ICEContacts._initialized = false;
|
||||
createDOM();
|
||||
});
|
||||
|
||||
suite('Initialization', function() {
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
ICEStore.setContacts([]).then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Lazyloads contact list overlay', function(done) {
|
||||
this.sinon.spy(MockLazyLoader, 'load');
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
sinon.assert.calledWith(MockLazyLoader.load, [iceContactsOverlay]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Sets header overlay title', function(done) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
var header = iceContactsOverlay.querySelector('header');
|
||||
assert.equal(header.dataset.l10nId, 'ice-contacts-overlay-title');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Should init when calling updateICEContacts', function(done) {
|
||||
ICEContacts._initialized = false;
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.isTrue(ICEContacts._initialized);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('No ICE contacts', function() {
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
ICEStore.setContacts([]).then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Should not show the ICE contacts bar', shouldNotShowICEContactsBar);
|
||||
});
|
||||
|
||||
suite('1 ICE contact with no telephone numbers', function() {
|
||||
var contact1;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
ICEStore.setContacts([contact1.id]).then(function() {
|
||||
done();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
test('Should not show the ICE contacts bar', shouldNotShowICEContactsBar);
|
||||
});
|
||||
|
||||
suite('2 ICE contact with no telephone numbers', function() {
|
||||
var contact1, contact2;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
var iceContacts = [];
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
iceContacts.push(contact1.id);
|
||||
if (iceContacts.length === 2) {
|
||||
ICEStore.setContacts(iceContacts).then(function() {
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
contact2 = new mozContact();
|
||||
contact2.givenName = ['ICE Contact'];
|
||||
contact2.familyName = ['2'];
|
||||
contact2.name = [contact2.givenName[0] + ' ' + contact2.familyName[0]];
|
||||
var contact2Req = navigator.mozContacts.save(contact2);
|
||||
contact2Req.onsuccess = function() {
|
||||
iceContacts.push(contact2.id);
|
||||
if (iceContacts.length === 2) {
|
||||
ICEStore.setContacts(iceContacts).then(function() {
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test('Should not show the ICE contacts bar', function(done) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.ok(iceContactBar.hasAttribute('hidden'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('1 ICE contact with 1 telephone number', function() {
|
||||
var contact1;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
contact1.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '111111111'
|
||||
}
|
||||
];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
ICEStore.setContacts([contact1.id]).then(function() {
|
||||
done();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
test('Should show the ICE contacts bar', function(done) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.isFalse(
|
||||
iceContactBar.hasAttribute('hidden'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Should include the ICE contact in the overlay', function(done) {
|
||||
shouldIncludeICEContacts(done, 2);
|
||||
});
|
||||
});
|
||||
|
||||
suite('1 ICE contact with 2 telephone number', function() {
|
||||
var contact1;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
contact1.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '111111111'
|
||||
},
|
||||
{
|
||||
type: ['mobile'],
|
||||
value: '222222222'
|
||||
}
|
||||
];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
ICEStore.setContacts([contact1.id]).then(function() {
|
||||
done();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
test('Should show the ICE contacts bar', function(done) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.isFalse(iceContactBar.hasAttribute('hidden'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Should include the ICE contact\'s phone numbers in the overlay',
|
||||
function(done) {
|
||||
shouldIncludeICEContacts(done, 3);
|
||||
});
|
||||
});
|
||||
|
||||
suite('2 ICE contact with 3 telephone numbers the first one and 4 the ' +
|
||||
'second one', function() {
|
||||
var contact1, contact2;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
var iceContacts = [];
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
contact1.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '111111111'
|
||||
},
|
||||
{
|
||||
type: ['mobile'],
|
||||
value: '222222222'
|
||||
},
|
||||
{
|
||||
type: ['work'],
|
||||
value: '333333333'
|
||||
}
|
||||
];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
iceContacts.push(contact1.id);
|
||||
if (iceContacts.length === 2) {
|
||||
ICEStore.setContacts(iceContacts).then(function() {
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
contact2 = new mozContact();
|
||||
contact2.givenName = ['ICE Contact'];
|
||||
contact2.familyName = ['2'];
|
||||
contact2.name = [contact2.givenName[0] + ' ' + contact2.familyName[0]];
|
||||
contact2.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '444444444'
|
||||
},
|
||||
{
|
||||
type: ['mobile'],
|
||||
value: '555555555'
|
||||
},
|
||||
{
|
||||
type: ['work'],
|
||||
value: '666666666'
|
||||
},
|
||||
{
|
||||
type: ['other'],
|
||||
value: '777777777'
|
||||
}
|
||||
];
|
||||
var contact2Req = navigator.mozContacts.save(contact2);
|
||||
contact2Req.onsuccess = function() {
|
||||
iceContacts.push(contact2.id);
|
||||
if (iceContacts.length === 2) {
|
||||
ICEStore.setContacts(iceContacts).then(function() {
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test('Should show the ICE contacts bar', function(done) {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.isFalse(iceContactBar.hasAttribute('hidden'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Should include the ICE contacts\' phone numbers in the overlay',
|
||||
function(done) {
|
||||
shouldIncludeICEContacts(done, 8);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Is from ICE contacts', function() {
|
||||
var contact1;
|
||||
var contact2;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
contact1.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '123'
|
||||
}
|
||||
];
|
||||
contact2 = new mozContact();
|
||||
contact2.givenName = ['ICE Contact'];
|
||||
contact2.familyName = ['2'];
|
||||
contact2.name = [contact2.givenName[0] + ' ' + contact2.familyName[0]];
|
||||
contact2.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '456'
|
||||
}
|
||||
];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
ICEStore.setContacts([contact1.id]).then(function() {
|
||||
done();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
test('Should include contact1', function() {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.isTrue(ICEContacts.isFromICEContact('123'));
|
||||
});
|
||||
});
|
||||
|
||||
test('Should not include contact2', function() {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
assert.isFalse(ICEContacts.isFromICEContact('456'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
suite('Should create correct DOM', function() {
|
||||
var contact1;
|
||||
|
||||
setup(function(done) {
|
||||
navigator.mozContacts.clear();
|
||||
contact1 = new mozContact();
|
||||
contact1.givenName = ['ICE Contact'];
|
||||
contact1.familyName = ['1'];
|
||||
contact1.name = [contact1.givenName[0] + ' ' + contact1.familyName[0]];
|
||||
contact1.tel = [
|
||||
{
|
||||
type: ['home'],
|
||||
value: '123'
|
||||
}
|
||||
];
|
||||
var contact1Req = navigator.mozContacts.save(contact1);
|
||||
contact1Req.onsuccess = function() {
|
||||
ICEStore.setContacts([contact1.id]).then(function() {
|
||||
ICEContacts.updateICEContacts().then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
test('Should include name element', function() {
|
||||
assert.equal(iceContactsOverlay.querySelector('.js-name').textContent,
|
||||
contact1.name);
|
||||
});
|
||||
|
||||
test('Should include tel-type element', function() {
|
||||
assert.equal(
|
||||
iceContactsOverlay.querySelector('.js-tel-type').dataset.l10nId,
|
||||
contact1.tel[0].type);
|
||||
});
|
||||
|
||||
test('Should include time element', function() {
|
||||
assert.equal(iceContactsOverlay.querySelector('.js-tel').textContent,
|
||||
contact1.tel[0].value);
|
||||
});
|
||||
|
||||
test('Should include cancel button', function() {
|
||||
assert.ok(
|
||||
iceContactsOverlay.querySelector('#contact-list-overlay-cancel'));
|
||||
});
|
||||
|
||||
test('Tapping a contact dials them', function() {
|
||||
this.sinon.spy(CallHandler, 'call');
|
||||
iceContactsOverlay.querySelector('button').click();
|
||||
sinon.assert.calledWith(CallHandler.call, contact1.tel[0].value);
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
243
gaia/apps/emergency-call/test/unit/keypad_test.js
Executable file
243
gaia/apps/emergency-call/test/unit/keypad_test.js
Executable file
|
|
@ -0,0 +1,243 @@
|
|||
/* globals gTonesFrequencies, KeypadManager, loadBodyHTML,
|
||||
MockIccManager, MockL10n, MockNavigatorMozTelephony,
|
||||
MockNavigatorSettings, MockSettingsListener, MocksHelper,
|
||||
MockTonePlayer
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
|
||||
require('/shared/test/unit/mocks/mock_iccmanager.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
|
||||
require('/shared/test/unit/mocks/mock_settings_listener.js');
|
||||
require('/shared/test/unit/mocks/dialer/mock_call.js');
|
||||
require('/shared/test/unit/mocks/dialer/mock_utils.js');
|
||||
require('/shared/test/unit/mocks/dialer/mock_tone_player.js');
|
||||
|
||||
require('/shared/js/dialer/dtmf_tone.js');
|
||||
require('/js/keypad.js');
|
||||
|
||||
|
||||
var mocksHelperForKeypad = new MocksHelper([
|
||||
'LazyLoader',
|
||||
'Utils',
|
||||
'SettingsListener',
|
||||
'TonePlayer'
|
||||
]).init();
|
||||
|
||||
suite('Keypad', function() {
|
||||
var subject;
|
||||
var realMozIccManager;
|
||||
var realMozL10n;
|
||||
var realMozSettings;
|
||||
var realMozTelephony;
|
||||
|
||||
mocksHelperForKeypad.attachTestHelpers();
|
||||
|
||||
// Dummy node used as event target
|
||||
var dummyNode = document.createElement('div');
|
||||
|
||||
/**
|
||||
* Simulate a touchstart event
|
||||
*
|
||||
* @param key {String} The target's dataset value, the touched key
|
||||
*/
|
||||
function mockTouchStart(key) {
|
||||
var fakeEvent = {
|
||||
target: dummyNode,
|
||||
preventDefault: function() {},
|
||||
stopPropagation: function() {},
|
||||
type: 'touchstart'
|
||||
};
|
||||
dummyNode.dataset.value = key;
|
||||
subject.keyHandler(fakeEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a touchend event
|
||||
*
|
||||
* @param key {String} The target's dataset value, the touched key
|
||||
*/
|
||||
function mockTouchEnd(key) {
|
||||
var fakeEvent = {
|
||||
target: dummyNode,
|
||||
preventDefault: function() {},
|
||||
stopPropagation: function() {},
|
||||
type: 'touchend'
|
||||
};
|
||||
dummyNode.dataset.value = key;
|
||||
subject.keyHandler(fakeEvent);
|
||||
}
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozIccManager = navigator.mozIccManager;
|
||||
navigator.mozIccManager = new MockIccManager();
|
||||
|
||||
realMozL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
|
||||
realMozSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = MockNavigatorSettings;
|
||||
MockNavigatorSettings.mSyncRepliesOnly = true;
|
||||
|
||||
realMozTelephony = navigator.mozTelephony;
|
||||
navigator.mozTelephony = MockNavigatorMozTelephony;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozIccManager = realMozIccManager;
|
||||
navigator.mozL10n = realMozL10n;
|
||||
navigator.mozSettings = realMozSettings;
|
||||
MockNavigatorSettings.mSyncRepliesOnly = false;
|
||||
MockNavigatorMozTelephony.mSuiteTeardown();
|
||||
navigator.mozTelephony = realMozTelephony;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
loadBodyHTML('/test/unit/mock_dialer_index.html');
|
||||
|
||||
dummyNode = document.createElement('div');
|
||||
|
||||
subject = KeypadManager;
|
||||
subject.init(false);
|
||||
|
||||
this.sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
dummyNode = null;
|
||||
MockNavigatorMozTelephony.mTeardown();
|
||||
});
|
||||
|
||||
suite('Keypad Manager', function() {
|
||||
test('initializates the TonePlayer to use the "system" channel',
|
||||
function() {
|
||||
this.sinon.spy(MockTonePlayer, 'init');
|
||||
KeypadManager.init(/* oncall */ false);
|
||||
|
||||
sinon.assert.calledWith(MockTonePlayer.init, 'system');
|
||||
});
|
||||
|
||||
test('sanitizePhoneNumber', function(done) {
|
||||
var testCases = {
|
||||
'111-111-1111': '111-111-1111',
|
||||
' 222-222-2222 ': '222-222-2222',
|
||||
' 333 - 333 - 3333 ': '333-333-3333',
|
||||
'4444 4444 4444': '444444444444',
|
||||
'555\n555\n5555': '5555555555',
|
||||
'666\t666\t6666': '6666666666'
|
||||
};
|
||||
|
||||
var counter = 0;
|
||||
|
||||
function verifyNumber(index) {
|
||||
return function() {
|
||||
var sanitized = subject.sanitizePhoneNumber(index);
|
||||
assert.equal(sanitized, testCases[index]);
|
||||
};
|
||||
}
|
||||
|
||||
for (var i in testCases) {
|
||||
counter++;
|
||||
test('#sanitizePhoneNumber - test case ' + counter, verifyNumber(i));
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
test('Adds active class to keys when pressed', function() {
|
||||
subject._phoneNumber = '';
|
||||
|
||||
mockTouchStart('1');
|
||||
assert.isTrue(dummyNode.classList.contains('active'));
|
||||
mockTouchEnd('1');
|
||||
assert.isFalse(dummyNode.classList.contains('active'));
|
||||
});
|
||||
|
||||
test('Multi-tap events are ignored', function() {
|
||||
mockTouchStart('1');
|
||||
assert.equal(subject._phoneNumber, '1');
|
||||
mockTouchStart('2');
|
||||
assert.equal(subject._phoneNumber, '1');
|
||||
mockTouchEnd('2');
|
||||
assert.equal(subject._phoneNumber, '1');
|
||||
mockTouchEnd('1');
|
||||
assert.equal(subject._phoneNumber, '1');
|
||||
});
|
||||
|
||||
suite('Audible and DTMF tones when composing numbers', function() {
|
||||
|
||||
setup(function() {
|
||||
subject._observePreferences();
|
||||
MockSettingsListener.mCallbacks['phone.ring.keypad'](true);
|
||||
});
|
||||
|
||||
test('Pressing a button plays a short tone', function() {
|
||||
var startSpy = this.sinon.spy(MockTonePlayer, 'start');
|
||||
var stopSpy = this.sinon.spy(MockTonePlayer, 'stop');
|
||||
|
||||
mockTouchStart('1');
|
||||
assert.isTrue(startSpy.calledWith(gTonesFrequencies['1'], true));
|
||||
mockTouchEnd('1');
|
||||
assert.isTrue(stopSpy.calledOnce);
|
||||
});
|
||||
|
||||
test('Button tones are disabled via prefs', function() {
|
||||
var startSpy = this.sinon.spy(MockTonePlayer, 'start');
|
||||
|
||||
MockSettingsListener.mCallbacks['phone.ring.keypad'](false);
|
||||
mockTouchStart('1');
|
||||
assert.isTrue(startSpy.notCalled);
|
||||
mockTouchEnd('1');
|
||||
assert.isTrue(startSpy.notCalled);
|
||||
});
|
||||
|
||||
test('Pressing a button does not play a DTMF tone', function() {
|
||||
var startToneSpy =
|
||||
this.sinon.spy(MockNavigatorMozTelephony, 'startTone');
|
||||
var stopToneSpy =
|
||||
this.sinon.spy(MockNavigatorMozTelephony, 'stopTone');
|
||||
|
||||
mockTouchStart('1');
|
||||
assert.isTrue(stopToneSpy.notCalled);
|
||||
assert.isTrue(startToneSpy.notCalled);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Keypad vibration', function() {
|
||||
setup(function() {
|
||||
this.sinon.spy(navigator, 'vibrate');
|
||||
subject._observePreferences();
|
||||
});
|
||||
|
||||
test('vibrates if setting is set', function() {
|
||||
MockSettingsListener.mCallbacks['keyboard.vibration'](true);
|
||||
|
||||
mockTouchStart('1');
|
||||
sinon.assert.calledWith(navigator.vibrate, 50);
|
||||
});
|
||||
|
||||
test('does not vibrate if setting is not set', function() {
|
||||
MockSettingsListener.mCallbacks['keyboard.vibration'](false);
|
||||
|
||||
mockTouchStart('1');
|
||||
sinon.assert.notCalled(navigator.vibrate);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Long press', function() {
|
||||
setup(function() {
|
||||
this.sinon.spy(window, 'setTimeout');
|
||||
});
|
||||
|
||||
test('should not try to detect the voicemail action', function() {
|
||||
mockTouchStart('1');
|
||||
sinon.assert.calledOnce(window.setTimeout);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
7
gaia/apps/emergency-call/test/unit/mock_call_handler.js
Executable file
7
gaia/apps/emergency-call/test/unit/mock_call_handler.js
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
/* exported MockCallHandler */
|
||||
|
||||
var MockCallHandler = {
|
||||
call: function() {}
|
||||
};
|
||||
80
gaia/apps/emergency-call/test/unit/mock_dialer_index.html
Executable file
80
gaia/apps/emergency-call/test/unit/mock_dialer_index.html
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
<section id="keyboard-view" class="full-height">
|
||||
<div id="phone-number-view-container">
|
||||
<div class="phone-number-view-wrapper-input">
|
||||
<input id="phone-number-view" type="text" class="phone-number-font font-light" readonly="readonly" data-l10n-id="phoneNumberInput">
|
||||
<div id="fake-phone-number-view"></div>
|
||||
</div>
|
||||
<button id="keypad-delete" class="reset-button" data-value="delete" data-l10n-id="keypadDelete">
|
||||
</button>
|
||||
</div>
|
||||
<section id="ice-contacts-bar" class="ice-contacts-bar" hidden data-l10n-id="ice-contacts-bar-text">
|
||||
ICE Contacts - In Case of Emergency
|
||||
</section>
|
||||
<article id="keyboard-container">
|
||||
<section id="keypad" dir="ltr">
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="1">
|
||||
<span>1</span>
|
||||
<em class="keypad-subicon" data-l10n-id="voiceMailKey"></em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="2">
|
||||
<span>2</span>
|
||||
<em>ABC</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="3">
|
||||
<span>3</span>
|
||||
<em>DEF</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="4">
|
||||
<span>4</span>
|
||||
<em>GHI</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="5">
|
||||
<span>5</span>
|
||||
<em>JKL</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="6">
|
||||
<span>6</span>
|
||||
<em>MNO</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="7">
|
||||
<span>7</span>
|
||||
<em>PQRS</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="8">
|
||||
<span>8</span>
|
||||
<em>TUV</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="9">
|
||||
<span>9</span>
|
||||
<em>WXYZ</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keypad-row">
|
||||
<div class="keypad-key" role="key" data-value="*" data-l10n-id="asterisk">
|
||||
<div class="asterisk">*</div>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-value="0">
|
||||
<span>0</span>
|
||||
<em class="font-size-plus">+</em>
|
||||
</div>
|
||||
<div class="keypad-key" role="key" data-type="dial" data-value="#" data-l10n-id="hashSign">
|
||||
<div class="sharp">#</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="keypad-callbar">
|
||||
<span role="button" id="keypad-callbar-cancel" class='keypad-callbar-button cancel' data-type="action" data-value="cancel" >
|
||||
<div data-l10n-id="cancel">Cancel</div>
|
||||
</span>
|
||||
<span role="button" id="keypad-callbar-call-action" data-type="action" data-value="make-call">
|
||||
<div>
|
||||
</div>
|
||||
</span>
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
7
gaia/apps/emergency-call/test/unit/mock_ice_contacts.js
Executable file
7
gaia/apps/emergency-call/test/unit/mock_ice_contacts.js
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
/* exported MockICEContacts */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockICEContacts = {
|
||||
isFromICEContact: function() {}
|
||||
};
|
||||
33
gaia/apps/emergency-call/test/unit/mock_keypad.js
Executable file
33
gaia/apps/emergency-call/test/unit/mock_keypad.js
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
/* exported MockKeypadManager */
|
||||
|
||||
var MockKeypadManager = {
|
||||
init: function() {},
|
||||
maxFontSize: 12,
|
||||
minFontSize: 8,
|
||||
formatPhoneNumber:
|
||||
function khm_formatPhoneNumber(ellipsisSide) {
|
||||
this.mFormatPhoneNumberCalled = true;
|
||||
},
|
||||
mFormatPhoneNumberCalled: false,
|
||||
updateAdditionalContactInfo:
|
||||
function khm_updateAdditionalContactInfo(ellipsisSide, maxFontSize) {
|
||||
this.mUpdateAdditionalContactInfo = true;
|
||||
},
|
||||
mUpdateAdditionalContactInfo: false,
|
||||
mTearDown: function khm_tearDown() {
|
||||
this.mFormatPhoneNumberCalled = false;
|
||||
this.mUpdateAdditionalContactInfo = false;
|
||||
},
|
||||
|
||||
mOnMakeCall: null,
|
||||
updatePhoneNumber: function khm_updatePhoneNumber(number) {
|
||||
this._phoneNumber = number;
|
||||
},
|
||||
makeCall: function makeCall() {
|
||||
if (this.mOnMakeCall) {
|
||||
this.mOnMakeCall(this._phoneNumber);
|
||||
}
|
||||
}
|
||||
};
|
||||
4
gaia/apps/emergency-call/test/unit/setup.js
Executable file
4
gaia/apps/emergency-call/test/unit/setup.js
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
require('/shared/test/unit/mocks/elements/custom_elements_helper.js');
|
||||
Loading…
Reference in a new issue