Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Reland "Merge pull request #16656 from Rik/telephony-promise-969280"
Browse files Browse the repository at this point in the history
This reverts commit 548965a.
  • Loading branch information
KWierso committed Feb 27, 2014
1 parent 548965a commit 98001aa
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 56 deletions.
104 changes: 64 additions & 40 deletions apps/communications/dialer/js/telephony_helper.js
@@ -1,5 +1,8 @@
'use strict';

/* global _, TonePlayer, LazyLoader, IccHelper, ConfirmDialog, LazyL10n */
/* exported TelephonyHelper */

var TelephonyHelper = (function() {
var confirmLoaded = false;

Expand Down Expand Up @@ -52,10 +55,10 @@ var TelephonyHelper = (function() {
[480, 620, 500], [0, 0, 500],
[480, 620, 500], [0, 0, 500]];
TonePlayer.playSequence(sequence);
};
}

function startDial(
conn, sanitizedNumber, oncall, connected, disconnected, error) {
conn, sanitizedNumber, oncall, onconnected, ondisconnected, onerror) {

var telephony = navigator.mozTelephony;
if (!telephony) {
Expand All @@ -74,62 +77,83 @@ var TelephonyHelper = (function() {
var cardState = IccHelper.cardState;
var emergencyOnly = conn.voice.emergencyCallsOnly;
var hasCard = (conn.iccId !== null);
var call;
var promiseOrCall;

// Note: no need to check for cardState null. While airplane mode is on
// cardState is null and we handle that situation in call() above.
if (((cardState === 'unknown') || (cardState === 'illegal')) &&
(emergencyOnly === false)) {
error();
onerror();
return;
} else if (emergencyOnly) {
// If the mobileConnection has a sim card we let gecko take the
// default service, otherwise we force the first slot.
var serviceId = hasCard ? undefined : 0;
call = telephony.dialEmergency(sanitizedNumber, serviceId);
promiseOrCall = telephony.dialEmergency(sanitizedNumber, serviceId);
} else {
call = telephony.dial(sanitizedNumber);
promiseOrCall = telephony.dial(sanitizedNumber);
}

if (call) {
if (oncall) {
oncall();
}
call.onconnected = connected;
call.ondisconnected = disconnected;
call.onerror = function errorCB(evt) {
if (error) {
error();
}

var errorName = evt.call.error.name;
if (errorName === 'BadNumberError') {
// If the call is rejected for a bad number and we're in emergency
// only mode, then just tell the user that they're not connected
// to a network. Otherwise, tell them the number is bad.
displayMessage(emergencyOnly ? 'NoNetwork' : 'BadNumber');
} else if (errorName === 'DeviceNotAcceptedError') {
displayMessage('DeviceNotAccepted');
} else if (errorName === 'RadioNotAvailable') {
displayMessage('FlightMode');
} else if (errorName === 'BusyError') {
notifyBusyLine();
displayMessage('NumberIsBusy');
} else if (errorName === 'FDNBlockedError' ||
errorName === 'FdnCheckFailure') {
displayMessage('FixedDialingNumbers');
} else {
// If the call failed for some other reason we should still
// display something to the user. See bug 846403.
console.error('Unexpected error: ', errorName);
}
};
/* XXX: Temporary fix to handle old and new telephony API
To remove when bug 969218 lands */
if (promiseOrCall && promiseOrCall.then) {
promiseOrCall.then(function(call) {
installHandlers(call, emergencyOnly, oncall, onconnected,
ondisconnected, onerror);
}).catch(function(errorName) {
displayMessage('UnableToCall');
});
} else {
displayMessage('UnableToCall');
installHandlers(promiseOrCall, emergencyOnly, oncall, onconnected,
ondisconnected, onerror);
}
});
}

function installHandlers(call, emergencyOnly, oncall, onconnected,
ondisconnected, onerror) {
if (call) {
if (oncall) {
oncall();
}
call.onconnected = onconnected;
call.ondisconnected = ondisconnected;
call.onerror = function errorCB(evt) {
var errorName = evt.call.error.name;
handleError(errorName, emergencyOnly, onerror);
};
} else {
displayMessage('UnableToCall');
}
}

function handleError(errorName, emergencyOnly, onerror) {
if (onerror) {
onerror();
}

if (errorName === 'BadNumberError') {
// If the call is rejected for a bad number and we're in emergency
// only mode, then just tell the user that they're not connected
// to a network. Otherwise, tell them the number is bad.
displayMessage(emergencyOnly ? 'NoNetwork' : 'BadNumber');
} else if (errorName === 'DeviceNotAcceptedError') {
displayMessage('DeviceNotAccepted');
} else if (errorName === 'RadioNotAvailable') {
displayMessage('FlightMode');
} else if (errorName === 'BusyError') {
notifyBusyLine();
displayMessage('NumberIsBusy');
} else if (errorName === 'FDNBlockedError' ||
errorName === 'FdnCheckFailure') {
displayMessage('FixedDialingNumbers');
} else {
// If the call failed for some other reason we should still
// display something to the user. See bug 846403.
console.error('Unexpected error: ', errorName);
}
}

var isValid = function t_isValid(sanitizedNumber) {
var validExp = /^[0-9#+*]{1,50}$/;
return validExp.test(sanitizedNumber);
Expand Down
151 changes: 147 additions & 4 deletions apps/communications/dialer/test/unit/telephony_helper_test.js
@@ -1,6 +1,6 @@
/* global ConfirmDialog, MocksHelper, MockIccHelper, MockMozL10n,
MockMozMobileConnection, MockMozTelephony, MockNavigatorSettings,
MockTonePlayer, TelephonyHelper */
MockTonePlayer, Promise, TelephonyHelper */

'use strict';

Expand Down Expand Up @@ -244,11 +244,55 @@ suite('telephony helper', function() {
assert.equal(mockCall.ondisconnected, ondisconnected);
});

test('should trigger the onerror callback on error', function(done) {
subject.call('123', null, null, null, function() {
test('should trigger the onerror callback on error', function() {
var onerrorStub = this.sinon.stub();
subject.call('123', null, null, null, onerrorStub);
mockCall.onerror(createCallError());
sinon.assert.called(onerrorStub);
});
});

suite('Callbacks binding, promise edition', function() {
var mockCall;
var mockPromise;

setup(function() {
mockCall = {};
mockPromise = Promise.resolve(mockCall);
this.sinon.stub(MockMozTelephony, 'dial').returns(mockPromise);
});

test('should trigger oncall as soon as we get a call object',
function(done) {
subject.call('123', function() {
done();
});
mockCall.onerror(createCallError());
});

test('should bind the onconnected callback', function(done) {
var onconnected = function uniq_onconnected() {};
subject.call('123', null, onconnected);
mockPromise.then(function() {
assert.equal(mockCall.onconnected, onconnected);
}).then(done, done);
});

test('should bind the ondisconnected callback', function(done) {
var ondisconnected = function uniq_ondisconnected() {};
subject.call('123', null, null, ondisconnected);
mockPromise.then(function() {
assert.isFunction(mockCall.ondisconnected);
assert.equal(mockCall.ondisconnected, ondisconnected);
}).then(done, done);
});

test('should trigger the onerror callback on error', function(done) {
var onerrorStub = this.sinon.stub();
subject.call('123', null, null, null, onerrorStub);
mockPromise.then(function() {
mockCall.onerror(createCallError());
sinon.assert.called(onerrorStub);
}).then(done, done);
});
});

Expand Down Expand Up @@ -321,13 +365,112 @@ suite('telephony helper', function() {
});
});

suite('Call error handling, promise edition', function() {
var mockCall;
var mockPromise;

setup(function() {
mockCall = {};
mockPromise = Promise.resolve(mockCall);
this.sinon.stub(MockMozTelephony, 'dial').returns(mockPromise);
this.sinon.stub(MockMozTelephony, 'dialEmergency').returns(mockPromise);
});

suite('BadNumberError handle', function() {
test('should display the BadNumber message', function(done) {
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('BadNumberError'));
assert.isTrue(spyConfirmShow.calledWith('invalidNumberToDialTitle',
'invalidNumberToDialMessage'));
}).then(done, done);
});

test('should display the NoNetwork message in emergency mode',
function(done) {
MockMozMobileConnection.voice.emergencyCallsOnly = true;
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('BadNumberError'));
assert.isTrue(spyConfirmShow.calledWith('emergencyDialogTitle',
'emergencyDialogBodyBadNumber'));
}).then(done, done);
});
});

test('should handle BusyError', function(done) {
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('BusyError'));
assert.isTrue(spyConfirmShow.calledWith('numberIsBusyTitle',
'numberIsBusyMessage'));
}).then(done, done);
});

test('should handle FDNBlockedError', function(done) {
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('FDNBlockedError'));
assert.isTrue(spyConfirmShow.calledWith('fdnIsEnabledTitle',
'fdnIsEnabledMessage'));
}).then(done, done);
});

test('should handle FdnCheckFailure', function(done) {
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('FdnCheckFailure'));
assert.isTrue(spyConfirmShow.calledWith('fdnIsEnabledTitle',
'fdnIsEnabledMessage'));
}).then(done, done);
});

test('should play the busy tone', function(done) {
var playSpy = this.sinon.spy(MockTonePlayer, 'playSequence');
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('BusyError'));
assert.isTrue(playSpy.calledOnce);
}).then(done, done);
});

test('should handle DeviceNotAcceptedError', function(done) {
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('DeviceNotAcceptedError'));
assert.isTrue(spyConfirmShow.calledWith('emergencyDialogTitle',
'emergencyDialogBodyDeviceNotAccepted'));
}).then(done, done);
});

test('should handle RadioNotAvailable', function(done) {
subject.call('123');
mockPromise.then(function() {
mockCall.onerror(createCallError('RadioNotAvailable'));
assert.isTrue(spyConfirmShow.calledWith('callAirplaneModeTitle',
'callAirplaneModeMessage'));
}).then(done, done);
});
});

test('should display a message if we didn\'t get a call back', function() {
this.sinon.stub(MockMozTelephony, 'dial').returns(null);
subject.call('123');
assert.isTrue(spyConfirmShow.calledWith('unableToCallTitle',
'unableToCallMessage'));
});

test('should display a message if we didn\'t get a call back,promise edition',
function(done) {
var mockPromise = Promise.reject();
this.sinon.stub(MockMozTelephony, 'dial').returns(mockPromise);
subject.call('123');
mockPromise.catch(function() {
assert.isTrue(spyConfirmShow.calledWith('unableToCallTitle',
'unableToCallMessage'));
}).then(done, done);
});

suite('DSDS >', function() {
setup(function() {
navigator.mozMobileConnection = undefined;
Expand Down
35 changes: 25 additions & 10 deletions apps/system/emergency-call/js/dialer.js
@@ -1,24 +1,39 @@
'use strict';

/* exported CallHandler */
/* global KeypadManager */

var CallHandler = {
_telephony: window.navigator.mozTelephony,

call: function ch_call(number) {
var sanitizedNumber = number.replace(/-/g, '');
var telephony = this._telephony;
if (telephony) {
var call = telephony.dialEmergency(sanitizedNumber);
if (call) {
var cb = function clearPhoneView() {
KeypadManager.updatePhoneNumber('');
};
call.onconnected = cb;

call.ondisconnected = function callEnded() {
cb();
};
/* XXX: Temporary fix to handle old and new telephony API
To remove when bug 969218 lands */
var promiseOrCall = telephony.dialEmergency(sanitizedNumber);
if (promiseOrCall && promiseOrCall.then) {
promiseOrCall.then(function(call) {
this._installHandlers(call);
}.bind(this));
} else {
this._installHandlers(promiseOrCall);
}
}
},

_installHandlers: function(call) {
if (call) {
var cb = function clearPhoneView() {
KeypadManager.updatePhoneNumber('');
};
call.onconnected = cb;

call.ondisconnected = function callEnded() {
cb();
};
}
}
};

Expand Down

0 comments on commit 98001aa

Please sign in to comment.