Linphone callState 电话状态的监听状态(一)
0. 阅读指南
因为粘贴的代码比较多, 阅读之前请先看目录.
如果对这篇文章有什么建议的话, 请在评论中指出. 尽量把文章写好点.
1. 说明
LinphoneService有个重要的机制, 就是通过注册LinphoneCoreListener的实例, 当Linphone的状态发声变化的时候, 会回调相应的方法. 然后linphone上层会做相应的处理.]
为了更好的分析整个linphone, 现在这个LinphoneCoreListener注册回调机制是怎么也躲不过去了.
用到的地方
图2.1是通过Android studio进行查找addListener调用的地方, 说明了, 这些方法一旦实例化, 便注册了addListener(), 一旦linphone有变化, 这些类就做做出相应的处理.
2. 回调都需要什么
2.1 LinphoneCoreListener.java
代码
直接看注释, 我中文稍微翻译一下
package org.linphone.core;
import java.nio.ByteBuffer;
/**
*
*This interface holds all callbacks that the application should implement. None is mandatory.
* 这个接口是接收回调, 但不是强制的.
*/
public interface LinphoneCoreListener {
/**
* @deprecated
* Ask the application some authentication information
**/
@Deprecated
void authInfoRequested(LinphoneCore lc, String realm, String username, String domain);
/**
* Ask the application some authentication information
* 询问一下有什么验证信息
* @param lc the LinphoneCore
* @param authInfo a LinphoneAuthInfo pre-filled with username, realm and domain values as much as possible
* @param method the type of authentication requested (HttpDigest, Tls, ...)
**/
void authenticationRequested(LinphoneCore lc, LinphoneAuthInfo authInfo, LinphoneCore.AuthMethod method);
/**
* Call stats notification
*/
void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats);
/**
* Reports that a new subscription request has been received and wait for a decision.
*Status on this subscription request is notified by changing policy for this friend
* 有新的注册请求
*@param lc LinphoneCore
*@param lf LinphoneFriend corresponding to the subscriber
*@param url of the subscriber
*
*/
void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, String url);
/**
* Report status change for a friend previously added to LinphoneCore.
* @param lc LinphoneCore
* @param lf updated LinphoneFriend
*/
void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf);
/**
* invoked when a new dtmf is received
* @param lc LinphoneCore
* @param call LinphoneCall involved in the dtmf sending
* @param dtmf value of the dtmf sent
*/
void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf);
/**
* Report Notified message received for this identity.
* @param lc LinphoneCore
* @param call LinphoneCall in case the notify is part of a dialog, may be null
* @param from LinphoneAddress the message comes from
* @param event String the raw body of the notify event.
*
*/
void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event);
/**
* Notifies progress of a call transfer.
* @param lc the LinphoneCore
* @param call the call through which the transfer was sent.
* @param new_call_state the state of the call resulting of the transfer, at the other party.
**/
void transferState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State new_call_state);
/**
* Notifies an incoming INFO message.
* @param lc the LinphoneCore.
* @param info the info message
*/
void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info);
/**
* Notifies of subscription requests state changes, including new incoming subscriptions.
* @param lc the LinphoneCore
* @param ev LinphoneEvent object representing the subscription context.
* @param state actual state of the subscription.
*/
void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev, SubscriptionState state);
/**
* Notifies about outgoing generic publish states.
* @param lc the LinphoneCore
* @param ev a LinphoneEvent representing the publish, typically created by {@link LinphoneCore#publish}
* @param state the publish state
*/
void publishStateChanged(LinphoneCore lc, LinphoneEvent ev, PublishState state);
/**
* Notifies the application that it should show up
* @deprecated
*/
@Deprecated
void show(LinphoneCore lc);
/**
* Callback that notifies various events with human readable text.
* @deprecated
*/
@Deprecated
void displayStatus(LinphoneCore lc,String message);
/**
* Callback to display a message to the user
* @deprecated
*/
@Deprecated
void displayMessage(LinphoneCore lc,String message);
/**
* Callback to display a warning to the user
* @deprecated
*/
@Deprecated
void displayWarning(LinphoneCore lc,String message);
/**
* Callback to be notified about the transfer progress.
* @param lc the LinphoneCore
* @param message the LinphoneChatMessage
* @param content the LinphoneContent
* @param progress percentage of the transfer done
*/
void fileTransferProgressIndication(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, int progress);
/**
* Callback to be notified when new data has been received
* @param lc the LinphoneCore
* @param message the LinphoneChatMessage
* @param content the LinphoneContent
* @param buffer
* @param size
*/
void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, byte[] buffer, int size);
/**
* Callback to be notified when new data needs to be sent
* @param lc the LinphoneCore
* @param message the LinphoneChatMessage
* @param content the LinphoneContent
* @param buffer
* @param size
* @return the number of bytes written into buffer
*/
int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, ByteBuffer buffer, int size);
/**
* General State notification
* @param state LinphoneCore.State
*/
void globalState(LinphoneCore lc,LinphoneCore.GlobalState state, String message);
/**
* Registration state notification
* */
void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage);
/**
* Notifies the changes about the remote provisioning step
* @param lc the LinphoneCore
* @param state the RemoteProvisioningState
* @param message the error message if state == Failed
*/
void configuringStatus(LinphoneCore lc, LinphoneCore.RemoteProvisioningState state, String message);
/**
* invoked when a new linphone chat message is received
* 接收聊天信息
* @param lc LinphoneCore
* @param cr LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room.
* @param message incoming linphone chat message message
*/
void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message);
/**
* invoked when a new linphone chat message is received and we cannot decrypt this
* @param lc LinphoneCore
* @param cr LinphoneChatRoom involved in this conversation. Can be be created by the framework in case the from is not present in any chat room.
* @param message incoming linphone chat message message
*/
void messageReceivedUnableToDecrypted(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message);
/** Call State notification
* @param state LinphoneCall.State
*/
void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message);
/**
* Callback to display change in encryption state.
* @param encrypted true if all streams of the call are encrypted
* @param authenticationToken token like ZRTP SAS that may be displayed to user
*/
void callEncryptionChanged(LinphoneCore lc, LinphoneCall call, boolean encrypted, String authenticationToken);
/**
* Notifies of an incoming NOTIFY received.
* @param lc the linphoneCore
* @param ev a LinphoneEvent representing the subscription context for which this notify belongs, or null if it is a NOTIFY out of of any subscription.
* @param eventName the event name
* @param content content of the NOTIFY request.
*/
void notifyReceived(LinphoneCore lc, LinphoneEvent ev, String eventName, LinphoneContent content);
/**
* invoked when a composing notification is received
* @param lc LinphoneCore
* @param cr LinphoneChatRoom involved in the conversation.
*/
void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr);
/**
* Invoked when echo cancalation calibration is completed
* @param lc LinphoneCore
* @param status
* @param delay_ms echo delay
* @param data
*/
void ecCalibrationStatus(LinphoneCore lc, LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data);
/**
* Callback prototype for reporting log collection upload progress indication.
*/
void uploadProgressIndication(LinphoneCore lc, int offset, int total);
/**
* Callback prototype for reporting log collection upload state change.
* @param lc LinphoneCore object
* @param state The state of the log collection upload
* @param info Additional information: error message in case of error state, URL of uploaded file in case of success.
*/
void uploadStateChanged(LinphoneCore lc, LinphoneCore.LogCollectionUploadState state, String info);
/**
* Callback prototype for reporting LinphoneFriendList creation.
* @param lc LinphoneCore object
* @param list LinphoneFriendList object
*/
void friendListCreated(LinphoneCore lc, LinphoneFriendList list);
/**
* Callback prototype for reporting LinphoneFriendList removal.
* @param lc LinphoneCore object
* @param list LinphoneFriendList object
*/
void friendListRemoved(LinphoneCore lc, LinphoneFriendList list);
}
其中, 有个地方implements这个方法
LinphoneCoreListenerBase implements LinphoneCoreListener
目的是为了, 可以随意的继承想要的方法, 而不用全部实现.
说明
这个是回调的核心方法. 提供所有需要回调的方法.
2.2 LinphoneCall.java 电话的回调状态
代码
/**
* Linphone call states
*
*/
static class State {
static private Vector
private final int mValue;
public final int value() {return mValue;}
private final String mStringValue;
/**
* Idle
*/
public final static State Idle = new State(0,"Idle");
/**
* Incoming call received.
*/
public final static State IncomingReceived = new State(1,"IncomingReceived");
/**
* Outgoing call initialiazed.
*/
public final static State OutgoingInit = new State(2,"OutgoingInit");
/**
* Outgoing call in progress.
*/
public final static State OutgoingProgress = new State(3,"OutgoingProgress");
/**
* Outgoing call ringing.
*/
public final static State OutgoingRinging = new State(4,"OutgoingRinging");
/**
* Outgoing call early media
*/
public final static State OutgoingEarlyMedia = new State(5,"OutgoingEarlyMedia");
/**
* Connected
*/
public final static State Connected = new State(6,"Connected");
/**
* Streams running
*/
public final static State StreamsRunning = new State(7,"StreamsRunning");
/**
* Pausing
*/
public final static State Pausing = new State(8,"Pausing");
/**
* Paused
*/
public final static State Paused = new State(9,"Paused");
/**
* Resuming
*/
public final static State Resuming = new State(10,"Resuming");
/**
* Refered
*/
public final static State Refered = new State(11,"Refered");
/**
* Error
*/
public final static State Error = new State(12,"Error");
/**
* Call end
*/
public final static State CallEnd = new State(13,"CallEnd");
/**
* Paused by remote
*/
public final static State PausedByRemote = new State(14,"PausedByRemote");
/**
* The call's parameters are updated, used for example when video is asked by remote
*/
public static final State CallUpdatedByRemote = new State(15, "UpdatedByRemote");
/**
* We are proposing early media to an incoming call
*/
public static final State CallIncomingEarlyMedia = new State(16,"IncomingEarlyMedia");
/**
* We have initiated a call update. When the remote accepts the call update, state will move to StreamsRunning.
*/
public static final State CallUpdating = new State(17, "Updating");
/**
* The call object is now released.
*/
public static final State CallReleased = new State(18,"Released");
/**
* The call is updated by remote while not yet answered (SIP UPDATE in early dialog received)
*/
public static final State CallEarlyUpdatedByRemote = new State(19,"EarlyUpdatedByRemote");
/**
* We are updating the call while not yet answered (SIP UPDATE in early dialog sent)
**/
public static final State CallEarlyUpdating = new State(20,"EarlyUpdating");
private State(int value,String stringValue) {
mValue = value;
values.addElement(this);
mStringValue=stringValue;
}
public static State fromInt(int value) {
for (int i=0; i State state = (State) values.elementAt(i); if (state.mValue == value) return state; } throw new RuntimeException("state not found ["+value+"]"); } public String toString() { return mStringValue; } } 说明 这个方法中包含了所有电话的状态, 比如 IncomingReceived 接收来电OutgoingInit 拨出电话OutongingProgress 电话拨出中OutgoingRinging 拨出响铃中OutgoingEarlyMedia ConnectedStreamsRunning 接通中Paused 暂停Resuming ReferedErrorCallEndPausedByRemote 被远方暂停UpdatedByRemoteInconingEarlyMediaUpdatingReleasedEarlyUpdatedByRemoteEarlyUpdating 等我看完了, 怎么有这么多方法. 2.3 LinphoneCore.GlobalState Global状态 代码 /** * linphone core states */ static public class GlobalState { static private Vector /** * Off */ static public GlobalState GlobalOff = new GlobalState(0, "GlobalOff"); /** * Startup */ static public GlobalState GlobalStartup = new GlobalState(1, "GlobalStartup"); /** * On */ static public GlobalState GlobalOn = new GlobalState(2, "GlobalOn"); /** * Shutdown */ static public GlobalState GlobalShutdown = new GlobalState(3, "GlobalShutdown"); /** * Configuring */ static public GlobalState GlobalConfiguring = new GlobalState(4, "GlobalConfiguring"); 说明 表示全局的状态 GlobalOffGlobalStartupGlobalOnGlobalStudownGlobalConfiguring 全局的一个状态 2.4 LinphoneCoreListenerBase.java 这个是实现所有LinphoneCoreListener方法, 目的是不用实现所有的方法, 而是有目的的实现其中的某个或某几个方法. 2.5 LinphoneChatMessage.State 代码 public static class State { static private Vector private final int mValue; public final int value() {return mValue;} private final String mStringValue; /** * Initial state */ public final static State Idle = new State(0,"Idle"); /** * Delivery in progress */ public final static State InProgress = new State(1,"InProgress"); /** * Message succesffully delivered an acknoleged by remote end point */ public final static State Delivered = new State(2,"Delivered"); /** * Message was not delivered */ public final static State NotDelivered = new State(3,"NotDelivered"); /** * Message was received(and acknowledged) but cannot get file from server */ public final static State FileTransferError = new State(4,"FileTransferError"); /** * File transfer has been completed successfully. */ public final static State FileTransferDone = new State(5,"FileTransferDone"); /** * Message successfully delivered and acknowledged to destination */ public final static State DeliveredToUser = new State(6,"DeliveredToUser"); /** * Message displayed to the remote user */ public final static State Displayed = new State(7,"Displayed"); private State(int value,String stringValue) { mValue = value; values.addElement(this); mStringValue=stringValue; } public static State fromInt(int value) { for (int i=0; i State state = (State) values.elementAt(i); if (state.mValue == value) return state; } throw new RuntimeException("state not found ["+value+"]"); } public String toString() { return mStringValue; } public int toInt() { return mValue; } } 说明 主要是聊天中的一些状态 IdleInProgressDeliveredNotDeliveredFileTransferErrorFileTransferDoneDeliveredToUserDisplayed 3. 逐一介绍 3.1 CallActivity.java 代码 mListener = new LinphoneCoreListenerBase() { @Override public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { displayMissedChats(); // 从jni获取数据, 显示在界面上 } @Override public void callState(LinphoneCore lc, final LinphoneCall call, LinphoneCall.State state, String message) { LinphoneUtils._log("CallActivity#Turn", "callState(" + "LinphoneCore lc " + lc + ", final LinphoneCall call" + call + ", LinphoneCall.State state" + state + ", String message" + message + ")"); if (LinphoneManager.getLc().getCallsNb() == 0) { finish(); return; } if (state == State.IncomingReceived) { startIncomingCallActivity(); return; } else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) { LinphoneUtils._log("CallActivity", "} else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) {"); if (LinphoneManager.getLc().getCurrentCall() != null) { LinphoneUtils._log("CallActivity", "if (LinphoneManager.getLc().getCurrentCall() != null) enabledVideoButton(false);"); enabledVideoButton(false); } if (isVideoEnabled(call)) { LinphoneUtils._log("CallActivity", "if (isVideoEnabled(call)) showAudioView()"); showAudioView(); } } else if (state == State.Resuming) { if (LinphonePreferences.instance().isVideoEnabled()) { status.refreshStatusItems(call, isVideoEnabled(call)); if (call.getCurrentParamsCopy().getVideoEnabled()) { showVideoView(); } } if (LinphoneManager.getLc().getCurrentCall() != null) { enabledVideoButton(true); } } else if (state == State.StreamsRunning) { LinphoneUtils._log("CallActivity", "} else if (state == State.StreamsRunning) {"); switchVideo(isVideoEnabled(call)); enableAndRefreshInCallActions(); if (status != null) { LinphoneUtils._log("CallActivity", "if (status != null) {"); videoProgress.setVisibility(View.GONE); status.refreshStatusItems(call, isVideoEnabled(call)); } else { LinphoneUtils._log("CallActivity", "if (status ===== null) {"); } } else if (state == State.CallUpdatedByRemote) { LinphoneUtils._log("CallActivity", "} else if (state == State.CallUpdatedByRemote) {"); // If the correspondent proposes video while audio call boolean videoEnabled = LinphonePreferences.instance().isVideoEnabled(); if (!videoEnabled) { LinphoneUtils._log("CallActivity#Turn", "if (!videoEnabled) acceptCallUpdate(false);"); acceptCallUpdate(false); } boolean remoteVideo = call.getRemoteParams().getVideoEnabled(); boolean localVideo = call.getCurrentParamsCopy().getVideoEnabled(); boolean autoAcceptCameraPolicy = LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests(); if (remoteVideo && !localVideo && !autoAcceptCameraPolicy && !LinphoneManager.getLc().isInConference()) { showAcceptCallUpdateDialog(); createTimerForDialog(SECONDS_BEFORE_DENYING_CALL_UPDATE); } // else if (remoteVideo && !LinphoneManager.getLc().isInConference() && autoAcceptCameraPolicy) { // mHandler.post(new Runnable() { // @Override // public void run() { // acceptCallUpdate(true); // } // }); // } } 说明 在这个界面实际上就是管理接听话, 还有是否接受视频通话等. 何处调用 在LinphoneActivity开启的时候, 会对状态有个判断. 而LinphoneActivity是被LinphoneService调用的. 3.2 CallIncomingActivity.java 代码 mListener = new LinphoneCoreListenerBase() { @Override public void callState(LinphoneCore lc, LinphoneCall call, State state, String message) { LinphoneUtils._log("CallIncomingActivity", "callState(LinphoneCore lc " + lc + ", LinphoneCall call " + call + ", State state " + state + ", String message) " + message + ""); if (call == mCall && State.CallEnd == state) { finish(); } if (state == State.StreamsRunning) { // The following should not be needed except some devices need it (e.g. Galaxy S). LinphoneManager.getLc().enableSpeaker(LinphoneManager.getLc().isSpeakerEnabled()); } } }; 说明 当有来电电话时, 调用此界面 何处调用 在CallActivity中, 有一个事件监听, 当收到IncomingReceived这个状态的时候调用. 3.3 CallOutgoingActivity.java 代码 mListener = new LinphoneCoreListenerBase() { @Override public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { if (call == mCall && State.Connected == state) { if (!LinphoneActivity.isInstanciated()) { return; } LinphoneActivity.instance().startIncallActivity(mCall); finish(); return; } else if (state == State.Error) { // Convert LinphoneCore message for internalization if (message != null && call.getErrorInfo().getReason() == Reason.Declined) { displayCustomToast(getString(R.string.error_call_declined), Toast.LENGTH_SHORT); } else if (message != null && call.getErrorInfo().getReason() == Reason.NotFound) { displayCustomToast(getString(R.string.error_user_not_found), Toast.LENGTH_SHORT); } else if (message != null && call.getErrorInfo().getReason() == Reason.Media) { displayCustomToast(getString(R.string.error_incompatible_media), Toast.LENGTH_SHORT); } else if (message != null && call.getErrorInfo().getReason() == Reason.Busy) { displayCustomToast(getString(R.string.error_user_busy), Toast.LENGTH_SHORT); } else if (message != null) { displayCustomToast(getString(R.string.error_unknown) + " - " + message, Toast.LENGTH_SHORT); } } if (LinphoneManager.getLc().getCallsNb() == 0) { finish(); return; } } }; 说明 类似CallIncomingActivity的调用方式, 只是逻辑流程相反而已. 何处调用 跟CallIncomingActivity的调用方式相同, 只是状态不同, 走的逻辑不相同. 3.4 ChatFragment.java 代码 mListener = new LinphoneCoreListenerBase(){ @Override public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { LinphoneAddress from = cr.getPeerAddress(); if (from.asStringUriOnly().equals(sipUri)) { LinphoneService.instance().removeMessageNotification(); cr.markAsRead(); LinphoneActivity.instance().updateMissedChatCount(); adapter.addMessage(cr.getHistory(1)[0]); String externalBodyUrl = message.getExternalBodyUrl(); LinphoneContent fileTransferContent = message.getFileTransferInformation(); if (externalBodyUrl != null || fileTransferContent != null) { LinphoneActivity.instance().checkAndRequestExternalStoragePermission(); } } } @Override public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom room) { if (chatRoom != null && room != null && chatRoom.getPeerAddress().asStringUriOnly().equals(room.getPeerAddress().asStringUriOnly())) { remoteComposing.setVisibility(chatRoom.isRemoteComposing() ? View.VISIBLE : View.GONE); } } }; 说明 主要是处理接收消息的逻辑. 3.5 ChatListFragment.java 代码 mListener = new LinphoneCoreListenerBase() { @Override public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { refresh(); } }; 说明 更新消息列表 3.6 LinphoneActivity.java 代码 mListener = new LinphoneCoreListenerBase() { @Override public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) { displayMissedChats(getUnreadMessageCount()); } @Override public void registrationState(LinphoneCore lc, LinphoneProxyConfig proxy, LinphoneCore.RegistrationState state, String smessage) { if (state.equals(RegistrationState.RegistrationCleared)) { if (lc != null) { LinphoneAuthInfo authInfo = lc.findAuthInfo(proxy.getIdentity(), proxy.getRealm(), proxy.getDomain()); if (authInfo != null) lc.removeAuthInfo(authInfo); } } refreshAccounts(); if (getResources().getBoolean(R.bool.use_phone_number_validation) && proxy.getDomain().equals(getString(R.string.default_domain))) { if (state.equals(RegistrationState.RegistrationOk)) { LinphoneManager.getInstance().isAccountWithAlias(); } } if (state.equals(RegistrationState.RegistrationFailed) && newProxyConfig) { newProxyConfig = false; if (proxy.getError() == Reason.BadCredentials) { //displayCustomToast(getString(R.string.error_bad_credentials), Toast.LENGTH_LONG); } if (proxy.getError() == Reason.Unauthorized) { displayCustomToast(getString(R.string.error_unauthorized), Toast.LENGTH_LONG); } if (proxy.getError() == Reason.IOError) { displayCustomToast(getString(R.string.error_io_error), Toast.LENGTH_LONG); } } } @Override public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { LinphoneUtils._log("LinphoneActivity", "callState(" + "LinphoneCore lc " + lc + ", LinphoneCall call " + call + ", LinphoneCall.State state " + state + ", String message " + message + ")"); if (state == State.IncomingReceived) { LinphoneUtils._log("LinphoneActivity", "startActivity(new Intent(LinphoneActivity.instance(), CallIncomingActivity.class));"); startActivity(new Intent(LinphoneActivity.instance(), CallIncomingActivity.class)); } else if (state == State.OutgoingInit || state == State.OutgoingProgress) { LinphoneUtils._log("LinphoneActivity", "startActivity(new Intent(LinphoneActivity.instance(), CallOutgoingActivity.class));"); startActivity(new Intent(LinphoneActivity.instance(), CallOutgoingActivity.class)); } else if (state == State.CallEnd || state == State.Error || state == State.CallReleased) { LinphoneUtils._log("LinphoneActivity", "resetClassicMenuLayoutAndGoBackToCallIfStillRunning();"); resetClassicMenuLayoutAndGoBackToCallIfStillRunning(); } int missedCalls = LinphoneManager.getLc().getMissedCallsCount(); displayMissedCalls(missedCalls); } }; 说明 功能主界面, 主要是用来处理所有状态的分配, 比如跳转到图像管理界面CallActivity, 显示主界面, 拨打电话界面. 3.7 LinphoneService.java 代码 LinphoneManager.getLc().addListener(mListener = new LinphoneCoreListenerBase() { @Override public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { LinphoneUtils._log("LinphoneService", "callState(LinphoneCore lc " + lc + ", LinphoneCall call " + call + ", LinphoneCall.State state " + state + ", String message " + message + ")"); if (instance == null) { Log.i("Service not ready, discarding call state change to ", state.toString()); return; } if (state == LinphoneCall.State.IncomingReceived) { onIncomingReceived(); } if (state == State.CallEnd || state == State.CallReleased || state == State.Error) { destroyOverlay(); } if (state == State.CallEnd && call.getCallLog().getStatus() == CallStatus.Missed) { int missedCallCount = LinphoneManager.getLcIfManagerNotDestroyedOrNull().getMissedCallsCount(); String body; if (missedCallCount > 1) { LinphoneService._log("missedCallCount > 1"); body = getString(R.string.missed_calls_notif_body).replace("%i", String.valueOf(missedCallCount)); } else { LinphoneAddress address = call.getRemoteAddress(); LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(address); LinphoneService._log("!!!!!!!(missedCallCount > 1) , address = " + address + " , c = "); if (c != null) { body = c.getFullName(); } else { body = address.getDisplayName(); if (body == null) { body = address.asStringUriOnly(); } } } Notification notif = Compatibility.createMissedCallNotification(instance, getString(R.string.missed_calls_notif_title), body, mMissedCallsNotifContentIntent); notifyWrapper(MISSED_NOTIF_ID, notif); } if (state == State.StreamsRunning) { // Workaround bug current call seems to be updated after state changed to streams running if (getResources().getBoolean(R.bool.enable_call_notification)) refreshIncallIcon(call); } else { if (getResources().getBoolean(R.bool.enable_call_notification)) refreshIncallIcon(LinphoneManager.getLc().getCurrentCall()); } } @Override public void globalState(LinphoneCore lc, LinphoneCore.GlobalState state, String message) { if (state == GlobalState.GlobalOn && displayServiceNotification()) { LinphoneService._log("globalState(LinphoneCore lc" + ", LinphoneCore.GlobalState state == " + state + ", String message == " + message); sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); } } @Override public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage) { // if (instance == null) { // Log.i("Service not ready, discarding registration state change to ",state.toString()); // return; // } LinphoneService._log("LinphoneCore lc" + ", LinphoneProxyConfig cfg == " + cfg + ", LinphoneCore.RegistrationState state == " + state + ", String smessage == " + smessage); if (!mDisableRegistrationStatus) { if (displayServiceNotification() && state == RegistrationState.RegistrationOk && LinphoneManager.getLc().getDefaultProxyConfig() != null && LinphoneManager.getLc().getDefaultProxyConfig().isRegistered()) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_registered); } if (displayServiceNotification() && (state == RegistrationState.RegistrationFailed || state == RegistrationState.RegistrationCleared) && (LinphoneManager.getLc().getDefaultProxyConfig() == null || !LinphoneManager.getLc().getDefaultProxyConfig().isRegistered())) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_register_failure); } if (displayServiceNotification() && state == RegistrationState.RegistrationNone) { sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); } } } }); 说明 这个时非常重要的逻辑了, 是主要的监听服务. 而且弹出的Notification这里也有管理. 3.8 StatusFragment.java 代码 mListener = new LinphoneCoreListenerBase(){ @Override public void registrationState(final LinphoneCore lc, final LinphoneProxyConfig proxy, final LinphoneCore.RegistrationState state, String smessage) { if (!isAttached || !LinphoneService.isReady()) { return; } if(lc.getProxyConfigList() == null){ statusLed.setImageResource(R.drawable.led_disconnected); statusText.setText(getString(R.string.no_account)); } else { statusLed.setVisibility(View.VISIBLE); } if (lc.getDefaultProxyConfig() != null && lc.getDefaultProxyConfig().equals(proxy)) { statusLed.setImageResource(getStatusIconResource(state, true)); statusText.setText(getStatusIconText(state)); } else if(lc.getDefaultProxyConfig() == null) { statusLed.setImageResource(getStatusIconResource(state, true)); statusText.setText(getStatusIconText(state)); } try { statusText.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { lc.refreshRegisters(); } }); } catch (IllegalStateException ise) {} } @Override public void notifyReceived(LinphoneCore lc, LinphoneEvent ev, String eventName, LinphoneContent content) { if(!content.getType().equals("application")) return; if(!content.getSubtype().equals("simple-message-summary")) return; if (content.getData() == null) return; int unreadCount = -1; String data = content.getDataAsString(); String[] voiceMail = data.split("voice-message: "); final String[] intToParse = voiceMail[1].split("/",0); unreadCount = Integer.parseInt(intToParse[0]); if (unreadCount > 0) { voicemailCount.setText(unreadCount); voicemail.setVisibility(View.VISIBLE); voicemailCount.setVisibility(View.VISIBLE); } else { voicemail.setVisibility(View.GONE); voicemailCount.setVisibility(View.GONE); } } }; 说明 显示音频视频的速率 **Audio** Codec:opus / 48kHz Encoder:An opus encoder. Decoder:An opus decoder. Upload bandwidth:31 kbits/s Download bandwidth:32 kbits/s ICE connectivity: Not activated IP Familiy:ipV4 Sender loss rate: 0% Receiver loss rate: 0% Jitter buffer:32.79ms **Video** Encoder:A VP8 video encoder using libvpx library. Decoder:A VP8 video decoder using libvpx library. Upload bandwidth:194 kbits/s Download bandwidth:0 kbits/s ICE connectivity: not activated IP Family:Ipv4 Sender losdd rate:0% Recevier loss rate: 0% Sent video resolution: 480x640 Received video resolution: 352*288 3.9 AssistantActivity.java 代码 mListener = new LinphoneCoreListenerBase() { @Override public void configuringStatus(LinphoneCore lc, final LinphoneCore.RemoteProvisioningState state, String message) { if (progress != null) progress.dismiss(); if (state == LinphoneCore.RemoteProvisioningState.ConfiguringSuccessful) { LOG("AssistantActivity goToLinphoneActivity();"); goToLinphoneActivity(); } else if (state == LinphoneCore.RemoteProvisioningState.ConfiguringFailed) { Toast.makeText(AssistantActivity.instance(), getString(R.string.remote_provisioning_failure), Toast.LENGTH_LONG).show(); } } @Override public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, RegistrationState state, String smessage) { if (remoteProvisioningInProgress) { if (progress != null) progress.dismiss(); if (state == RegistrationState.RegistrationOk) { remoteProvisioningInProgress = false; success(); } } else if (accountCreated && !newAccount) { if (address != null && address.asString().equals(cfg.getAddress().asString())) { if (state == RegistrationState.RegistrationOk) { if (progress != null) progress.dismiss(); if (LinphoneManager.getLc().getDefaultProxyConfig() != null) { accountCreator.isAccountUsed(); } } else if (state == RegistrationState.RegistrationFailed) { if (progress != null) progress.dismiss(); if (dialog == null || !dialog.isShowing()) { dialog = createErrorDialog(cfg, smessage); dialog.show(); } } else if (!(state == RegistrationState.RegistrationProgress)) { if (progress != null) progress.dismiss(); } } } } }; 说明 3.10 RemoteProvisioningActivity.java 代码 mListener = new LinphoneCoreListenerBase(){ @Override public void configuringStatus(LinphoneCore lc, final RemoteProvisioningState state, String message) { if (spinner != null) spinner.setVisibility(View.GONE); if (state == RemoteProvisioningState.ConfiguringSuccessful) { goToLinphoneActivity(); } else if (state == RemoteProvisioningState.ConfiguringFailed) { Toast.makeText(RemoteProvisioningActivity.this, R.string.remote_provisioning_failure, Toast.LENGTH_LONG).show(); } } }; 说明 3.11 RemoteProvisioningLoginActivity.java 代码 mListener = new LinphoneCoreListenerBase(){ @Override public void configuringStatus(LinphoneCore lc, final LinphoneCore.RemoteProvisioningState state, String message) { if (state == LinphoneCore.RemoteProvisioningState.ConfiguringSuccessful) { //TODO } else if (state == LinphoneCore.RemoteProvisioningState.ConfiguringFailed) { Toast.makeText(RemoteProvisioningLoginActivity.this, R.string.remote_provisioning_failure, Toast.LENGTH_LONG).show(); } } }; 说明 4. LinphoneCoreListener中callState()和transferState的区别 代码 /** * Notifies progress of a call transfer. * @param lc the LinphoneCore * @param call the call through which the transfer was sent. * @param new_call_state the state of the call resulting of the transfer, at the other party. **/ void transferState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State new_call_state); /** Call State notification * @param state LinphoneCall.State */ void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message); 说明 transferState时一个新到的电话的状态, callstate是第一个通信的状态. 5. 监听状态是一个观察者模式 调用此方法注册Listener linphonecore_jni.cc中 extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addListener(JNIEnv* env, jobject thiz, jlong lc, jobject jlistener) { LinphoneJavaBindings *ljb = (LinphoneJavaBindings *) linphone_core_get_user_data((LinphoneCore *)lc); LinphoneCoreVTable *vTable = linphone_core_v_table_new(); LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, jlistener, ljb); linphone_core_v_table_set_user_data(vTable, ldata); linphone_core_add_listener((LinphoneCore*)lc, vTable); } 在vtabls.c中 void linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable){ LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get()); _linphone_core_cbs_set_v_table(cbs, vtable, FALSE); _linphone_core_add_callbacks(lc, cbs, FALSE); linphone_core_cbs_unref(cbs); } 因为要涉及到很多结构体的使用, 如果不分析结构体, 就没办法彻底的弄明白这个观察者模式时怎么来的. 这会又要涉及到很多的内容, 所以下一篇详细追踪.