diff --git a/.gitignore b/.gitignore
index f6721f6f3..426ff4445 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ obj
gen
bin
doc
+default.properties
local.properties
project.properties
tests/*$py.class
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5a9dd168e..d649a1660 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -31,6 +31,9 @@
+
+
+
@@ -104,6 +107,14 @@
+
+
+
+
+
+
+
+
diff --git a/res/values/non_localizable_custom.xml b/res/values/non_localizable_custom.xml
index 96e14b649..56c2e3afc 100644
--- a/res/values/non_localizable_custom.xml
+++ b/res/values/non_localizable_custom.xml
@@ -55,6 +55,8 @@
true
+ true
+
false
false
false
diff --git a/src/org/linphone/BluetoothManager.java b/src/org/linphone/BluetoothManager.java
new file mode 100644
index 000000000..5e6287442
--- /dev/null
+++ b/src/org/linphone/BluetoothManager.java
@@ -0,0 +1,40 @@
+package org.linphone;
+
+import org.linphone.mediastream.Log;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class BluetoothManager extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ boolean routeToBT = context.getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
+ if (!routeToBT)
+ return;
+
+ String action = intent.getAction();
+ LinphoneManager lm = LinphoneManager.getInstance();
+
+ if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
+ Log.e("Bluetooth Received Event" + " ACTION_ACL_DISCONNECTED" );
+
+ if (lm != null) {
+ lm.uninitBluetooth();
+ lm.routeAudioToReceiver();
+ }
+ } else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
+ Log.e("Bluetooth Received Event" + " ACTION_ACL_CONNECTED" );
+
+ if (lm != null) {
+ lm.routeToBluetoothIfAvailable();
+ }
+ } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ Log.e("Bluetooth state changed!");
+ if (lm != null) {
+ lm.startBluetooth();
+ }
+ }
+ }
+}
diff --git a/src/org/linphone/InCallActivity.java b/src/org/linphone/InCallActivity.java
index 5c56d344c..706a920bb 100644
--- a/src/org/linphone/InCallActivity.java
+++ b/src/org/linphone/InCallActivity.java
@@ -166,6 +166,10 @@ public class InCallActivity extends FragmentActivity implements
callFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer, callFragment).commitAllowingStateLoss();
}
+
+ boolean routeToBT = getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
+ if (routeToBT && LinphoneManager.isInstanciated() && !isSpeakerEnabled)
+ LinphoneManager.getInstance().routeToBluetoothIfAvailable();
}
@Override
@@ -475,11 +479,15 @@ public class InCallActivity extends FragmentActivity implements
if (isSpeakerEnabled) {
LinphoneManager.getInstance().routeAudioToSpeaker();
speaker.setBackgroundResource(R.drawable.speaker_on);
+ LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
} else {
LinphoneManager.getInstance().routeAudioToReceiver();
speaker.setBackgroundResource(R.drawable.speaker_off);
+
+ boolean routeToBT = getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
+ if (!routeToBT)
+ LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
}
- LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
}
private void pauseOrResumeCall() {
@@ -969,8 +977,13 @@ public class InCallActivity extends FragmentActivity implements
switchVideo(isVideoEnabled, false);
}
- // The following should not be needed except some devices need it (e.g. Galaxy S).
- LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
+ boolean routeToBT = getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
+ if (routeToBT && LinphoneManager.isInstanciated() && !isSpeakerEnabled) {
+ LinphoneManager.getInstance().routeToBluetoothIfAvailable();
+ } else {
+ // The following should not be needed except some devices need it (e.g. Galaxy S).
+ LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
+ }
isMicMuted = LinphoneManager.getLc().isMicMuted();
enableAndRefreshInCallActions();
diff --git a/src/org/linphone/LinphoneManager.java b/src/org/linphone/LinphoneManager.java
index e7afcb1b7..ecbea4552 100644
--- a/src/org/linphone/LinphoneManager.java
+++ b/src/org/linphone/LinphoneManager.java
@@ -76,7 +76,12 @@ import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration.
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -141,6 +146,10 @@ public final class LinphoneManager implements LinphoneCoreListener {
private static boolean sExited;
private WakeLock mIncallWakeLock;
+
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothHeadset mBluetoothHeadset;
+ private BluetoothProfile.ServiceListener mProfileListener;
private static List simpleListeners = new ArrayList();
public static void addListener(LinphoneSimpleListener listener) {
@@ -190,9 +199,23 @@ public final class LinphoneManager implements LinphoneCoreListener {
private BroadcastReceiver mKeepAliveReceiver = new KeepAliveReceiver();
private void routeAudioToSpeakerHelper(boolean speakerOn) {
- mLc.enableSpeaker(speakerOn);
+ boolean routeToBluetoothEnabled = false;
+ if (!speakerOn) {
+ boolean routeToBT = mServiceContext.getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
+ if (!routeToBT || (routeToBT && !routeToBluetoothIfAvailable())) {
+ mLc.enableSpeaker(false);
+ uninitBluetooth();
+ } else {
+ Log.d("Routing audio to bluetooth headset");
+ routeToBluetoothEnabled = true;
+ }
+ } else {
+ mLc.enableSpeaker(true);
+ uninitBluetooth();
+ }
+
for (LinphoneOnAudioChangedListener listener : getSimpleListeners(LinphoneOnAudioChangedListener.class)) {
- listener.onAudioStateChanged(speakerOn ? AudioState.SPEAKER : AudioState.EARPIECE);
+ listener.onAudioStateChanged(speakerOn ? AudioState.SPEAKER : (routeToBluetoothEnabled ? AudioState.BLUETOOTH : AudioState.EARPIECE));
}
}
public void routeAudioToSpeaker() {
@@ -216,6 +239,66 @@ public final class LinphoneManager implements LinphoneCoreListener {
public void routeAudioToReceiver() {
routeAudioToSpeakerHelper(false);
}
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void startBluetooth() {
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30) && mBluetoothAdapter.isEnabled()) {
+ mProfileListener = new BluetoothProfile.ServiceListener() {
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET) {
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ Log.d("Bluetooth headset connected");
+ routeToBluetoothIfAvailable();
+ }
+ }
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET) {
+ mBluetoothHeadset = null;
+ Log.d("Bluetooth headset disconnected");
+ routeAudioToReceiver();
+ }
+ }
+ };
+ mBluetoothAdapter.getProfileProxy(mServiceContext, mProfileListener, BluetoothProfile.HEADSET);
+ } else {
+ routeAudioToReceiver();
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public boolean routeToBluetoothIfAvailable() {
+ BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30) && mBluetoothAdapter.isEnabled() && mAudioManager.isBluetoothScoAvailableOffCall()) {
+ mAudioManager.setBluetoothScoOn(true);
+ mAudioManager.startBluetoothSco();
+
+ boolean connected = false;
+ if (mBluetoothHeadset != null) {
+ List devices = mBluetoothHeadset.getConnectedDevices();
+ for (final BluetoothDevice dev : devices) {
+ connected |= mBluetoothHeadset.getConnectionState(dev) == BluetoothHeadset.STATE_CONNECTED;
+ }
+ }
+
+ if (!connected) {
+ Log.d("No bluetooth device available");
+ uninitBluetooth();
+ }
+ return connected;
+ }
+ return false;
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void uninitBluetooth() {
+ if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30) && mAudioManager != null) {
+ mAudioManager.stopBluetoothSco();
+ mAudioManager.setBluetoothScoOn(false);
+ }
+ }
public synchronized static final LinphoneManager createAndStart(
Context c, LinphoneServiceListener listener) {
@@ -463,6 +546,11 @@ public final class LinphoneManager implements LinphoneCoreListener {
IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
lFilter.addAction(Intent.ACTION_SCREEN_OFF);
mServiceContext.registerReceiver(mKeepAliveReceiver, lFilter);
+
+ boolean routeToBT = mServiceContext.getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
+ if (routeToBT) {
+ startBluetooth();
+ }
}
catch (Exception e) {
Log.e(e, "Cannot start linphone");
@@ -474,7 +562,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
copyIfNotExist(R.raw.ringback,mRingbackSoundFile);
copyIfNotExist(R.raw.toy_mono,mPauseSoundFile);
copyFromPackage(R.raw.linphonerc, new File(mLinphoneInitialConfigFile).getName());
- copyIfNotExist(R.raw.lpconfig, new File(mLPConfigXsd).getName());
+// copyIfNotExist(R.raw.lpconfig, new File(mLPConfigXsd).getName());
copyIfNotExist(R.raw.rootca, new File(mLinphoneRootCaFile).getName());
}
private void copyIfNotExist(int ressourceId,String target) throws IOException {
diff --git a/src/org/linphone/LinphoneSimpleListener.java b/src/org/linphone/LinphoneSimpleListener.java
index b34fa7469..3d2fe0d7f 100644
--- a/src/org/linphone/LinphoneSimpleListener.java
+++ b/src/org/linphone/LinphoneSimpleListener.java
@@ -56,7 +56,7 @@ public interface LinphoneSimpleListener {
}
public static interface LinphoneOnAudioChangedListener extends LinphoneSimpleListener {
- public enum AudioState {EARPIECE, SPEAKER}
+ public enum AudioState {EARPIECE, SPEAKER, BLUETOOTH}
void onAudioStateChanged(AudioState state);
}