说明
突然有一天, 我发现了linphone的蓝牙耳机功能, 但是由于功能进度的需要,打算将蓝牙去掉.
步骤
1. 线去掉核心.
主要是BluetoothManager类, 后边详细贴出.
2. 去掉LinphoneManager中的Bluetooth处理
private void routeAudioToSpeakerHelper(boolean speakerOn) {
Log.w("Routing audio to " + (speakerOn ? "speaker" : "earpiece") + ", disabling bluetooth audio route");
BluetoothManager.getInstance().disableBluetoothSCO();
if (checkmLcIsValide(mLc)) {
mLc.enableSpeaker(speakerOn);
}
}
public static void BluetoothManagerDestroy() {
if (BluetoothManager.getInstance() != null)
BluetoothManager.getInstance().destroy();
}
callState()
else if (state == State.IncomingReceived || (state == State.CallIncomingEarlyMedia && mR.getBoolean(R.bool.allow_ringing_while_early_media))) {
LinphoneUtils._log("LinphoneManager#config", "else if (state == State.IncomingReceived || (state == State.CallIncomingEarlyMedia && mR.getBoolean(R.bool.allow_ringing_while_early_media)))");
// Brighten screen for at least 10 seconds
if (mLc.getCallsNb() == 1) {
requestAudioFocus(STREAM_RING);
BluetoothManager.getInstance().disableBluetoothSCO(); // Just in case
ringingCall = call;
startRinging();
// otherwise there is the beep
}
}
public void startBluetooth() {
if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
BluetoothManager.getInstance().routeAudioToBluetooth();
// Hack to ensure the bluetooth route is really used
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
BluetoothManager.getInstance().routeAudioToBluetooth();
}
}, 500);
}
}
// You may need to call galaxys audio hack after this method
if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
if (mServiceContext.getResources().getBoolean(R.bool.isTablet)) {
Log.d("Stopped ringing, routing back to speaker");
routeAudioToSpeaker();
} else {
Log.d("Stopped ringing, routing back to earpiece");
routeAudioToReceiver();
}
}
3. CallActivity.java中的Bluetooth应用
onCreate
if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
BluetoothManager.getInstance().initBluetooth();
}
callstate
if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
BluetoothManager.getInstance().routeAudioToBluetooth();
}
initUI
if (false/*BluetoothManager.getInstance().isBluetoothHeadsetAvailable()*/) {
try {
audioRoute.setVisibility(View.VISIBLE);
speaker.setVisibility(View.GONE);
} catch (NullPointerException npe) {
Log.e("Bluetooth: Audio routes menu disabled on tablets for now (2)");
}
} else {
try {
audioRoute.setVisibility(View.GONE);
speaker.setVisibility(View.VISIBLE);
} catch (NullPointerException npe) {
Log.e("Bluetooth: Audio routes menu disabled on tablets for now (3)");
}
}
refreshInCallActions
if (BluetoothManager.getInstance().isUsingBluetoothAudioRoute()) {
routeEarpiece.setImageResource(R.drawable.route_earpiece);
routeBluetooth.setImageResource(R.drawable.route_bluetooth_selected);
} else {
routeEarpiece.setImageResource(R.drawable.route_earpiece_selected);
routeBluetooth.setImageResource(R.drawable.route_bluetooth);
}
onClick
else if (id == R.id.route_bluetooth) {
if (BluetoothManager.getInstance().routeAudioToBluetooth()) {
isSpeakerEnabled = false;
routeBluetooth.setImageResource(R.drawable.route_bluetooth_selected);
routeSpeaker.setImageResource(R.drawable.route_speaker);
routeEarpiece.setImageResource(R.drawable.route_earpiece);
}
hideOrDisplayAudioRoutes();
showVideoView
if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
Log.w("Bluetooth not available, using speaker");
LinphoneManager.getInstance().routeAudioToSpeaker();
isSpeakerEnabled = true;
}
4. LinphoneLauncherActivty中Bluetooth的应用
onCreate
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
BluetoothManager.getInstance().initBluetooth();
}
// We need LinphoneService to start bluetoothManager
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
BluetoothManager.getInstance().initBluetooth();
}
stopLinphoneService
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
BluetoothManager.getInstance().destroy();
}
5. LinphoneService中Bluetooth的应用
startLinphoneService()
handler.post(new Runnable() {
@Override
public void run() {
//WE need LinphoneService to start bluetoothManager
if (Version.sdkAboveOrEqual(Version.API11_HONEYCOMB_30)) {
BluetoothManager.getInstance().initBluetooth();
}
}
});
超长代码
BluetoothManager.java
package org.linphone;
/*
BluetoothManager.java
Copyright (C) 2012 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.util.List;
import org.linphone.mediastream.Log;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAssignedNumbers;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Build;
/**
* @author Sylvain Berfini
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class BluetoothManager extends BroadcastReceiver {
public int PLANTRONICS_BUTTON_PRESS = 1;
public int PLANTRONICS_BUTTON_LONG_PRESS = 2;
public int PLANTRONICS_BUTTON_DOUBLE_PRESS = 5;
public int PLANTRONICS_BUTTON_CALL = 2;
public int PLANTRONICS_BUTTON_MUTE = 3;
private static BluetoothManager instance;
private Context mContext;
private AudioManager mAudioManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothHeadset mBluetoothHeadset;
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mProfileListener;
private boolean isBluetoothConnected;
private boolean isScoConnected;
public static BluetoothManager getInstance() {
if (instance == null) {
instance = new BluetoothManager();
}
return instance;
}
public BluetoothManager() {
isBluetoothConnected = false;
if (!ensureInit()) {
//Log.w("[Bluetooth] Manager tried to init but LinphoneService not ready yet...");
}
instance = this;
}
public void initBluetooth() {
if (!ensureInit()) {
Log._w("[Bluetooth] Manager tried to init bluetooth but LinphoneService not ready yet...");
return;
}
IntentFilter filter = new IntentFilter();
filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.PLANTRONICS);
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
mContext.registerReceiver(this, filter);
Log._d("[Bluetooth] Receiver started");
startBluetooth();
}
private void startBluetooth() {
if (isBluetoothConnected) {
Log._e("[Bluetooth] Already started, skipping...");
return;
}
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
if (mProfileListener != null) {
Log._w("[Bluetooth] Headset profile was already opened, let's close it");
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
}
mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
Log._d("[Bluetooth] Headset connected");
mBluetoothHeadset = (BluetoothHeadset) proxy;
isBluetoothConnected = true;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
isBluetoothConnected = false;
Log._d("[Bluetooth] Headset disconnected");
LinphoneManager.getInstance().routeAudioToReceiver();
}
}
};
boolean success = mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
if (!success) {
Log._e("[Bluetooth] getProfileProxy failed !");
}
} else {
Log._w("[Bluetooth] Interface disabled on device");
}
}
private boolean ensureInit() {
if (mBluetoothAdapter == null) {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
if (mContext == null) {
if (LinphoneService.isReady()) {
mContext = LinphoneService.instance().getApplicationContext();
} else {
return false;
}
}
if (mContext != null && mAudioManager == null) {
mAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE));
}
return true;
}
public boolean routeAudioToBluetooth() {
ensureInit();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && mAudioManager != null && mAudioManager.isBluetoothScoAvailableOffCall()) {
if (isBluetoothHeadsetAvailable()) {
if (mAudioManager != null && !mAudioManager.isBluetoothScoOn()) {
Log._d("[Bluetooth] SCO off, let's start it");
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
}
} else {
return false;
}
// Hack to ensure bluetooth sco is really running
boolean ok = isUsingBluetoothAudioRoute();
int retries = 0;
while (!ok && retries < 5) {
retries++;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
if (mAudioManager != null) {
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
}
ok = isUsingBluetoothAudioRoute();
}
if (ok) {
if (retries > 0) {
Log._d("[Bluetooth] Audio route ok after " + retries + " retries");
} else {
Log._d("[Bluetooth] Audio route ok");
}
} else {
Log._d("[Bluetooth] Audio route still not ok...");
}
return ok;
}
return false;
}
public boolean isUsingBluetoothAudioRoute() {
return mBluetoothHeadset != null && mBluetoothHeadset.isAudioConnected(mBluetoothDevice) && isScoConnected;
}
public boolean isBluetoothHeadsetAvailable() {
ensureInit();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && mAudioManager != null && mAudioManager.isBluetoothScoAvailableOffCall()) {
boolean isHeadsetConnected = false;
if (mBluetoothHeadset != null) {
List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
mBluetoothDevice = null;
for (final BluetoothDevice dev : devices) {
if (mBluetoothHeadset.getConnectionState(dev) == BluetoothHeadset.STATE_CONNECTED) {
mBluetoothDevice = dev;
isHeadsetConnected = true;
break;
}
}
Log._d(isHeadsetConnected ? "[Bluetooth] Headset found, bluetooth audio route available" : "[Bluetooth] No headset found, bluetooth audio route unavailable");
}
return isHeadsetConnected;
}
return false;
}
public void disableBluetoothSCO() {
if (mAudioManager != null && mAudioManager.isBluetoothScoOn()) {
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
// Hack to ensure bluetooth sco is really stopped
int retries = 0;
while (isScoConnected && retries < 10) {
retries++;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
mAudioManager.stopBluetoothSco();
mAudioManager.setBluetoothScoOn(false);
}
Log._w("[Bluetooth] SCO disconnected!");
}
}
public void stopBluetooth() {
Log._w("[Bluetooth] Stopping...");
isBluetoothConnected = false;
disableBluetoothSCO();
if (mBluetoothAdapter != null && mProfileListener != null && mBluetoothHeadset != null) {
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
mProfileListener = null;
}
mBluetoothDevice = null;
Log._w("[Bluetooth] Stopped!");
if (LinphoneManager.isInstanciated()) {
LinphoneManager.getInstance().routeAudioToReceiver();
}
}
public void destroy() {
try {
stopBluetooth();
try {
mContext.unregisterReceiver(this);
Log._d("[Bluetooth] Receiver stopped");
} catch (Exception e) {
}
} catch (Exception e) {
Log._e(e);
}
}
public void onReceive(Context context, Intent intent) {
if (!LinphoneManager.isInstanciated())
return;
String action = intent.getAction();
if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(action)) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 0);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log._d("[Bluetooth] SCO state: connected");
// LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
isScoConnected = true;
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
Log._d("[Bluetooth] SCO state: disconnected");
// LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
isScoConnected = false;
} else {
Log._d("[Bluetooth] SCO state: " + state);
}
} else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.STATE_DISCONNECTED);
if (state == 0) {
Log._d("[Bluetooth] State: disconnected");
stopBluetooth();
} else if (state == 2) {
Log._d("[Bluetooth] State: connected");
startBluetooth();
} else {
Log._d("[Bluetooth] State: " + state);
}
} else if (intent.getAction().equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
String command = intent.getExtras().getString(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
//int type = intent.getExtras().getInt(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE);
Object[] args = (Object[]) intent.getExtras().get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
String eventName = (String) args[0];
if (eventName.equals("BUTTON") && args.length >= 3) {
Integer buttonID = (Integer) args[1];
Integer mode = (Integer) args[2];
Log._d("[Bluetooth] Event: " + command + " : " + eventName + ", id = " + buttonID + " (" + mode + ")");
}
}
}
}