InCallUI中维护通话状态需要大量的消息传递,而为了消息传递代码中定义了的大量的接口,主要集中在InCallPresenter和CallList中
InCallPresenter
packages/apps/InCallUI/src/com/android/incallui/InCallPresenter.java,MVP模式中的M
InCallStateListener
通知通话状态有改变
public interface InCallStateListener {
// TODO: Enhance state to contain the call objects instead of passing CallList
public void onStateChange(InCallState oldState, InCallState newState, CallList callList);
}
private final Set<InCallStateListener> mListeners = Collections.newSetFromMap(
new ConcurrentHashMap<InCallStateListener, Boolean>(8, 0.9f, 1));
在onCallListChange中被调用
@Override
public void onCallListChange(CallList callList) {
...
for (InCallStateListener listener : mListeners) {
listener.onStateChange(oldState, mInCallState, callList);
}
...
}
这个是CallList中接口的实现,调用链条会在后面的CallList中讲。
InCallStateListener是最主要的通知通话状态变化的接口,CallButtonPresenter、CallCardPresenter、ConferenceManagerPresenter、ProximitySensor、StatusBarNotifier和VideoPauseController使用它
IncomingCallListener
通知来电
public interface IncomingCallListener {
public void onIncomingCall(InCallState oldState, InCallState newState, Call call);
}
private final List<IncomingCallListener> mIncomingCallListeners = new CopyOnWriteArrayList<>();
被调用方法
@Override
public void onIncomingCall(Call call) {
...
InCallState newState = startOrFinishUi(InCallState.INCOMING);
InCallState oldState = mInCallState;
Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
mInCallState = newState;
for (IncomingCallListener listener : mIncomingCallListeners) {
listener.onIncomingCall(oldState, mInCallState, call);
}
}
这个是CallList中接口的实现,调用链会追述到CallList中的
public void onCallAdded(android.telecom.Call telecommCall) {
Call call = new Call(telecommCall);
if (call.getState() == Call.State.INCOMING ||
call.getState() == Call.State.CALL_WAITING) {
onIncoming(call, call.getCannedSmsResponses());
} else {
onUpdate(call);
}
...
}
其中onUpdate(call)会导致InCallStateListener接口的调用,依据Call状态分别走不同的路径。
AnswerPresenter、CallButtonPresenter、CallCardPresenter、ConferenceManagerPresenter、StatusBarNotifier和VideoPauseController中使用CanAddCallListener
通知有Call添加
public interface CanAddCallListener {
public void onCanAddCallChanged(boolean canAddCall);
}
private final Set<CanAddCallListener> mCanAddCallListeners = Collections.newSetFromMap(
new ConcurrentHashMap<CanAddCallListener, Boolean>(8, 0.9f, 1));
被调用方法
public void onCanAddCallChanged(boolean canAddCall) {
for (CanAddCallListener listener : mCanAddCallListeners) {
listener.onCanAddCallChanged(canAddCall);
}
}
@Override
public void onCanAddCallChanged(boolean canAddCall) {
InCallPresenter.getInstance().onCanAddCallChanged(canAddCall);
}
只在CallButtonPresenter中注册和反注册,更新通话按键状态。
InCallDetailsListener
通知Call的信息变化
public interface InCallDetailsListener {
public void onDetailsChanged(Call call, android.telecom.Call.Details details);
}
private final Set<InCallDetailsListener> mDetailsListeners = Collections.newSetFromMap(
new ConcurrentHashMap<InCallDetailsListener, Boolean>(8, 0.9f, 1));
被调用方法
private final android.telecom.Call.Callback mCallCallback =
new android.telecom.Call.Callback() {
...
@Override
public void onDetailsChanged(android.telecom.Call telecomCall,
android.telecom.Call.Details details) {
final Call call = mCallList.getCallByTelecommCall(telecomCall);
...
for (InCallDetailsListener listener : mDetailsListeners) {
listener.onDetailsChanged(call, details);
}
}
...
};
是个回调,注册方法为
public void onCallAdded(android.telecom.Call call) {
...
call.registerCallback(mCallCallback);
}
被InCallServiceImpl调用
@Override
public void onCallAdded(Call call) {
if ((CallList.getInstance().getVideoUpgradeRequestCall() != null ||
CallList.getInstance().getSendingVideoUpgradeRequestCall() != null)
&& !isEmergency(call)) {
call.disconnect();
InCallUtils.showOutgoingFailMsg(getApplicationContext(), call);
} else {
CallList.getInstance().onCallAdded(call);
InCallPresenter.getInstance().onCallAdded(call);
}
}
CallButtonPresenter、ConferenceManagerPresenter、VideoCallPresenter和ProximitySensor使用该接口
InCallOrientationListener
横竖屏切换
public interface InCallOrientationListener {
public void onDeviceOrientationChanged(int orientation);
}
private final Set<InCallOrientationListener> mOrientationListeners = Collections.newSetFromMap(
new ConcurrentHashMap<InCallOrientationListener, Boolean>(8, 0.9f, 1));
被调用方法
public void onDeviceOrientationChange(int orientation) {
for (InCallOrientationListener listener : mOrientationListeners) {
listener.onDeviceOrientationChanged(orientation);
}
}
本应该是InCallActivity中调用的,不过mtk目前代码中是注释掉的状态
private void doOrientationChanged(int rotation)
VideoCallPresenter中使用
InCallEventListener
通知视频通话全屏显示的状态变化
public interface InCallEventListener {
public void onFullscreenModeChanged(boolean isFullscreenMode);
}
private final Set<InCallEventListener> mInCallEventListeners = Collections.newSetFromMap(
new ConcurrentHashMap<InCallEventListener, Boolean>(8, 0.9f, 1));
被调用方法
public void notifyFullscreenModeChange(boolean isFullscreenMode) {
for (InCallEventListener listener : mInCallEventListeners) {
listener.onFullscreenModeChanged(isFullscreenMode);
}
}
被调用方法
public boolean toggleFullscreenMode() {
mIsFullScreen = !mIsFullScreen;
Log.v(this, "toggleFullscreenMode = " + mIsFullScreen);
notifyFullscreenModeChange(mIsFullScreen);
return mIsFullScreen;
}
public void setFullScreen(boolean isFullScreen) {
Log.v(this, "setFullScreen = " + isFullScreen);
if (mIsFullScreen == isFullScreen) {
Log.v(this, "setFullScreen ignored as already in that state.");
return;
}
mIsFullScreen = isFullScreen;
notifyFullscreenModeChange(mIsFullScreen);
}
CallCardPresenter和VideoCallPresenter中使用
InCallUiListener
通知通话UI是否要在前台显示或退出前台
public interface InCallUiListener {
void onUiShowing(boolean showing);
}
private final Set<InCallUiListener> mInCallUiListeners = Collections.newSetFromMap(
new ConcurrentHashMap<InCallUiListener, Boolean>(8, 0.9f, 1));
被调用方法
public void onUiShowing(boolean showing) {
...
for (InCallUiListener listener : mInCallUiListeners) {
listener.onUiShowing(showing);
}
...
}
InCallActivity的onResume和onPause调用
实现这个接口的的地方是AnswerPresenter和VideoPauseController
InCallVideoCallListener
通知视频通话中是否开启摄像头和要退出全屏通话状态
public interface InCallVideoCallListener {
void onHidePreviewRequest(boolean hide);
void disbaleFullScreen();
}
private final Set<InCallVideoCallListener> mInCallVideoCallListener = Collections.newSetFromMap(
new ConcurrentHashMap<InCallVideoCallListener, Boolean>(8, 0.9f, 1));
被调用方法
public void notifyHideLocalVideoChanged(boolean hide) {
for (InCallVideoCallListener listener : mInCallVideoCallListener) {
listener.onHidePreviewRequest(hide);
}
}
public void notifyDisableVideoCallFullScreen() {
for (InCallVideoCallListener listener : mInCallVideoCallListener) {
listener.disbaleFullScreen();
}
}
notifyHideLocalVideoChanged被CallButtonFragment的onClick(打开或关闭摄像头),notifyDisableVideoCallFullScreen被CallButtonFragment的onClick(能点击证明肯定不是全屏状态)和InCallActivity的onStop(要退出全屏状态)
VideoCallPresenter中使用
PhoneRecorderListener
更新通话录音Ui
public interface PhoneRecorderListener {
public void onUpdateRecordState(int state, int customValue);
}
private final ArrayList<PhoneRecorderListener> mPhoneRecorderListeners = Lists.newArrayList();
被调用方法
public void onUpdateRecordState(int state, int customValue) {
Log.i(this, "onUpdateRecordState: state = " + state + " ;customValue = " + customValue);
mRecordingState = state;
for (PhoneRecorderListener listener : mPhoneRecorderListeners) {
listener.onUpdateRecordState(state, customValue);
}
}
CallCardlPresenter和CallButtonPresenter中使用
CallList
代码在packages/apps/InCallUI/src/com/android/incallui/CallList.java,维护了Telecom模块上报的通话信息,以Call的形式,所以名称叫CallList。
Listener
public interface Listener {
/**
* Called when a new incoming call comes in.
* This is the only method that gets called for incoming calls. Listeners
* that want to perform an action on incoming call should respond in this method
* because {@link #onCallListChange} does not automatically get called for
* incoming calls.
*/
public void onIncomingCall(Call call);
/**
* Called when a new modify call request comes in
* This is the only method that gets called for modify requests.
*/
public void onUpgradeToVideo(Call call);
/**
* Called anytime there are changes to the call list. The change can be switching call
* states, updating information, etc. This method will NOT be called for new incoming
* calls and for calls that switch to disconnected state. Listeners must add actions
* to those method implementations if they want to deal with those actions.
*/
public void onCallListChange(CallList callList);
/**
* Called when a call switches to the disconnected state. This is the only method
* that will get called upon disconnection.
*/
public void onDisconnect(Call call);
/// M: Add for recording. @{
public void onStorageFull();
public void onUpdateRecordState(final int state, final int customValue);
/// @}
}
private final Set<Listener> mListeners = Collections.newSetFromMap(
new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
InCallPresenter实现该接口,并在setUp中注册监听
mCallList.addListener(this);
这个通知调用的地方众多,拿最常用的路径举例:
private void notifyGenericListeners() {
for (Listener listener : mListeners) {
listener.onCallListChange(this);
}
}
public void onUpdate(Call call) {
onUpdateCall(call);
notifyGenericListeners();
}
packages/apps/InCallUI/src/com/android/incallui/Call.java
private android.telecom.Call.Callback mTelecomCallCallback =
new android.telecom.Call.Callback() {
@Override
public void onStateChanged(android.telecom.Call call, int newState) {
Log.d(this, "TelecommCallCallback onStateChanged call=" + call + " newState="
+ newState);
InCallTrace.begin("telecomStateChanged");
update();
InCallTrace.end("telecomStateChanged");
}
@Override
public void onParentChanged(android.telecom.Call call,
android.telecom.Call newParent) {
Log.d(this, "TelecommCallCallback onParentChanged call=" + call + " newParent="
+ newParent);
update();
}
@Override
public void onChildrenChanged(android.telecom.Call call,
List<android.telecom.Call> children) {
/// M: for VOLTE @{
handleChildrenChanged();
/// @}
update();
}
@Override
public void onDetailsChanged(android.telecom.Call call,
android.telecom.Call.Details details) {
Log.d(this, "TelecommCallCallback onStateChanged call=" + call + " details="
+ details);
InCallTrace.begin("telecomDetailsChanged");
update();
InCallTrace.end("telecomDetailsChanged");
/// M: for VOLTE @{
handleDetailsChanged(details);
/// @}
}
@Override
public void onCannedTextResponsesLoaded(android.telecom.Call call,
List<String> cannedTextResponses) {
Log.d(this, "TelecommCallCallback onStateChanged call=" + call
+ " cannedTextResponses=" + cannedTextResponses);
update();
}
@Override
public void onPostDialWait(android.telecom.Call call,
String remainingPostDialSequence) {
Log.d(this, "TelecommCallCallback onStateChanged call=" + call
+ " remainingPostDialSequence=" + remainingPostDialSequence);
update();
}
@Override
public void onVideoCallChanged(android.telecom.Call call,
VideoCall videoCall) {
Log.d(this, "TelecommCallCallback onStateChanged call=" + call + " videoCall="
+ videoCall);
update();
}
...
@Override
public void onConferenceableCallsChanged(android.telecom.Call call,
List<android.telecom.Call> conferenceableCalls) {
update();
}
};
该回调可以看到众多引起Call变化的原因,它是在Call构造函数中注册的
public Call(android.telecom.Call telecommCall) {
mTelecommCall = telecommCall;
mId = ID_PREFIX + Integer.toString(sIdCounter++);
updateFromTelecommCall();
mTelecommCall.registerCallback(mTelecomCallCallback);
...
}
这样Call有任何变化都会通知已注册的对象。
CallUpdateListener
public interface CallUpdateListener {
public void onCallChanged(Call call);
public void onSessionModificationStateChange(int sessionModificationState);
}
private final HashMap<String, List<CallUpdateListener>> mCallUpdateListenerMap = Maps
.newHashMap();
被调用路径
public void onSessionModificationStateChange(Call call, int sessionModificationState) {
final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
if (listeners != null) {
for (CallUpdateListener listener : listeners) {
listener.onSessionModificationStateChange(sessionModificationState);
}
}
}
public void notifyCallUpdateListeners(Call call) {
final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
if (listeners != null) {
for (CallUpdateListener listener : listeners) {
listener.onCallChanged(call);
}
}
}
onSessionModificationStateChange被Call调用,
public void setSessionModificationState(int state) {
...
if (hasChanged) {
CallList.getInstance().onSessionModificationStateChange(this, state);
}
...
}
该方法调用地方很多,都是音频和视频通话切换的地方。
notifyCallUpdateListeners调用链最后还是追述到刚才讲的mTelecomCallCallback中
众多xxxPresenter都有注册它,不过注册的地方都不在onUiReady方法中。其它类中都是空实现,只有AnswerPresenter确切实现了onCallChanged,主要功能是用于更新短信拒接的内容。
吐槽
可以看出接口很多很复杂很头晕。
1.CanAddCallListener,为啥要单独定义这么个接口给CallButton用,onStateChange也在更新通话状态呀,这个是多余的接口
2.IncomingCallListener,为啥要单独定义这个接口,这个和InCallStateListener对比只是Call的状态不同,而且只在添加通话的时候调用一次,代码中基本都是IncomingCallListener和InCallStateListener要同时实现,有什么意义吗?
CallCardPresenter中的实现:
@Override
public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
// same logic should happen as with onStateChange()
onStateChange(oldState, newState, CallList.getInstance());
}
CallButtonPresenter中的实现:
@Override
public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
...
onStateChange(oldState, newState, CallList.getInstance());
}
StatusBarNotifier中实现
@Override
public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
...
updateNotification(newState, CallList.getInstance());
...
}
StatusBarNotifier中的InCallStateListener实现
@Override
public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
...
updateNotification(newState, callList);
}
这个有意思吗?一个if语句的事甚至if都不用要单独一个接口
3.CallUpdateListener ,一堆类实现它,为啥都是空方法?只有一个AnswerPresenter不是空方法,看了代码有必要单独写个接口?更新短信拒接的内容和UI是依据号码就能完成的事情,只有在获取不到对方号码的时候才设置为不能短信拒接,至于短信内容这个是完全固定的。
4.AnswerPresenter 实现了CallList.Listener,这个有点破坏了InCallUI的代码设计,Presenter获取通知应该只和InCallPresenter打交道才对。不过这个是mtk为了视频通话加的,google原生没这样写。
5.和通话状态无关的事件,例如InCallOrientationListener和InCallEventListener(横竖屏变化和退出全屏),可以用本地广播实现。和CallList和InCallPresenter完全没啥关系,实现的地方发送个广播,需要的地方接受广播就可以了。再说也并没有使用InCallOrientationListener,而是直接用了InCallPresenter中的方法:
public void onDeviceRotationChange(int rotation) {
if (mCallList != null) {
mCallList.notifyCallsOfDeviceRotation(toRotationAngle(rotation));
}
}
继而调用了CallList中的notifyCallsOfDeviceRotation:
public void notifyCallsOfDeviceRotation(int rotation) {
for (Call call : mCallById.values()) {
if (call.getVideoCall() != null && CallUtils.isVideoCall(call)) {
call.getVideoCall().setDeviceOrientation(rotation);
}
}
}
其它的notify方法都是CallList通知InCallPresenter,而这个却是调用Call的方法,破坏了CallList的含义,它是被动接受Call变化并发送通知的这么一个类,却有了一个和Telecom通信的方法,应该把这个方法挪到TelecomAdapter中,TelecomAdapter才负责与Telecom通信。
总之接口漫天飞,有过度设计的嫌疑。