demo地址:https://gitee.com/me_project_share/linphone-demo
1、项目级build.gradle引入库
buildscript {
repositories {
google()
jcenter()
maven { url 'https://linphone.org/releases/maven_repository/' }
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.3"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'https://linphone.org/releases/maven_repository/' }
}
}
2、app级build.gradle引入
implementation('org.linphone:linphone-sdk-android:5.0.0')
implementation 'androidx.media:media:1.4.0'//响铃
3、添加权限
<uses-permission android:name="android.permission.INTERNET" />//网络权限
<uses-permission android:name="android.permission.CAMERA"
tools:ignore="PermissionImpliesUnsupportedChromeOsHardware" />//摄像头权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />//手机状态权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />//麦克风权限
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />//麦克风设置权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />//网络状态权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />//读取本地权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />//写入本地权限
4、AndroidManifest.xml加入Linphone Service
<service
android:name="org.linphone.core.tools.service.CoreService"
android:foregroundServiceType="phoneCall|camera|microphone"
android:label="@string/library_name"
android:stopWithTask="false" />
5、初始化Linphone
LinphoneManager.getInstance()
.init(this)
.start();
6、登录Linphone
LinphoneManager.getInstance().Login("账号","密码","ip","端口");
7、拨号
LinphoneManager.getInstance().startCall(number, false/*是否是视频通话*/);
其他api参考下面文件或者下载demo
需要的文件
LinphoneManager文件:
public class LinphoneManager {
private static LinphoneManager INSTANCE = null;
private Call.State previousCallState = Call.State.Idle;
private Context mContext;
private CorePreferences corePreferences;
private Core core;
private RegistrationCallback registrationCallback;
private boolean gsmCallActive = false;
// private PhoneCallback phoneCallback;
private boolean coreIsStart = false;
private Call currentCall;
public LinphoneManager() {
}
public static LinphoneManager getInstance() {
if (INSTANCE == null) {
synchronized (LinphoneUtils.class) {
if (INSTANCE == null) {
INSTANCE = new LinphoneManager();
}
}
}
return INSTANCE;
}
public LinphoneManager init(Context context) {
mContext = context.getApplicationContext();
//开启日志
Factory.instance().enableLogCollection(LogCollectionState.Enabled);
Factory.instance().enableLogcatLogs(true);
corePreferences = new CorePreferences(mContext);
corePreferences.copyAssetsFromPackage();
core = Factory.instance().createCoreWithConfig(corePreferences.getConfig(), mContext);
return this;
}
/**
* 登录到服务器
*
* @param username 账号名
* @param password 密码
* @param ip IP地址
* @param port 端口号
*/
public void Login(@NonNull String username, @NonNull String password, @NonNull String ip, @NonNull String port) {
core.clearProxyConfig();
String domain = ip + ":" + port;
AccountCreator accountCreator = core.createAccountCreator(corePreferences.getXmlrpcUrl());
accountCreator.setLanguage(Locale.getDefault().getLanguage());
accountCreator.reset();
accountCreator.setUsername(username);//账号
accountCreator.setPassword(password);//密码
accountCreator.setDisplayName(username);//显示名称
accountCreator.setDomain(domain);//域名
accountCreator.setTransport(TransportType.Udp);//传输类型
accountCreator.createProxyConfig();
}
public LinphoneManager registrationCallback(RegistrationCallback registrationCallback) {
this.registrationCallback = registrationCallback;
return this;
}
public LinphoneManager registerPhoneCallback(PhoneCallback phoneCallback) {
// this.phoneCallback = phoneCallback;
return this;
}
/**
* 启动linphone
*/
public void start() {
if (!coreIsStart) {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] starting Linphone");
coreIsStart = true;
initLinphone();
core.addListener(mCoreListener);
core.start();
TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 注册 phone state listener");
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
/***
* 初始化设置
*/
private void initLinphone() {
configureCore();
// initUserCertificates();
}
private void configureCore() {
core.setNativeRingingEnabled(true);// 来电铃声
core.setVibrationOnIncomingCallEnabled(true);// 来电振动
core.enableEchoCancellation(true); //回声消除
core.enableAdaptiveRateControl(true); //自适应码率控制
}
/***
* 设置存放用户x509证书的目录路径
*/
private void initUserCertificates() {
String userCertsPath = corePreferences.userCertificatesPath;
File f = new File(userCertsPath);
if (!f.exists()) {
if (!f.mkdir()) {
Log.e(mContext.getString(R.string.library_name), "[LinphoneManager] " + userCertsPath + "不能创建");
}
}
core.setUserCertificatesPath(userCertsPath);
}
/**
* 拨打电话
*
* @param toPhoneNum String
* @param isVideoCall Boolean
*/
public void startCall(@NonNull String toPhoneNum, @NonNull boolean isVideoCall) {
if (toPhoneNum.trim().isEmpty()) {
Toast.makeText(mContext, "请输入正确的呼叫号码", Toast.LENGTH_LONG).show();
return;
}
try {
Address addressToCall = core.interpretUrl(toPhoneNum);
if (addressToCall != null) {
addressToCall.setDisplayName(toPhoneNum);
}
CallParams params = core.createCallParams(null);
if (params != null) {
//启用通话录音
// params.setRecordFile(LinphoneUtils.getRecordingFilePathForAddress(mContext, addressToCall));
//启动低宽带模式
if (LinphoneUtils.checkIfNetworkHasLowBandwidth(mContext)) {
Log.w(mContext.getString(R.string.library_name), "[LinphoneManager] Enabling low bandwidth mode!");
params.enableLowBandwidth(true);
}
if (isVideoCall) {
params.enableVideo(true);
core.enableVideoCapture(true);
core.enableVideoDisplay(true);
} else {
params.enableVideo(false);
}
core.inviteAddressWithParams(addressToCall, params);
} else {
core.inviteAddress(addressToCall);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 接听来电
*/
public int answerCall(Call call) {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 接听来电 " + call);
CallParams params = core.createCallParams(call);
if (params != null) {
//启用通话录音
// params.setRecordFile(LinphoneUtils.getRecordingFilePathForAddress(mContext, call.getRemoteAddress()));
if (LinphoneUtils.checkIfNetworkHasLowBandwidth(mContext)) {
Log.w(mContext.getString(R.string.library_name), "[LinphoneManager] 启用低带宽模式!");
params.enableLowBandwidth(true);
}
params.enableVideo(isVideoCall(call));
return call.acceptWithParams(params);
} else {
return -1;
}
}
/**
* 拒接电话
*
* @param call Call
*/
public int declineCall(Call call) {
String voiceMailUri = corePreferences.getVoiceMailUri();
if (voiceMailUri != null && corePreferences.isRedirectDeclinedCallToVoiceMail()) {
Address voiceMailAddress = core.interpretUrl(voiceMailUri);
if (voiceMailAddress != null) {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 重定向 call " + call +" 语音信箱URI: " + voiceMailUri);
return call.redirectTo(voiceMailAddress);
}
} else {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 拒接电话"+call);
return call.decline(Reason.Declined);
}
return -1;
}
/**
* 挂断电话
*/
public int terminateCall(Call call) {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 挂断电话 " + call);
return call.terminate();
}
/***
* 麦克风是否开启
* @return
*/
public boolean micEnabled() {
return core.micEnabled();
}
/***
* 扬声器是否开启
* @return
*/
public boolean speakerEnabled() {
return core.getOutputAudioDevice().getType() == AudioDevice.Type.Speaker;
}
/**
* 启动麦克风
*
* @param micEnabled Boolean
*/
public void enableMic(Boolean micEnabled) {
if (core != null) {
core.enableMic(micEnabled);
}
}
/**
* 扬声器或听筒
*
* @param SpeakerEnabled Boolean
*/
public void enableSpeaker(boolean SpeakerEnabled) {
if (core != null) {
if (SpeakerEnabled) {
AudioRouteUtils.routeAudioToSpeaker(core);
} else {
AudioRouteUtils.routeAudioToEarpiece(core);
}
}
}
/**
* 是否是视频电话
*
* @return Boolean
*/
public boolean isVideoCall(Call call) {
CallParams remoteParams = call.getRemoteParams();
return remoteParams != null && remoteParams.videoEnabled();
}
/**
* 设置视频界面
*
* @param videoRendering TextureView 对方界面
* @param videoPreview CaptureTextureView 自己界面
*/
public void setVideoWindowId(TextureView videoRendering, TextureView videoPreview) {
if (core != null) {
core.setNativeVideoWindowId(videoRendering);
core.setNativePreviewWindowId(videoPreview);
}
}
/**
* 停止linphone
*/
private void stop() {
coreIsStart = false;
TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 注销 phone state listener");
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
core.removeListener(mCoreListener);
core.stop();
}
/**
* 注销登录
*/
public void loginOut() {
if (core != null) {
core.clearProxyConfig();
}
}
public Call getCurrentCall() {
return (currentCall != null) ? currentCall : null;
}
public void getLoginState() {
if (core != null && core.getDefaultAccount() != null){
Intent intent = new Intent("com.okcis.siplibrary.LoginState");
intent.putExtra("LoginState", core.getDefaultAccount().getState().toInt());
mContext.sendBroadcast(intent);
}
}
CoreListenerStub mCoreListener = new CoreListenerStub() {
@Override
public void onRegistrationStateChanged(@NonNull Core core, @NonNull ProxyConfig proxyConfig, RegistrationState state, @NonNull String message) {
super.onRegistrationStateChanged(core, proxyConfig, state, message);
Intent intent = new Intent("com.okcis.siplibrary.LoginState");
switch (state) {
case None:
intent.putExtra("LoginState", RegistrationState.None.toInt());
mContext.sendBroadcast(intent);
// registrationCallback.registrationNone();
break;
case Progress:
intent.putExtra("LoginState", RegistrationState.Progress.toInt());
mContext.sendBroadcast(intent);
// registrationCallback.registrationProgress();
break;
case Ok:
intent.putExtra("LoginState", RegistrationState.Ok.toInt());
mContext.sendBroadcast(intent);
// registrationCallback.registrationOk();
break;
case Cleared:
intent.putExtra("LoginState", RegistrationState.Cleared.toInt());
mContext.sendBroadcast(intent);
// registrationCallback.registrationClear();
break;
case Failed:
intent.putExtra("LoginState", RegistrationState.Failed.toInt());
mContext.sendBroadcast(intent);
// registrationCallback.registrationFailed();
break;
}
}
@Override
public void onCallStateChanged(@NonNull Core core, @NonNull Call call, Call.State state, @NonNull String message) {
super.onCallStateChanged(core, call, state, message);
Log.e(mContext.getString(R.string.library_name), "[LinphoneManager] Call state changed " + state + " " + core.getCallsNb());
if (!gsmCallActive) {
currentCall = call;
}
Intent intent = new Intent("com.okcis.siplibrary.CallState");
intent.setPackage(mContext.getPackageName());
switch (state) {
case IncomingReceived:
case IncomingEarlyMedia:
if (gsmCallActive) {
Log.w(mContext.getString(R.string.library_name), "[LinphoneManager] Refusing the call with reason busy because a GSM call is active");
call.decline(Reason.Busy);
return;
}
intent.putExtra("CallState", Call.State.IncomingReceived.toInt());
mContext.sendBroadcast(intent);
// phoneCallback.incomingCall(currentCall);
gsmCallActive = true;
//自动接听
if (corePreferences.isAutoAnswerEnabled()) {
int autoAnswerDelay = corePreferences.getAutoAnswerTime();
if (autoAnswerDelay == 0) {
Log.w(mContext.getString(R.string.library_name), "[LinphoneManager] 立即自动接听来电");
answerCall(currentCall);
} else {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] " + autoAnswerDelay + "ms后自动接听来电");
Handler mainThreadHandler = new Handler(Looper.getMainLooper());
mainThreadHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.w(mContext.getString(R.string.library_name), "[LinphoneManager] Auto answering call");
answerCall(currentCall);
}
}, autoAnswerDelay);
}
}
break;
case OutgoingInit:
if (core.getCallsNb() == 1 && !gsmCallActive) {
intent.putExtra("CallState", Call.State.OutgoingInit.toInt());
mContext.sendBroadcast(intent);
// phoneCallback.outgoingInit(currentCall);
gsmCallActive = true;
}
break;
case OutgoingProgress:
if (core.getCallsNb() == 1 && corePreferences.isRouteAudioToBluetoothIfAvailable()) {
AudioRouteUtils.routeAudioToBluetooth(core, currentCall);
}
break;
case Connected:
intent.putExtra("CallState", Call.State.Connected.toInt());
mContext.sendBroadcast(intent);
// phoneCallback.callConnected(currentCall);
break;
case StreamsRunning:
if (core.getCallsNb() == 1) {
// 只有第一次呼叫在StreamsRunning时才尝试连接蓝牙或耳机
if (previousCallState == Call.State.Connected) {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 第一次进入StreamsRunning状态,尝试将音频路由到可用的耳机或蓝牙设备");
if (AudioRouteUtils.isHeadsetAudioRouteAvailable(core)) {
AudioRouteUtils.routeAudioToHeadset(core, currentCall);
} else if (corePreferences.isRouteAudioToBluetoothIfAvailable() && AudioRouteUtils.isBluetoothAudioRouteAvailable(core)) {
AudioRouteUtils.routeAudioToBluetooth(core, currentCall);
}
}
}
if (corePreferences.isRouteAudioToSpeakerWhenVideoIsEnabled() && currentCall.getCurrentParams().videoEnabled()) {
// 如果使用耳机或蓝牙,在启用视频时不打开扬声器
if (!AudioRouteUtils.isHeadsetAudioRouteAvailable(core) &&
!AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(core, currentCall)
) {
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 视频通话,没有有线耳机,没有使用蓝牙,音频路由到扬声器");
AudioRouteUtils.routeAudioToSpeaker(core);
}
}
break;
case End:
if (core.getCallsNb() == 0) {
intent.putExtra("CallState", Call.State.End.toInt());
mContext.sendBroadcast(intent);
// phoneCallback.callEnd(currentCall);
gsmCallActive = false;
}
break;
case Released:
if (core.getCallsNb() == 0) {
intent.putExtra("CallState", Call.State.Released.toInt());
mContext.sendBroadcast(intent);
// phoneCallback.callReleased(currentCall);
gsmCallActive = false;
}
break;
case Error:
if (core.getCallsNb() == 0) {
int msgId;
switch (currentCall.getErrorInfo().getReason()) {
case Busy:
msgId = R.string.call_error_user_busy;
break;
case IOError:
msgId = R.string.call_error_io_error;
break;
case NotAcceptable:
msgId = R.string.call_error_incompatible_media_params;
break;
case NotFound:
msgId = R.string.call_error_user_not_found;
break;
case Forbidden:
msgId = R.string.call_error_forbidden;
break;
default:
msgId = R.string.call_error_unknown;
break;
}
intent.putExtra("CallState", Call.State.Error.toInt());
mContext.sendBroadcast(intent);
// phoneCallback.error(mContext.getString(msgId));
gsmCallActive = false;
}
break;
}
previousCallState = state;
}
};
PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String phoneNumber) {
super.onCallStateChanged(state, phoneNumber);
Log.i(mContext.getString(R.string.library_name), "[LinphoneManager] 手机状态 " + state);
switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
case TelephonyManager.CALL_STATE_RINGING:
gsmCallActive = true;
break;
default:
gsmCallActive = false;
break;
}
}
};
}
CorePreferences文件
public class CorePreferences {
private Context mContext;
public Config config;
public String basePath;
public String configPath;
public String factoryConfigPath;
public String linphoneDefaultValuesPath;
public String defaultValuesPath;
public String userCertificatesPath;
public CorePreferences(Context context) {
mContext = context;
basePath = mContext.getFilesDir().getAbsolutePath();
configPath = basePath + "/.linphonerc";
factoryConfigPath = basePath + "/linphonerc";
linphoneDefaultValuesPath = basePath + "/assistant_linphone_default_values";
defaultValuesPath = basePath + "/assistant_default_values";
userCertificatesPath = basePath + "/user-certs";
config = Factory.instance().createConfigWithFactory(
configPath,
factoryConfigPath
);
String TagName = mContext.getString(R.string.library_name);
Factory.instance().setDebugMode(isDebugEnabled(), TagName);
}
public Config getConfig(){
return (config != null)? config:null;
}
/***
* 是否开启debug
* @return
*/
public boolean isDebugEnabled() {
if (config == null) return false;
return config.getBool("app", "debug", false);
}
public CorePreferences enabledDebug(boolean enabled) {
if (config == null) return null;
config.setBool("app", "debug", enabled);
return this;
}
/***
* 是否开启自动应答
* @return
*/
public boolean isAutoAnswerEnabled() {
if (config == null) return false;
return config.getBool("app", "auto_answer", false);
}
public CorePreferences enableAutoAnswer(boolean enable) {
if (config == null) return null;
config.setBool("app", "auto_answer", enable);
return this;
}
/***
* 自动应答时间
* @return
*/
public int getAutoAnswerTime() {
if (config == null) return 0;
return config.getInt("app", "auto_answer_delay", 0);
}
public CorePreferences setAutoAnswerTime(int time) {
if (config == null) return null;
config.setInt("app", "auto_answer_delay", time);
return this;
}
/***
* 获取语音箱地址
* @return
*/
public String getVoiceMailUri() {
if (config == null) return null;
return config.getString("app", "voice_mail", null);
}
public CorePreferences setVoiceMailUri(String uri) {
if (config == null) return null;
config.setString("app", "voice_mail", uri);
return this;
}
/***
* 是否开启:被拒绝通话转到语音箱
* @return
*/
public boolean isRedirectDeclinedCallToVoiceMail() {
if (config == null) return false;
return config.getBool("app", "redirect_declined_call_to_voice_mail", true);
}
public CorePreferences enabledRedirectDeclinedCallToVoiceMail(boolean value) {
if (config == null) return null;
config.setBool("app", "redirect_declined_call_to_voice_mail", value);
return this;
}
/**
* 是否获取可用蓝牙设备
* @return
*/
public boolean isRouteAudioToBluetoothIfAvailable() {
if (config == null) return false;
return config.getBool("app", "route_audio_to_bluetooth_if_available", true);
}
public CorePreferences enabledRouteAudioToBluetoothIfAvailable(boolean value) {
if (config == null) return null;
config.setBool("app", "route_audio_to_bluetooth_if_available", value);
return this;
}
/***
* 视频通话是否启用扬声器
* @return
*/
public boolean isRouteAudioToSpeakerWhenVideoIsEnabled() {
if (config == null) return false;
return config.getBool("app", "route_audio_to_speaker_when_video_enabled", true);
}
public CorePreferences enabledRouteAudioToSpeakerWhenVideoIsEnabled(boolean value) {
if (config == null) return null;
config.setBool("app", "route_audio_to_speaker_when_video_enabled", value);
return this;
}
/***
* 获取xmlrpc_url
* @return
*/
public String getXmlrpcUrl() {
if (config == null) return null;
return config.getString("assistant", "xmlrpc_url", null);
}
public void setXmlrpcUrl(String value) {
if (config == null) return;
config.setString("assistant", "xmlrpc_url", value);
}
public void copyAssetsFromPackage() {
copy("linphonerc_default", configPath, false);
copy("linphonerc_factory", factoryConfigPath, true);
copy("assistant_linphone_default_values", linphoneDefaultValuesPath, true);
copy("assistant_default_values", defaultValuesPath, true);
move(basePath + "/linphone-log-history.db", basePath + "/call-history.db", false);
move(basePath + "/zrtp_secrets", basePath + "/zrtp-secrets.db", false);
}
private void copy(String from, String to, boolean overrideIfExists) {
File outFile = new File(to);
if (outFile.exists()) {
if (!overrideIfExists) {
Log.i(mContext.getString(R.string.library_name), "[CorePreferences] copy文件" + from + "已经存在,路径:" + to);
return;
}
}
Log.i(mContext.getString(R.string.library_name), "[CorePreferences] 从assets copy" + from + "文件,路径:" + to);
try {
OutputStream outStream = new FileOutputStream(outFile);
InputStream inFile = mContext.getAssets().open(from);
byte[] buffer = new byte[1024];
int length = inFile.read(buffer);
while (length > 0) {
outStream.write(buffer, 0, length);
length = inFile.read(buffer);
}
inFile.close();
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void move(String from, String to, boolean overrideIfExists) {
File inFile = new File(from);
File outFile = new File(to);
if (inFile.exists()) {
if (outFile.exists() && !overrideIfExists) {
Log.w(mContext.getString(R.string.library_name), "[CorePreferences] move文件" + from + "已经存在,路径:" + to);
} else {
try {
InputStream inStream = new FileInputStream(inFile);
OutputStream outStream = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
inStream.close();
outStream.flush();
outStream.close();
inFile.delete();
Log.i(mContext.getString(R.string.library_name), "[CorePreferences] move文件" + from + "完成,路径:" + to);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} else {
Log.w(mContext.getString(R.string.library_name), "[CorePreferences] move" + from + "文件失败,没有文件资源");
}
}
}
AudioRouteUtils文件:
public class AudioRouteUtils {
/***
* 切换到听筒
* @param core
*/
public static void routeAudioToEarpiece(Core core) {
if (core.getCallsNb() == 0) {
Log.e("AudioRouteUtils","[Audio Route Helper] No call found, aborting earpiece audio route change");
return;
}
Call call = core.getCurrentCall();
if (call == null){
call = core.getCalls()[0];
}
for (AudioDevice audioDevice: core.getAudioDevices()) {
if (audioDevice.getType() == AudioDevice.Type.Earpiece) {
Log.i("AudioRouteUtils","[Audio Route Helper Earpiece] 找到听筒音频设备 "+ audioDevice.getDeviceName());
call.setOutputAudioDevice(audioDevice);
return;
}
}
Log.e("AudioRouteUtils","[Audio Route Helper] 找不到听筒音频设备");
}
/***
* 切换到扬声器
* @param core
*/
public static void routeAudioToSpeaker(Core core) {
if (core.getCallsNb() == 0) {
Log.e("AudioRouteUtils","[Audio Route Helper] No call found, aborting speaker audio route change");
return;
}
Call call = core.getCurrentCall();
if (call == null){
call = core.getCalls()[0];
}
for (AudioDevice audioDevice: core.getAudioDevices()) {
if (audioDevice.getType() == AudioDevice.Type.Speaker) {
Log.i("AudioRouteUtils","[Audio Route Helper Speaker] 找到扬声器音频设备 "+ audioDevice.getDeviceName());
call.setOutputAudioDevice(audioDevice);
return;
}
}
Log.e("AudioRouteUtils","[Audio Route Helper] 找不到扬声器音频设备");
}
/***
* 切换到蓝牙设备
* @param core
* @param call
*/
public static void routeAudioToBluetooth(Core core, Call call) {
if (core.getCallsNb() == 0) {
Log.e("AudioRouteUtils", "[Audio Route Helper] No call found, aborting bluetooth audio route change");
return;
}
Call currentCall;
if (call == null){
currentCall = core.getCurrentCall();
if (currentCall == null){
currentCall = core.getCalls()[0];
}
} else {
currentCall = call;
}
for (AudioDevice audioDevice: core.getAudioDevices()) {
if (audioDevice.getType() == AudioDevice.Type.Bluetooth) {
if (audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
Log.i("AudioRouteUtils","[Audio Route Helper] 找到蓝牙音频设备 " + audioDevice.getDeviceName());
currentCall.setOutputAudioDevice(audioDevice);
return;
}
}
}
Log.e("AudioRouteUtils","[Audio Route Helper] 找不到蓝牙音频设备");
}
/***
* 切换到耳机
* @param core
* @param call
*/
public static void routeAudioToHeadset(Core core, Call call) {
if (core.getCallsNb() == 0) {
Log.e("AudioRouteUtils","[Audio Route Helper] No call found, aborting headset audio route change");
return;
}
Call currentCall;
if (call == null){
currentCall = core.getCurrentCall();
if (currentCall == null){
currentCall = core.getCalls()[0];
}
} else {
currentCall = call;
}
for (AudioDevice audioDevice: core.getAudioDevices()) {
if (audioDevice.getType() == AudioDevice.Type.Headphones || audioDevice.getType() == AudioDevice.Type.Headset) {
if (audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
Log.i("AudioRouteUtils","[Audio Route Helper] 找到有线耳机音频设备 " + audioDevice.getDeviceName());
currentCall.setOutputAudioDevice(audioDevice);
return;
}
}
}
Log.e("AudioRouteUtils","[Audio Route Helper] 找不到有线耳机音频设备");
}
/***
* 耳机是否可用
* @param core
* @return
*/
public static boolean isHeadsetAudioRouteAvailable(Core core) {
for (AudioDevice audioDevice : core.getAudioDevices()) {
if ((audioDevice.getType() == AudioDevice.Type.Headset || audioDevice.getType() == AudioDevice.Type.Headphones) &&
audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
Log.i("AudioRouteUtils","[Audio Route Helper] 找到耳机设备 " + audioDevice.getDeviceName());
return true;
}
}
return false;
}
/***
* 当前是否在使用蓝牙设备
* @param core
* @param call
* @return
*/
public static boolean isBluetoothAudioRouteCurrentlyUsed(Core core,Call call) {
if (core.getCallsNb() == 0) {
Log.w("AudioRouteUtils","[Audio Route Helper] No call found, so bluetooth audio route isn't used");
return false;
}
Call currentCall;
if (call == null){
currentCall = core.getCurrentCall();
if (currentCall == null){
currentCall = core.getCalls()[0];
}
} else {
currentCall = call;
}
AudioDevice audioDevice = currentCall.getOutputAudioDevice();
Log.i("AudioRouteUtils","[Audio Route Helper] 当前正在使用的音频设备是 " + audioDevice.getDeviceName());
return audioDevice.getType() == AudioDevice.Type.Bluetooth;
}
/***
* 蓝牙设备是否可用
* @param core
* @return
*/
public static boolean isBluetoothAudioRouteAvailable(Core core) {
for (AudioDevice audioDevice : core.getAudioDevices()) {
if (audioDevice.getType() == AudioDevice.Type.Bluetooth &&
audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) {
Log.i("AudioRouteUtils","[Audio Route Helper] 找到蓝牙音频设备 " + audioDevice.getDeviceName());
return true;
}
}
return false;
}
}
FileUtils文件:
public class FileUtils {
public static final String VFS_PLAIN_FILE_EXTENSION = ".bctbx_evfs_plain";
public static String getDisplayName(Address address) {
return (!address.getDisplayName().trim().isEmpty())? address.getUsername() : "";
}
public static File getFileStoragePath(Context context, String fileName) {
File path = getFileStorageDir(context, isExtensionImage(fileName));
File file = new File(path, fileName);
int prefix = 1;
while (file.exists()) {
file = new File(path, prefix + "_" + fileName);
prefix += 1;
}
return file;
}
public static File getFileStorageDir(Context context, Boolean isPicture) {
File path = null;
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
String directory = Environment.DIRECTORY_DOWNLOADS;
if (isPicture) {
directory = Environment.DIRECTORY_PICTURES;
}
path = context.getExternalFilesDir(directory);
}
File returnPath = (path != null)? path: context.getFilesDir();
return returnPath;
}
public static Boolean isExtensionImage(String path) {
String extension = getExtensionFromFileName(path).toLowerCase(Locale.getDefault());
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
return (type != null) && type.startsWith("image/") ? true : false;
}
public static String getExtensionFromFileName(String fileName) {
String realFileName = "";
if (fileName.endsWith(VFS_PLAIN_FILE_EXTENSION)) {
realFileName = fileName.substring(0, fileName.length() - VFS_PLAIN_FILE_EXTENSION.length());
} else {
realFileName = fileName;
}
String extension = MimeTypeMap.getFileExtensionFromUrl(realFileName);
if (extension != null && !extension.isEmpty()) {
int i = realFileName.lastIndexOf('.');
if (i > 0) {
extension = realFileName.substring(i + 1);
}
}
return extension;
}
}
LinphoneUtils文件:
public class LinphoneUtils {
/***
* 检查网络带宽是否过低
* @param context
* @return
*/
public static boolean checkIfNetworkHasLowBandwidth(Context context){
ConnectivityManager connMgr =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (networkInfo != null && networkInfo.isConnected()) {
if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
if (networkInfo.getSubtype() == NETWORK_TYPE_EDGE
|| networkInfo.getSubtype() == NETWORK_TYPE_GPRS
|| networkInfo.getSubtype() == NETWORK_TYPE_IDEN){
return true;
} else {
return false;
}
}
}
// In doubt return false
return false;
}
/***
* 录音文件地址
* @param context
* @param address
* @return
*/
public static String getRecordingFilePathForAddress(Context context, Address address) {
String displayName = FileUtils.getDisplayName(address);
DateFormat dateFormat = new SimpleDateFormat(
"dd-MM-yyyy-HH-mm-ss",
Locale.getDefault()
);
String fileName = displayName + "_" + dateFormat.format(new Date()) + ".mkv";
return FileUtils.getFileStoragePath(context, fileName).getAbsolutePath();
}
}
MyBroadcastReceiver文件:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == "com.okcis.siplibrary.CallState"){
int callState = intent.getIntExtra("CallState", -1);
if (callState != -1){
Activity currentActivity = BaseActivity.getCurrentActivity();
if (callState == Call.State.IncomingReceived.toInt()){
Intent startIntent = new Intent(context, CallIncomingActivity.class);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startIntent);
} else if (callState == Call.State.OutgoingInit.toInt()){
Intent startIntent = new Intent(context, CallOutgoingActivity.class);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startIntent);
} else if (callState == Call.State.Connected.toInt()){
if (currentActivity instanceof CallOutgoingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallIncomingActivity){
currentActivity.finish();
}
Intent startIntent = new Intent(context, CallConnectActivity.class);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startIntent);
} else if (callState == Call.State.End.toInt()){
if (currentActivity instanceof LinphoneMainActivity){
} else if (currentActivity instanceof CallOutgoingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallIncomingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallConnectActivity){
currentActivity.finish();
}
} else if (callState == Call.State.Released.toInt()){
if (currentActivity instanceof LinphoneMainActivity){
} else if (currentActivity instanceof CallOutgoingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallIncomingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallConnectActivity){
currentActivity.finish();
}
} else if (callState == Call.State.Error.toInt()){
if (currentActivity instanceof LinphoneMainActivity){
} else if (currentActivity instanceof CallOutgoingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallIncomingActivity){
currentActivity.finish();
} else if (currentActivity instanceof CallConnectActivity){
currentActivity.finish();
}
}
}
}
}
}
BaseActivity文件:
public class BaseActivity extends AppCompatActivity {
private static List<Activity> currentActivityList = new LinkedList<>();
public static synchronized Activity getCurrentActivity() {
return (currentActivityList.size() > 0)?currentActivityList.get(currentActivityList.size() - 1):null;
}
public synchronized void setCurrentActivity(Activity activity) {
currentActivityList.add(activity);
}
public synchronized void removeCurrentActivity(Activity activity){
for (Activity currentActivity: currentActivityList){
if (currentActivity == activity){
currentActivityList.remove(activity);
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
}
return super.onKeyDown(keyCode, event);
}
}