Add conference call support to sl4a.
Implement InCallService for sl4a.
Add our InCallService to manifest.
Add conference call and Phone class related RPC calls to TelecomManagerFacade.
Change-Id: I2cdd25797e788d42e907082e2d12dbade29f052d
diff --git a/Common/src/com/googlecode/android_scripting/facade/tele/InCallServiceImpl.java b/Common/src/com/googlecode/android_scripting/facade/tele/InCallServiceImpl.java
new file mode 100644
index 0000000..1cae047
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/InCallServiceImpl.java
@@ -0,0 +1,49 @@
+
+package com.googlecode.android_scripting.facade.tele;
+
+import java.util.HashMap;
+
+import android.telecom.Call;
+import android.telecom.InCallService;
+import android.telecom.Phone;
+
+import com.googlecode.android_scripting.Log;
+
+public class InCallServiceImpl extends InCallService {
+
+ public static Phone mPhone;
+ public static HashMap<String, Call> mCalls = new HashMap<String, Call>();
+
+ private Phone.Listener mPhoneListener = new Phone.Listener() {
+
+ @Override
+ public void onCallAdded(Phone phone, Call call) {
+ Log.d("onCallAdded: " + call.toString());
+ String id = TelecomManagerFacade.getCallId(call);
+ Log.d("Adding " + id);
+ mCalls.put(id, call);
+ }
+
+ @Override
+ public void onCallRemoved(Phone phone, Call call) {
+ Log.d("onCallRemoved: " + call.toString());
+ String id = TelecomManagerFacade.getCallId(call);
+ Log.d("Removing " + id);
+ mCalls.remove(id);
+ }
+ };
+
+ @Override
+ public void onPhoneCreated(Phone phone) {
+ Log.d("onPhoneCreated");
+ mPhone = phone;
+ mPhone.addListener(mPhoneListener);
+ }
+
+ @Override
+ public void onPhoneDestroyed(Phone phone) {
+ Log.d("onPhoneDestroyed");
+ mPhone.removeListener(mPhoneListener);
+ mPhone = null;
+ }
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/tele/TelecomManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/tele/TelecomManagerFacade.java
index 02edac2..a78610e 100644
--- a/Common/src/com/googlecode/android_scripting/facade/tele/TelecomManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/TelecomManagerFacade.java
@@ -16,17 +16,22 @@
package com.googlecode.android_scripting.facade.tele;
+import java.util.ArrayList;
import java.util.List;
import android.app.Service;
+import android.telecom.AudioState;
+import android.telecom.Call;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
+import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.facade.FacadeManager;
import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcParameter;
/**
* Exposes TelecomManager functionality.
@@ -47,6 +52,27 @@
public void shutdown() {
}
+ /**
+ * Returns an identifier of the call. When a phone number is available, the number will be
+ * returned. Otherwise, the standard object toString result of the Call object. e.g. A
+ * conference call does not have a single number associated with it, thus the toString Id will
+ * be returned.
+ *
+ * @param call
+ * @return
+ */
+ public static String getCallId(Call call) {
+ try {
+ String handle = call.getDetails().getHandle().toString();
+ int idx = handle.indexOf(":");
+ String number = handle.substring(idx + 1).trim();
+ return number;
+ } catch (NullPointerException e) {
+ Log.d("Failed to get a number from the call object, using toString.");
+ return call.toString();
+ }
+ }
+
@Rpc(description = "If there's a ringing call, accept on behalf of the user.")
public void telecomAcceptRingingCall() {
mTelecomManager.acceptRingingCall();
@@ -124,6 +150,53 @@
return mTelecomManager.isRinging();
}
+ @Rpc(description = "Joins two calls into a conference call. Calls are identified by their IDs listed by telecomPhoneGetCallIds")
+ public void telecomJoinCallsInConf(@RpcParameter(name = "callIdOne") String callIdOne,
+ @RpcParameter(name = "callIdTwo") String callIdTwo) {
+ Call callOne = InCallServiceImpl.mCalls.get(callIdOne);
+ Call callTwo = InCallServiceImpl.mCalls.get(callIdTwo);
+ callOne.conference(callTwo);
+ }
+
+ @Rpc(description = "Lists the IDs (phone numbers or hex hashes) of the current calls.")
+ public ArrayList<String> telecomPhoneGetCallIds() {
+ ArrayList<String> ids = new ArrayList<String>();
+ for (Call call : InCallServiceImpl.mPhone.getCalls()) {
+ ids.add(getCallId(call));
+ }
+ return ids;
+ }
+
+ @Rpc(description = "Sets the audio route (SPEAKER, BLUETOOTH, etc...).")
+ public void telecomPhoneSetAudioRoute(@RpcParameter(name = "route") String route) {
+ int r = 0;
+ if (route == "BLUETOOTH") {
+ r = AudioState.ROUTE_BLUETOOTH;
+ } else if (route == "EARPIECE") {
+ r = AudioState.ROUTE_EARPIECE;
+ } else if (route == "SPEAKER") {
+ r = AudioState.ROUTE_SPEAKER;
+ } else if (route == "WIRED_HEADSET") {
+ r = AudioState.ROUTE_WIRED_HEADSET;
+ } else if (route == "ALL") {
+ r = AudioState.ROUTE_ALL;
+ } else if (route == "WIRED_OR_EARPIECE") {
+ r = AudioState.ROUTE_WIRED_OR_EARPIECE;
+ }
+ InCallServiceImpl.mPhone.setAudioRoute(r);
+ }
+
+ @Rpc(description = "Turns the proximity sensor off. If screenOnImmediately is true, the screen will be turned on immediately")
+ public void telecomPhoneSetProximitySensorOff(
+ @RpcParameter(name = "screenOnImmediately") Boolean screenOnImmediately) {
+ InCallServiceImpl.mPhone.setProximitySensorOff(screenOnImmediately);
+ }
+
+ @Rpc(description = "Obtains the current call audio state of the phone.")
+ public AudioState telecomPhoneGetAudioState() {
+ return InCallServiceImpl.mPhone.getAudioState();
+ }
+
@Rpc(description = "Silences the rigner if there's a ringing call.")
public void telecomSilenceRinger() {
mTelecomManager.silenceRinger();
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
index f42ba49..7846173 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
@@ -50,6 +50,7 @@
import android.net.wifi.p2p.WifiP2pInfo;
import android.os.Bundle;
import android.os.ParcelUuid;
+import android.telecom.AudioState;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telephony.CellLocation;
@@ -108,6 +109,9 @@
if (data instanceof Address) {
return buildJsonAddress((Address) data);
}
+ if (data instanceof AudioState) {
+ return buildJsonAudioState((AudioState) data);
+ }
if (data instanceof Location) {
return buildJsonLocation((Location) data);
}
@@ -207,6 +211,13 @@
// throw new JSONException("Failed to build JSON result. " + data.getClass().getName());
}
+ private static JSONObject buildJsonAudioState(AudioState data) throws JSONException {
+ JSONObject state = new JSONObject();
+ state.put("isMuted", data.isMuted);
+ state.put("AudioRoute", AudioState.audioRouteToString(data.route));
+ return state;
+ }
+
private static Object buildDisplayMetrics(DisplayMetrics data) throws JSONException {
JSONObject dm = new JSONObject();
dm.put("widthPixels", data.widthPixels);
diff --git a/ScriptingLayerForAndroid/AndroidManifest.xml b/ScriptingLayerForAndroid/AndroidManifest.xml
index 6f3ddb1..d971b32 100644
--- a/ScriptingLayerForAndroid/AndroidManifest.xml
+++ b/ScriptingLayerForAndroid/AndroidManifest.xml
@@ -11,8 +11,11 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.BIND_INCALL_SERVICE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
+ <uses-permission android:name="com.android.server.telecom.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION" />
+ <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -156,6 +159,12 @@
<activity android:name="org.connectbot.HelpTopicActivity" android:configChanges="keyboardHidden|orientation" />
<service android:name=".activity.ScriptingLayerService" />
<service android:name=".activity.TriggerService" />
+ <service android:name="com.googlecode.android_scripting.facade.tele.InCallServiceImpl"
+ android:permission="android.permission.BIND_INCALL_SERVICE" >
+ <intent-filter>
+ <action android:name="android.telecom.InCallService"/>
+ </intent-filter>
+ </service>
<activity android:name=".activity.InterpreterManager" android:launchMode="singleTask" android:configChanges="keyboardHidden|orientation" />
<activity android:name=".activity.LogcatViewer" android:launchMode="singleTask" android:configChanges="keyboardHidden|orientation" />
<activity android:name=".activity.ScriptsLiveFolder" android:label="Scripts" android:icon="@drawable/live_folder" android:configChanges="keyboardHidden|orientation">