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