зеркало из https://github.com/mozilla/gecko-dev.git
Bug 707629 - Part 2: Complete most of the mozTelephony API. r=bent
This commit is contained in:
Родитель
46378df6f5
Коммит
5d72cf01e7
|
@ -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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче