Added bluetooth audio route for Android 3.0+
This commit is contained in:
parent
d414e1f533
commit
a1f4edb281
7 changed files with 162 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@ obj
|
||||||
gen
|
gen
|
||||||
bin
|
bin
|
||||||
doc
|
doc
|
||||||
|
default.properties
|
||||||
local.properties
|
local.properties
|
||||||
project.properties
|
project.properties
|
||||||
tests/*$py.class
|
tests/*$py.class
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<!-- Needed to use our own Contact editor -->
|
<!-- Needed to use our own Contact editor -->
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
<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"/>
|
<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>
|
<intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action></intent-filter>
|
||||||
</receiver>
|
</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">
|
<receiver android:name="org.linphone.BootReceiver">
|
||||||
<intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"></action></intent-filter>
|
<intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"></action></intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
|
|
||||||
<bool name="hash_images_as_name_before_upload">true</bool>
|
<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_every_log">false</bool>
|
||||||
<bool name="disable_all_security_features_for_markets">false</bool> <!-- Disable TLS/SRTP/ZRTP -->
|
<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 -->
|
<bool name="disable_all_patented_codecs_for_markets">false</bool> <!-- Disable MPEG4/H264 -->
|
||||||
|
|
40
src/org/linphone/BluetoothManager.java
Normal file
40
src/org/linphone/BluetoothManager.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -166,6 +166,10 @@ public class InCallActivity extends FragmentActivity implements
|
||||||
callFragment.setArguments(getIntent().getExtras());
|
callFragment.setArguments(getIntent().getExtras());
|
||||||
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer, callFragment).commitAllowingStateLoss();
|
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
|
@Override
|
||||||
|
@ -475,12 +479,16 @@ public class InCallActivity extends FragmentActivity implements
|
||||||
if (isSpeakerEnabled) {
|
if (isSpeakerEnabled) {
|
||||||
LinphoneManager.getInstance().routeAudioToSpeaker();
|
LinphoneManager.getInstance().routeAudioToSpeaker();
|
||||||
speaker.setBackgroundResource(R.drawable.speaker_on);
|
speaker.setBackgroundResource(R.drawable.speaker_on);
|
||||||
|
LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
|
||||||
} else {
|
} else {
|
||||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
LinphoneManager.getInstance().routeAudioToReceiver();
|
||||||
speaker.setBackgroundResource(R.drawable.speaker_off);
|
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() {
|
private void pauseOrResumeCall() {
|
||||||
LinphoneCore lc = LinphoneManager.getLc();
|
LinphoneCore lc = LinphoneManager.getLc();
|
||||||
|
@ -969,8 +977,13 @@ public class InCallActivity extends FragmentActivity implements
|
||||||
switchVideo(isVideoEnabled, false);
|
switchVideo(isVideoEnabled, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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).
|
// The following should not be needed except some devices need it (e.g. Galaxy S).
|
||||||
LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
|
LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
isMicMuted = LinphoneManager.getLc().isMicMuted();
|
isMicMuted = LinphoneManager.getLc().isMicMuted();
|
||||||
enableAndRefreshInCallActions();
|
enableAndRefreshInCallActions();
|
||||||
|
|
|
@ -76,7 +76,12 @@ import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration.
|
||||||
import org.linphone.mediastream.video.capture.hwconf.Hacks;
|
import org.linphone.mediastream.video.capture.hwconf.Hacks;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
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.BroadcastReceiver;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -142,6 +147,10 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
|
|
||||||
private WakeLock mIncallWakeLock;
|
private WakeLock mIncallWakeLock;
|
||||||
|
|
||||||
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
|
private BluetoothHeadset mBluetoothHeadset;
|
||||||
|
private BluetoothProfile.ServiceListener mProfileListener;
|
||||||
|
|
||||||
private static List<LinphoneSimpleListener> simpleListeners = new ArrayList<LinphoneSimpleListener>();
|
private static List<LinphoneSimpleListener> simpleListeners = new ArrayList<LinphoneSimpleListener>();
|
||||||
public static void addListener(LinphoneSimpleListener listener) {
|
public static void addListener(LinphoneSimpleListener listener) {
|
||||||
if (!simpleListeners.contains(listener)) {
|
if (!simpleListeners.contains(listener)) {
|
||||||
|
@ -190,9 +199,23 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
private BroadcastReceiver mKeepAliveReceiver = new KeepAliveReceiver();
|
private BroadcastReceiver mKeepAliveReceiver = new KeepAliveReceiver();
|
||||||
|
|
||||||
private void routeAudioToSpeakerHelper(boolean speakerOn) {
|
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)) {
|
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() {
|
public void routeAudioToSpeaker() {
|
||||||
|
@ -217,6 +240,66 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
routeAudioToSpeakerHelper(false);
|
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(
|
public synchronized static final LinphoneManager createAndStart(
|
||||||
Context c, LinphoneServiceListener listener) {
|
Context c, LinphoneServiceListener listener) {
|
||||||
if (instance != null)
|
if (instance != null)
|
||||||
|
@ -463,6 +546,11 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||||
lFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
lFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||||
mServiceContext.registerReceiver(mKeepAliveReceiver, lFilter);
|
mServiceContext.registerReceiver(mKeepAliveReceiver, lFilter);
|
||||||
|
|
||||||
|
boolean routeToBT = mServiceContext.getResources().getBoolean(R.bool.route_audio_to_bluetooth_if_available);
|
||||||
|
if (routeToBT) {
|
||||||
|
startBluetooth();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Log.e(e, "Cannot start linphone");
|
Log.e(e, "Cannot start linphone");
|
||||||
|
@ -474,7 +562,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
|
||||||
copyIfNotExist(R.raw.ringback,mRingbackSoundFile);
|
copyIfNotExist(R.raw.ringback,mRingbackSoundFile);
|
||||||
copyIfNotExist(R.raw.toy_mono,mPauseSoundFile);
|
copyIfNotExist(R.raw.toy_mono,mPauseSoundFile);
|
||||||
copyFromPackage(R.raw.linphonerc, new File(mLinphoneInitialConfigFile).getName());
|
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());
|
copyIfNotExist(R.raw.rootca, new File(mLinphoneRootCaFile).getName());
|
||||||
}
|
}
|
||||||
private void copyIfNotExist(int ressourceId,String target) throws IOException {
|
private void copyIfNotExist(int ressourceId,String target) throws IOException {
|
||||||
|
|
|
@ -56,7 +56,7 @@ public interface LinphoneSimpleListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface LinphoneOnAudioChangedListener extends LinphoneSimpleListener {
|
public static interface LinphoneOnAudioChangedListener extends LinphoneSimpleListener {
|
||||||
public enum AudioState {EARPIECE, SPEAKER}
|
public enum AudioState {EARPIECE, SPEAKER, BLUETOOTH}
|
||||||
void onAudioStateChanged(AudioState state);
|
void onAudioStateChanged(AudioState state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue