Added bluetooth audio route for Android 3.0+

This commit is contained in:
Sylvain Berfini 2013-02-18 10:26:00 +01:00
parent d414e1f533
commit a1f4edb281
7 changed files with 162 additions and 7 deletions

1
.gitignore vendored
View file

@ -3,6 +3,7 @@ obj
gen
bin
doc
default.properties
local.properties
project.properties
tests/*$py.class

View file

@ -31,6 +31,9 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Needed to use our own Contact editor -->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<!-- Needed to route the audio to the bluetooth headset if available -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true"/>
@ -104,6 +107,14 @@
<intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action></intent-filter>
</receiver>
<receiver android:name="org.linphone.BluetoothManager">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
<action android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"/>
</intent-filter>
</receiver>
<receiver android:name="org.linphone.BootReceiver">
<intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"></action></intent-filter>
</receiver>

View file

@ -55,6 +55,8 @@
<bool name="hash_images_as_name_before_upload">true</bool>
<bool name="route_audio_to_bluetooth_if_available">true</bool>
<bool name="disable_every_log">false</bool>
<bool name="disable_all_security_features_for_markets">false</bool> <!-- Disable TLS/SRTP/ZRTP -->
<bool name="disable_all_patented_codecs_for_markets">false</bool> <!-- Disable MPEG4/H264 -->

View file

@ -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();
}
}
}
}

View file

@ -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();

View file

@ -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;
@ -142,6 +147,10 @@ public final class LinphoneManager implements LinphoneCoreListener {
private WakeLock mIncallWakeLock;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothHeadset mBluetoothHeadset;
private BluetoothProfile.ServiceListener mProfileListener;
private static List<LinphoneSimpleListener> simpleListeners = new ArrayList<LinphoneSimpleListener>();
public static void addListener(LinphoneSimpleListener listener) {
if (!simpleListeners.contains(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() {
@ -217,6 +240,66 @@ public final class LinphoneManager implements LinphoneCoreListener {
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<BluetoothDevice> 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) {
if (instance != null)
@ -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 {

View file

@ -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);
}