Bluetooth headset support thanks to Doğancan Kefeli

This commit is contained in:
Sylvain Berfini 2012-08-07 14:19:15 +02:00
parent b2694a1393
commit 0cee0e357b
8 changed files with 58 additions and 19 deletions

View file

@ -19,6 +19,9 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_LOGS" /> <uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Needed to use bluetooth headset -->
<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"/>

View file

@ -45,6 +45,7 @@
<string name="first_launch_suceeded_once_key">first_launch_suceeded_once_key</string> <string name="first_launch_suceeded_once_key">first_launch_suceeded_once_key</string>
<string name="pref_wifi_only_key">pref_wifi_only_key</string> <string name="pref_wifi_only_key">pref_wifi_only_key</string>
<string name="pref_use_bluetooth_if_available_key">pref_use_bluetooth_if_available_key</string>
<string name="pref_video_use_front_camera_key">pref_video_use_front_camera_key</string> <string name="pref_video_use_front_camera_key">pref_video_use_front_camera_key</string>
<string name="pref_video_codec_h263_key">pref_video_codec_h263_key</string> <string name="pref_video_codec_h263_key">pref_video_codec_h263_key</string>

View file

@ -240,6 +240,8 @@
<string name="pref_sipaccounts">SIP Accounts</string> <string name="pref_sipaccounts">SIP Accounts</string>
<string name="pref_wifi_only">Use wifi only</string> <string name="pref_wifi_only">Use wifi only</string>
<string name="pref_use_bluetooth_if_available">Use bluetooth headset</string>
<string name="pref_use_bluetooth_if_available_summary">Route incoming audio to bluetooth headset if available.</string>
<string name="wizard_failed">An error occurred, try again later.</string> <string name="wizard_failed">An error occurred, try again later.</string>
<string name="wizard_server_unavailable">Server unreachable, verify your internet connection.</string> <string name="wizard_server_unavailable">Server unreachable, verify your internet connection.</string>

View file

@ -56,6 +56,10 @@
<PreferenceCategory android:title="@string/pref_audio"> <PreferenceCategory android:title="@string/pref_audio">
<CheckBoxPreference android:key="@string/pref_use_bluetooth_if_available_key"
android:defaultValue="true"
android:title="@string/pref_use_bluetooth_if_available" android:summary="@string/pref_use_bluetooth_if_available_summary"/>
<CheckBoxPreference android:key="@string/pref_echo_limiter_key" <CheckBoxPreference android:key="@string/pref_echo_limiter_key"
android:title="@string/pref_echo_limiter" android:summary="@string/pref_echo_limiter_summary"/> android:title="@string/pref_echo_limiter" android:summary="@string/pref_echo_limiter_summary"/>

View file

@ -49,6 +49,7 @@ import java.util.TimerTask;
import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener; import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener.AudioState; import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener.AudioState;
import org.linphone.LinphoneSimpleListener.LinphoneServiceListener; import org.linphone.LinphoneSimpleListener.LinphoneServiceListener;
import org.linphone.compatibility.Compatibility;
import org.linphone.core.CallDirection; import org.linphone.core.CallDirection;
import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneAuthInfo; import org.linphone.core.LinphoneAuthInfo;
@ -195,33 +196,37 @@ public final class LinphoneManager implements LinphoneCoreListener {
private BroadcastReceiver mKeepAliveReceiver = new KeepAliveReceiver(); private BroadcastReceiver mKeepAliveReceiver = new KeepAliveReceiver();
private native void hackSpeakerState(boolean speakerOn); private native void hackSpeakerState(boolean speakerOn);
private static void sRouteAudioToSpeakerHelperHelper(boolean speakerOn) { private static void sRouteAudioToSpeakerOrBluetoothHelper(boolean speakerOn) {
getInstance().routeAudioToSpeakerHelperHelper(speakerOn); getInstance().routeAudioToSpeakerOrBluetoothHelper(speakerOn);
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void routeAudioToSpeakerHelperHelper(boolean speakerOn) { private void routeAudioToSpeakerOrBluetoothHelper(boolean speakerOn) {
boolean different = isSpeakerOn() ^ speakerOn; boolean different = isSpeakerOn() ^ speakerOn;
if (!different) { if (!different) {
Log.d("Skipping change audio route by the same route ", Log.d("Skipping change audio route by the same route ",
speakerOn ? "speaker" : "earpiece"); speakerOn ? "speaker" : "earpiece");
return; return;
} }
if (Hacks.needGalaxySAudioHack() || sLPref.useGalaxySHack()) if (mPref.getBoolean(getString(R.string.pref_use_bluetooth_if_available), true) && Compatibility.enableBluetoothHeadset(mAudioManager)) {
setAudioModeIncallForGalaxyS(); return;
if (sLPref.useSpecificAudioModeHack() != -1)
mAudioManager.setMode(sLPref.useSpecificAudioModeHack());
if (Hacks.needRoutingAPI() || sLPref.useAudioRoutingAPIHack()) {
mAudioManager.setRouting(
MODE_NORMAL,
speakerOn? ROUTE_SPEAKER : ROUTE_EARPIECE,
AudioManager.ROUTE_ALL);
} else { } else {
mAudioManager.setSpeakerphoneOn(speakerOn); if (Hacks.needGalaxySAudioHack() || sLPref.useGalaxySHack())
} setAudioModeIncallForGalaxyS();
for (LinphoneOnAudioChangedListener listener : getSimpleListeners(LinphoneOnAudioChangedListener.class)) {
listener.onAudioStateChanged(speakerOn ? AudioState.SPEAKER : AudioState.EARPIECE); if (sLPref.useSpecificAudioModeHack() != -1)
mAudioManager.setMode(sLPref.useSpecificAudioModeHack());
if (Hacks.needRoutingAPI() || sLPref.useAudioRoutingAPIHack()) {
mAudioManager.setRouting(
MODE_NORMAL,
speakerOn? ROUTE_SPEAKER : ROUTE_EARPIECE,
AudioManager.ROUTE_ALL);
} else {
mAudioManager.setSpeakerphoneOn(speakerOn);
}
for (LinphoneOnAudioChangedListener listener : getSimpleListeners(LinphoneOnAudioChangedListener.class)) {
listener.onAudioStateChanged(speakerOn ? AudioState.SPEAKER : AudioState.EARPIECE);
}
} }
} }
private synchronized void routeAudioToSpeakerHelper(boolean speakerOn) { private synchronized void routeAudioToSpeakerHelper(boolean speakerOn) {
@ -230,7 +235,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
Log.d("Hack to have speaker=",speakerOn," while on call"); Log.d("Hack to have speaker=",speakerOn," while on call");
hackSpeakerState(speakerOn); hackSpeakerState(speakerOn);
} else { } else {
routeAudioToSpeakerHelperHelper(speakerOn); routeAudioToSpeakerOrBluetoothHelper(speakerOn);
} }
} }

View file

@ -28,6 +28,7 @@ import static org.linphone.R.string.pref_echo_cancellation_key;
import static org.linphone.R.string.pref_echo_canceller_calibration_key; import static org.linphone.R.string.pref_echo_canceller_calibration_key;
import static org.linphone.R.string.pref_echo_limiter_key; import static org.linphone.R.string.pref_echo_limiter_key;
import static org.linphone.R.string.pref_media_encryption_key; import static org.linphone.R.string.pref_media_encryption_key;
import static org.linphone.R.string.pref_use_bluetooth_if_available_key;
import static org.linphone.R.string.pref_video_enable_key; import static org.linphone.R.string.pref_video_enable_key;
import java.util.ArrayList; import java.util.ArrayList;
@ -259,6 +260,10 @@ public class PreferencesActivity extends LinphonePreferencesActivity implements
} }
findPreference(pref_echo_limiter_key).setEnabled(true); findPreference(pref_echo_limiter_key).setEnabled(true);
if (!Version.sdkAboveOrEqual(8)) {
uncheckAndDisableCheckbox(pref_use_bluetooth_if_available_key);
}
initializeMediaEncryptionPreferences(); initializeMediaEncryptionPreferences();
detectAudioCodec(pref_codec_amr_key,"AMR", 8000, false); detectAudioCodec(pref_codec_amr_key,"AMR", 8000, false);

View file

@ -1,6 +1,7 @@
package org.linphone.compatibility; package org.linphone.compatibility;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.AudioManager;
import android.view.Display; import android.view.Display;
/* /*
@ -30,4 +31,14 @@ public class ApiEightPlus {
public static int getRotation(Display display) { public static int getRotation(Display display) {
return display.getRotation(); return display.getRotation();
} }
public static boolean enableBluetoothHeadset(AudioManager mAudioManager) {
if (mAudioManager.isBluetoothScoAvailableOffCall()){
mAudioManager.setBluetoothScoOn(true);
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
mAudioManager.startBluetoothSco();
return true;
}
return false;
}
} }

View file

@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
import android.view.Display; import android.view.Display;
import android.view.Window; import android.view.Window;
@ -183,4 +184,11 @@ public class Compatibility {
ApiFivePlus.setFullScreen(window); ApiFivePlus.setFullScreen(window);
} }
} }
public static boolean enableBluetoothHeadset(AudioManager mAudioManager) {
if (Version.sdkAboveOrEqual(8)) {
return ApiEightPlus.enableBluetoothHeadset(mAudioManager);
}
return false;
}
} }