2,来电界面以及响铃
来电时,除了有来电界面,还有响铃。
2.1 来电界面
在前面已经论述过,也是调用InCallController的onCallAdded方法启动来电界面,
大体流程和去电界面相同。不同的是CallList之后的onCallAdded方法,调用流程图如下,
CallList之后的onCallAdded方法如下,
public void onCallAdded(android.telecom.Call telecommCall) {
Trace.beginSection("onCallAdded");
Call call = new Call(telecommCall);
Log.d(this, "onCallAdded: callState=" + call.getState());
if (call.getState() == Call.State.INCOMING ||
call.getState() == Call.State.CALL_WAITING) {
onIncoming(call, call.getCannedSmsResponses());
} else {
onUpdate(call);
}
Trace.endSection();
}
根据call的状态判断是否是来电,如果是来电就调用onIncoming方法,否则就调用onUpdate方法, onIncoming方法如下,
for (Listener listener : mListeners) {
listener.onIncomingCall(call);
}
onIncomingCall方法如下,
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);
}
if (InCallServiceImpl.isDsdaEnabled() && (mInCallActivity != null)) {
mInCallActivity.updateDsdaTab();
}
}
首先会调用startOrFinishUi方法启动界面,
然后调用InCallActivity界面的各个view的Presenter 的onIncomingCall方法,展现来电界面。
和前面的监听器完全相同, mIncomingCallListeners保存着view注册的监听器,
private final List<IncomingCallListener> mIncomingCallListeners = new CopyOnWriteArrayList<>();
IncomingCallListener也是InCallPresenter的内部接口。通过调用addIncomingCallListener方法进行注册,
public void addIncomingCallListener(IncomingCallListener listener) {
Preconditions.checkNotNull(listener);
mIncomingCallListeners.add(listener);
}
在IncallUI结构中,
InCallPresenter控制着InCallActivity界面的显示;
AnswerPresenter控制着AnswerFragment的显示,是InCallActivity界面的一部分;
VideoCallPresenter控制着VideoCallFragment的显示,是InCallActivity界面的一部分;
CallCardPresenter控制着CallCardFragment的显示,是InCallActivity界面的一部分;
CallButtonPresenter控制着CallButtonFragment的显示,是InCallActivity界面的一部分;
这些Presenter一般会实现InCallPresenter的IncomingCallListener监听器。
CallCardPresenter的onIncomingCall方法如下,
public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
// same logic should happen as with onStateChange()
onStateChange(oldState, newState, CallList.getInstance());
}
onStateChange方法会根据当前的call状态控制view的显示。其他Presenter的过程几乎一样,在此就不论述了。
2.2 响铃分析
在来电流程中论述过, 调用Ringer的onCallAdded方法响铃。响铃过程都是在services telecom中,并没有跨进程的调用。
调用流程图如下,
Ringer的onCallAdded方法如下,
public void onCallAdded(final Call call) {
if (call.isIncoming() && call.getState() == CallState.RINGING) {
if (mRingingCalls.contains(call)) {
Log.wtf(this, "New ringing call is already in list of unanswered calls");
}
mRingingCalls.add(call);
updateRinging(call);
}
}
startRingingOrCallWaiting方法主要逻辑如下,
1,获取响铃歌曲,调用AsyncRingtonePlayer的play方法响铃,
mRingtonePlayer.play(foregroundCall.getRingtone());
2,调用Vibrator的vibrate方法振动,
mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
VIBRATION_ATTRIBUTES);
AsyncRingtonePlayer的play方法如下,
void play(Uri ringtone) {
Log.d(this, "Posting play.");
postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
}
新建一子线程,子线程中的handleMessage方法对EVENT_PLAY消息处理如下,
handlePlay((Uri) msg.obj);
handlePlay主要逻辑如下,
1,根据ringtoneUri构造Ringtone对象,
if (mRingtone == null) {
mRingtone = getRingtone(ringtoneUri);
2,调用handleRepeat方法发送EVENT_REPEAT消息并调用Ringtone的play接口响铃,
handleRepeat();
handleRepeat方法如下,
if (mRingtone.isPlaying()) {
Log.d(this, "Ringtone already playing.");
} else {
mRingtone.play();//响铃
Log.i(this, "Repeat ringtone.");
}
// Repost event to restart ringer in {@link RESTART_RINGER_MILLIS}.
synchronized(this) {
if (!mHandler.hasMessages(EVENT_REPEAT)) {//发送EVENT_REPEAT消息
mHandler.sendEmptyMessageDelayed(EVENT_REPEAT, RESTART_RINGER_MILLIS);
}
}
handleMessage方法对EVENT_REPEAT消息处理如下,
case EVENT_REPEAT:
handleRepeat();//重复响铃
break;
当然停止响铃是调用AsyncRingtonePlayer的stop方法,和play方法几乎完全相同,在此就不论述了。