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/res/values/non_localizable_defaults.xml b/res/values/non_localizable_defaults.xml
index 5af444e96..1b5bd2618 100644
--- a/res/values/non_localizable_defaults.xml
+++ b/res/values/non_localizable_defaults.xml
@@ -5,18 +5,18 @@
443
@string/tunnel_mode_entry_value_disabled
- false
+ true
true
true
true
- true
+ false
true
- true
+ false
true
- true
+ false
false
- false
+ true
true
true
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..16a26d6d7 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");
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);
}
diff --git a/src/org/linphone/RemoteProvisioning.java b/src/org/linphone/RemoteProvisioning.java
index 125dc5bcf..c44af7ad2 100644
--- a/src/org/linphone/RemoteProvisioning.java
+++ b/src/org/linphone/RemoteProvisioning.java
@@ -19,6 +19,7 @@ public class RemoteProvisioning {
String mRPAddress;
String mSchema;
String mLocalLP;
+ boolean value;
public RemoteProvisioningThread(final String RPAddress, final String LocalLP, final String schema) {
this.mRPAddress = RPAddress;
@@ -28,6 +29,7 @@ public class RemoteProvisioning {
public void run() {
try {
+ value = false;
Log.i("Download remote provisioning file from " + mRPAddress);
URL url = new URL(mRPAddress);
URLConnection ucon = url.openConnection();
@@ -68,6 +70,7 @@ public class RemoteProvisioning {
} else {
lp.sync();
}
+ value = true;
Log.i("Remote provisioning ok");
} catch (MalformedURLException e) {
Log.e("Invalid remote provisioning url: " + e.getLocalizedMessage());
@@ -81,7 +84,7 @@ public class RemoteProvisioning {
}
};
- static void download(String address, String lpfile, boolean check) {
+ static boolean download(String address, String lpfile, boolean check) {
try {
String schema = null;
if(check) {
@@ -92,13 +95,15 @@ public class RemoteProvisioning {
thread.start();
thread.wait();
}
+ return thread.value;
} catch (InterruptedException e) {
Log.e(e);
}
+ return false;
}
- static void download(String address, String lpfile) {
- download(address, lpfile, true);
+ static boolean download(String address, String lpfile) {
+ return download(address, lpfile, true);
}
static boolean isAvailable() {
diff --git a/src/org/linphone/setup/SetupActivity.java b/src/org/linphone/setup/SetupActivity.java
index 994a4e282..286d99459 100644
--- a/src/org/linphone/setup/SetupActivity.java
+++ b/src/org/linphone/setup/SetupActivity.java
@@ -139,7 +139,7 @@ public class SetupActivity extends FragmentActivity implements OnClickListener {
}
private void launchEchoCancellerCalibration(boolean sendEcCalibrationResult) {
- if (!Hacks.hasBuiltInEchoCanceller() && !mPref.getBoolean(getString(R.string.first_launch_suceeded_once_key), false)) {
+ if (LinphoneManager.getLc().needsEchoCalibration() && !mPref.getBoolean(getString(R.string.first_launch_suceeded_once_key), false)) {
EchoCancellerCalibrationFragment fragment = new EchoCancellerCalibrationFragment();
fragment.enableEcCalibrationResultSending(sendEcCalibrationResult);
changeFragment(fragment);
diff --git a/submodules/linphone b/submodules/linphone
index 5b14c78bb..d83779419 160000
--- a/submodules/linphone
+++ b/submodules/linphone
@@ -1 +1 @@
-Subproject commit 5b14c78bbd4a22494ec54531129e5ab30b4d1667
+Subproject commit d8377941920f52ffbedf2b8fb90b2e884e1a0dc5