Bug 707629 - Part 2: Complete most of the mozTelephony API. r=bent

This commit is contained in:
Philipp von Weitershausen 2011-12-07 14:57:19 +08:00
Родитель 46378df6f5
Коммит 5d72cf01e7
4 изменённых файлов: 225 добавлений и 80 удалений

Просмотреть файл

@ -46,6 +46,31 @@ const TELEPHONY_CALL_CID = Components.ID("{6b9b3daf-e5ea-460b-89a5-641ee20dd577}
const TELEPHONY_CALL_CONTRACTID = "@mozilla.org/telephony-call;1";
const DOM_RADIOSTATE_UNAVAILABLE = "unavailable";
const DOM_RADIOSTATE_OFF = "off";
const DOM_RADIOSTATE_READY = "ready";
const DOM_CARDSTATE_UNAVAILABLE = "unavailable";
const DOM_CARDSTATE_ABSENT = "absent";
const DOM_CARDSTATE_PIN_REQUIRED = "pin_required";
const DOM_CARDSTATE_PUK_REQUIRED = "puk_required";
const DOM_CARDSTATE_NETWORK_LOCKED = "network_locked";
const DOM_CARDSTATE_NOT_READY = "not_ready";
const DOM_CARDSTATE_READY = "ready";
const DOM_CALL_READYSTATE_DIALING = "dialing";
const DOM_CALL_READYSTATE_RINGING = "ringing";
const DOM_CALL_READYSTATE_BUSY = "busy";
const DOM_CALL_READYSTATE_CONNECTING = "connecting";
const DOM_CALL_READYSTATE_CONNECTED = "connected";
const DOM_CALL_READYSTATE_DISCONNECTING = "disconnecting";
const DOM_CALL_READYSTATE_DISCONNECTED = "disconnected";
const DOM_CALL_READYSTATE_INCOMING = "incoming";
const DOM_CALL_READYSTATE_HOLDING = "holding";
const DOM_CALL_READYSTATE_HELD = "held";
const CALLINDEX_TEMPORARY_DIALING = -1;
/**
* Define an event listener slot on an object, e.g.
*
@ -110,6 +135,9 @@ EventTarget.prototype = {
//TODO this does not deal with bubbling, defaultPrevented, canceling, etc.
//TODO disallow re-dispatch of the same event if it's already being
// dispatched (recursion).
if (!this._listeners) {
return;
}
let handlerList = this._listeners[event.type];
if (!handlerList) {
return;
@ -124,7 +152,7 @@ EventTarget.prototype = {
// registered before firing it.
let handlers = handlerList.slice();
handlers.forEach(function (handler) {
if (handerList.indexOf(handler) == -1) {
if (handlerList.indexOf(handler) == -1) {
return;
}
switch (typeof handler) {
@ -139,54 +167,42 @@ EventTarget.prototype = {
}
};
const DOM_RADIOSTATE_UNAVAILABLE = "unavailable";
const DOM_RADIOSTATE_OFF = "off";
const DOM_RADIOSTATE_READY = "ready";
const DOM_CARDSTATE_UNAVAILABLE = "unavailable";
const DOM_CARDSTATE_ABSENT = "absent";
const DOM_CARDSTATE_PIN_REQUIRED = "pin_required";
const DOM_CARDSTATE_PUK_REQUIRED = "puk_required";
const DOM_CARDSTATE_NETWORK_LOCKED = "network_locked";
const DOM_CARDSTATE_NOT_READY = "not_ready";
const DOM_CARDSTATE_READY = "ready";
/**
* Callback object that Telephony registers with nsITelephone.
* Telephony can't use itself because that might overload event handler
* attributes ('onfoobar').
*/
function TelephonyRadioCallback(telephony) {
function TelephoneCallback(telephony) {
this.telephony = telephony;
}
TelephonyRadioCallback.prototype = {
TelephoneCallback.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephoneCallback]),
// nsITelephoneCallback
onsignalstrengthchange: function onsignalstrengthchange(signalStrength) {
this.telephony.signalStrength = signalStrength;
onsignalstrengthchange: function onsignalstrengthchange(event) {
this.telephony.signalStrength = event.signalStrength;
this.telephony._dispatchEventByType("signalstrengthchange");
},
onoperatorchange: function onoperatorchange(operator) {
this.telephony.operator = operator;
onoperatorchange: function onoperatorchange(event) {
this.telephony.operator = event.operator;
this.telephony._dispatchEventByType("operatorchange");
},
onradiostatechange: function onradiostatechange(radioState) {
this.telephony.radioState = radioState;
onradiostatechange: function onradiostatechange(event) {
this.telephony.radioState = event.radioState;
this.telephony._dispatchEventByType("radiostatechange");
},
oncardstatechange: function oncardstatechange(cardState) {
this.telephony.cardState = cardState;
oncardstatechange: function oncardstatechange(event) {
this.telephony.cardState = event.cardState;
this.telephony._dispatchEventByType("cardstatechange");
},
oncallstatechange: function oncallstatechange(callState) {
this.telephony._processCallState(callState);
oncallstatechange: function oncallstatechange(event) {
this.telephony._processCallState(event);
},
};
@ -214,23 +230,35 @@ Telephony.prototype = {
init: function init(window) {
this.window = window;
this.radioInterface = Cc["@mozilla.org/telephony/radio-interface;1"]
.createInstance(Ci.nsITelephone);
this.radioCallback = new TelephonyRadioCallback(this);
this.telephone = Cc["@mozilla.org/telephony/radio-interface;1"]
.createInstance(Ci.nsITelephone);
this.telephoneCallback = new TelephoneCallback(this);
//TODO switch to method suggested by bz in bug 707507
window.addEventListener("unload", function onunload(event) {
this.radioInterface.unregisterCallback(this.radioCallback);
this.radioCallback = null;
this.telephone.unregisterCallback(this.telephoneCallback);
this.telephoneCallback = null;
this.window = null;
}.bind(this));
this.radioInterface.registerCallback(this.radioCallback);
this.telephone.registerCallback(this.telephoneCallback);
this.callsByIndex = {};
this.liveCalls = [];
let initialState = this.radioInterface.initialState;
this.operator = initialState.operator;
this.radioState = initialState.radioState;
this.cardState = initialState.cardState;
this.signalStrength = initialState.signalStrength;
this._processCallState(initialState.callState);
// Populate existing state.
let currentState = this.telephone.currentState;
let states = currentState.currentCalls;
for (let i = 0; i < states.length; i++) {
let state = states[i];
let call = new TelephonyCall(this.telephone, state.callIndex);
call.readyState = state.callState;
call.number = state.number;
this.liveCalls.push(call);
this.callsByIndex[state.callIndex] = call;
}
this.operator = currentState.operator;
this.radioState = currentState.radioState;
this.cardState = currentState.cardState;
this.signalStrength = currentState.signalStrength;
},
_dispatchEventByType: function _dispatchEventByType(type) {
@ -240,17 +268,92 @@ Telephony.prototype = {
this.dispatchEvent(event);
},
_processCallState: function _processCallState(callState) {
//TODO
_dispatchCallEvent: function _dispatchCallEvent(call, type, target) {
let event = this.window.document.createEvent("Event");
event.initEvent(type, false, false);
event.call = call; //XXX this is probably not going to work
//event.isTrusted = true;
target = target || call;
target.dispatchEvent(event);
},
_processCallState: function _processCallState(state) {
// If the call is dialing, chances are good that we just kicked that off
// so there's a call object without a callIndex. Let's fix that.
if (state.callState == DOM_CALL_READYSTATE_DIALING) {
let call = this.callsByIndex[CALLINDEX_TEMPORARY_DIALING];
if (call) {
call.callIndex = state.callIndex;
delete this.callsByIndex[CALLINDEX_TEMPORARY_DIALING];
this.callsByIndex[call.callIndex] = call;
// Nothing else to do, since the initial call state will already be
// DOM_CALL_READYSTATE_DIALING, so there's no event to dispatch.
return;
}
}
// If there is an existing call object, update state and dispatch event
// on it.
let call = this.callsByIndex[state.callIndex];
if (call) {
if (call.readyState == state.callState) {
// No change in ready state, don't dispatch an event.
return;
}
if (state.readyState == DOM_CALL_READYSTATE_DISCONNECTED) {
let index = this.liveCalls.indexOf(call);
if (index != -1) {
this.liveCalls.splice(index, 1);
}
delete this.callsByIndex[call.callIndex];
}
call.readyState = state.callState;
this._dispatchCallEvent(call, "readystatechange");
this._dispatchCallEvent(call, state.callState);
return;
}
// There's no call object yet, so let's create a new one, except when
// the state notified means that the call is over.
if (state.readyState == DOM_CALL_READYSTATE_DISCONNECTED) {
return;
}
call = new TelephonyCall(this.telephone, state.callIndex);
call.number = state.number;
call.readyState = state.callState;
this.callsByIndex[state.callIndex] = call;
this.liveCalls.push(call);
let target;
if (call.readyState == DOM_CALL_READYSTATE_INCOMING) {
target = this;
} else {
target = call;
this._dispatchCallEvent(call, "readystatechange");
}
this._dispatchCallEvent(call, state.callState, target);
},
callsByIndex: null,
// mozIDOMTelephony
liveCalls: null,
dial: function dial(number) {
this.radioInterface.dial(number);
return new TelephonyCall(number, DOM_CALL_READYSTATE_DIALING);
this.telephone.dial(number);
// We don't know ahead of time what callIndex the call is going to have
// so let's assign a temp value for now and sort it out on the first
// 'callstatechange' event.
//TODO ensure there isn't already an outgoing call
let callIndex = CALLINDEX_TEMPORARY_DIALING;
let call = new TelephonyCall(this.telephone, callIndex);
call.readyState = DOM_CALL_READYSTATE_DIALING;
call.number = number;
this.callsByIndex[callIndex] = call;
this.liveCalls.push(call);
return call;
},
// Additional stuff that's useful.
@ -261,23 +364,17 @@ Telephony.prototype = {
cardState: DOM_CARDSTATE_UNAVAILABLE,
};
defineEventListenerSlot(Telephony.prototype, DOM_CALL_READYSTATE_INCOMING);
//XXX philikon's additions
defineEventListenerSlot(Telephony.prototype, "radiostatechange");
defineEventListenerSlot(Telephony.prototype, "cardstatechange");
defineEventListenerSlot(Telephony.prototype, "signalstrengthchange");
defineEventListenerSlot(Telephony.prototype, "operatorchange");
defineEventListenerSlot(Telephony.prototype, "incoming");
const DOM_CALL_READYSTATE_DIALING = "dialing";
const DOM_CALL_READYSTATE_DOM_CALLING = "calling";
const DOM_CALL_READYSTATE_INCOMING = "incoming";
const DOM_CALL_READYSTATE_CONNECTED = "connected";
const DOM_CALL_READYSTATE_CLOSED = "closed";
const DOM_CALL_READYSTATE_BUSY = "busy";
function TelephonyCall(number, initialState) {
this.number = number;
this.readyState = initialState;
function TelephonyCall(telephone, callIndex) {
this.telephone = telephone;
this.callIndex = callIndex;
}
TelephonyCall.prototype = {
@ -293,21 +390,37 @@ TelephonyCall.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMTelephonyCall,
Ci.nsIDOMEventTarget]),
callIndex: null,
// mozIDOMTelephonyCall
number: null,
readyState: null,
answer: function answer() {
//TODO
if (this.readyState != DOM_CALL_READYSTATE_INCOMING) {
throw "Can only answer an incoming call!";
}
this.telephone.answerCall();
},
disconnect: function disconnect() {
//TODO
if (this.readyState == DOM_CALL_READYSTATE_INCOMING) {
this.telephone.rejectCall();
} else {
this.telephone.hangUp(this.callIndex);
}
},
};
defineEventListenerSlot(TelephonyCall.prototype, "connect");
defineEventListenerSlot(TelephonyCall.prototype, "disconnect");
defineEventListenerSlot(TelephonyCall.prototype, "busy");
defineEventListenerSlot(TelephonyCall.prototype, "readystatechange");
defineEventListenerSlot(TelephonyCall.prototype, DOM_CALL_READYSTATE_RINGING);
defineEventListenerSlot(TelephonyCall.prototype, DOM_CALL_READYSTATE_BUSY);
defineEventListenerSlot(TelephonyCall.prototype, DOM_CALL_READYSTATE_CONNECTING);
defineEventListenerSlot(TelephonyCall.prototype, DOM_CALL_READYSTATE_CONNECTED);
defineEventListenerSlot(TelephonyCall.prototype, DOM_CALL_READYSTATE_DISCONNECTING);
defineEventListenerSlot(TelephonyCall.prototype, DOM_CALL_READYSTATE_DISCONNECTED);
const NSGetFactory = XPCOMUtils.generateNSGetFactory([Telephony]);

Просмотреть файл

@ -36,6 +36,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsIDOMEventTarget.idl"
#include "nsIDOMEvent.idl"
interface nsIDOMEventListener;
interface mozIDOMTelephonyCall;
@ -74,3 +75,8 @@ interface mozIDOMTelephonyCall : nsIDOMEventTarget {
attribute nsIDOMEventListener ondisconnecting;
attribute nsIDOMEventListener ondisconnected;
};
[scriptable, uuid(c8c42b0c-a0dd-4702-9425-a7a80b2075c3)]
interface mozIDOMTelephonyCallEvent : nsIDOMEvent {
readonly attribute mozIDOMTelephonyCall call;
};

Просмотреть файл

@ -37,24 +37,26 @@
#include "nsISupports.idl"
[scriptable, uuid(e21cdf40-c511-442b-8c7d-aa75471b0423)]
[scriptable, uuid(9b7e3a01-9c45-4af3-81bb-1bf08a842226)]
interface nsITelephoneCallback : nsISupports {
void oncallstatechange(in jsval callState);
void oncallstatechange(in jsval event);
//XXX philikon's additions
void onoperatorchange(in jsval operatorInfo);
void onradiostatechange(in jsval radioState);
void oncardstatechange(in jsval cardState);
void onsignalstrengthchange(in jsval signalStrength);
void onoperatorchange(in jsval event);
void onradiostatechange(in jsval event);
void oncardstatechange(in jsval event);
void onsignalstrengthchange(in jsval event);
};
[scriptable, uuid(dda49485-5887-49bb-ba0b-10c5d116eb64)]
[scriptable, uuid(3d3deb80-fa5e-4e05-9153-91ee614f67d5)]
interface nsITelephone : nsISupports {
readonly attribute jsval initialState;
readonly attribute jsval currentState;
void dial(in DOMString number);
void hangup(in long callIndex);
void hangUp(in long callIndex);
void answerCall();
void rejectCall();
void registerCallback(in nsITelephoneCallback callback);
void unregisterCallback(in nsITelephoneCallback callback);

Просмотреть файл

@ -45,6 +45,7 @@ const DEBUG = true; // set to false to suppress debug messages
const TELEPHONYWORKER_CONTRACTID = "@mozilla.org/telephony/worker;1";
const TELEPHONYWORKER_CID = Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const DOM_CALL_READYSTATE_DISCONNECTED = "disconnected";
function nsTelephonyWorker() {
this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js");
@ -52,7 +53,13 @@ function nsTelephonyWorker() {
this.worker.onmessage = this.onmessage.bind(this);
this._callbacks = [];
this.initialState = {};
this.currentState = {
signalStrength: null,
operator: null,
radioState: null,
cardState: null,
currentCalls: {}
};
}
nsTelephonyWorker.prototype = {
@ -86,35 +93,39 @@ nsTelephonyWorker.prototype = {
debug("Received message: " + JSON.stringify(message));
let value;
switch (message.type) {
case "callstatechange":
case "signalstrengthchange":
this.initialState.signalStrength = message.signalStrength;
value = message.signalStrength;
this.currentState.signalStrength = message.signalStrength;
break;
case "operatorchange":
this.initialState.operator = message.operator;
value = message.operator;
this.currentState.operator = message.operator;
break;
case "onradiostatechange":
this.initialState.radioState = message.radioState;
value = message.radioState;
case "radiostatechange":
this.currentState.radioState = message.radioState;
break;
case "cardstatechange":
this.initialState.cardState = message.cardState;
value = message.cardState;
this.currentState.cardState = message.cardState;
break;
case "callstatechange":
this.initialState.callState = message.callState;
value = message.callState;
// Reuse the message object as the value here since there's more to
// the call state than just the callState integer.
if (message.callState == DOM_CALL_READYSTATE_DISCONNECTED) {
delete this.currentState.callState[message.callIndex];
} else {
this.currentState.callState[value.callIndex] = message;
}
break;
default:
// Got some message from the RIL worker that we don't know about.
return;
}
let methodname = "on" + message.type;
this._callbacks.forEach(function (callback) {
let method = callback[methodname];
if (typeof method != "function") {
return;
}
method.call(callback, value);
method.call(callback, message);
});
},
@ -124,13 +135,26 @@ nsTelephonyWorker.prototype = {
// nsITelephone
initialState: null,
currentState: null,
dial: function dial(number) {
debug("Dialing " + number);
this.worker.postMessage({type: "dial", number: number});
},
hangUp: function hangUp(callIndex) {
debug("Hanging up call no. " + callIndex);
this.worker.postMessage({type: "hangUp", callIndex: callIndex});
},
answerCall: function answerCall() {
this.worker.postMessage({type: "answerCall"});
},
rejectCall: function rejectCall() {
this.worker.postMessage({type: "rejectCall"});
},
_callbacks: null,
registerCallback: function registerCallback(callback) {