一.主要简介
作为快速上手的介绍,当然只是介绍需要重点关注能够快速定位代码的地方,有些具体的代码不会做详细介绍,只介绍需要关注类的重点代码片段。
二.功能介绍
上面两张图片分别是通话状态和来电状态图片,我主要介绍也是围绕这两张图片功能来介绍。
主要功能:
1.显示通话信息(联系人信息,通话状态时间等)
2.外音,静音,录音,键盘,呼叫保持,多方通话,电话切换,多方通话管理,视频通话,挂断电话,短信拒接等功能
3.来电通知栏提醒,全屏显示,通话切后台切换,靠近灭屏等功能
三.关键类和布局介绍
1.IncallUi重点只有一个InCallActivity,所有界面都是通过它切换fragment管理。
@Override
public void onFragmentAttached(Fragment fragment) {
if (fragment instanceof DialpadFragment) {
mDialpadFragment = (DialpadFragment) fragment;
} else if (fragment instanceof AnswerFragment) {
mAnswerFragment = (AnswerFragment) fragment;
} else if (fragment instanceof CallCardFragment) {
mCallCardFragment = (CallCardFragment) fragment;
mChildFragmentManager = mCallCardFragment.getChildFragmentManager();
} else if (fragment instanceof ConferenceManagerFragment) {
mConferenceManagerFragment = (ConferenceManagerFragment) fragment;
} else if (fragment instanceof CallButtonFragment) {
mCallButtonFragment = (CallButtonFragment) fragment;
}
}
private void showFragment(String tag, boolean show, boolean executeImmediately) {
Trace.beginSection("showFragment - " + tag);
final FragmentManager fm = getFragmentManagerForTag(tag);
if (fm == null) {
Log.w(TAG, "Fragment manager is null for : " + tag);
return;
}
Fragment fragment = fm.findFragmentByTag(tag);
if (!show && fragment == null) {
// Nothing to show, so bail early.
return;
}
final FragmentTransaction transaction = fm.beginTransaction();
if (show) {
if (fragment == null) {
fragment = createNewFragmentForTag(tag);
transaction.add(getContainerIdForFragment(tag), fragment, tag);
} else {
transaction.show(fragment);
}
Logger.logScreenView(getScreenTypeForTag(tag), this);
} else {
transaction.hide(fragment);
}
transaction.commitAllowingStateLoss();
if (executeImmediately) {
fm.executePendingTransactions();
}
Trace.endSection();
}
private Fragment createNewFragmentForTag(String tag) {
if (TAG_DIALPAD_FRAGMENT.equals(tag)) {
mDialpadFragment = new DialpadFragment();
return mDialpadFragment;
} else if (TAG_ANSWER_FRAGMENT.equals(tag)) {
if (AccessibilityUtil.isTalkBackEnabled(this)) {
mAnswerFragment = new AccessibleAnswerFragment();
} else {
mAnswerFragment = new GlowPadAnswerFragment();
}
return mAnswerFragment;
} else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) {
mConferenceManagerFragment = new ConferenceManagerFragment();
return mConferenceManagerFragment;
} else if (TAG_CALLCARD_FRAGMENT.equals(tag)) {
mCallCardFragment = new CallCardFragment();
return mCallCardFragment;
}
throw new IllegalStateException("Unexpected fragment: " + tag);
}
private FragmentManager getFragmentManagerForTag(String tag) {
if (TAG_DIALPAD_FRAGMENT.equals(tag)) {
return mChildFragmentManager;
} else if (TAG_ANSWER_FRAGMENT.equals(tag)) {
return mChildFragmentManager;
} else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) {
return getFragmentManager();
} else if (TAG_CALLCARD_FRAGMENT.equals(tag)) {
return getFragmentManager();
}
throw new IllegalStateException("Unexpected fragment: " + tag);
}
private int getScreenTypeForTag(String tag) {
switch (tag) {
case TAG_DIALPAD_FRAGMENT:
return ScreenEvent.INCALL_DIALPAD;
case TAG_CALLCARD_FRAGMENT:
return ScreenEvent.INCALL;
case TAG_CONFERENCE_FRAGMENT:
return ScreenEvent.CONFERENCE_MANAGEMENT;
case TAG_ANSWER_FRAGMENT:
return ScreenEvent.INCOMING_CALL;
default:
return ScreenEvent.UNKNOWN;
}
}
private int getContainerIdForFragment(String tag) {
if (TAG_DIALPAD_FRAGMENT.equals(tag)) {
return R.id.answer_and_dialpad_container;
} else if (TAG_ANSWER_FRAGMENT.equals(tag)) {
return R.id.answer_and_dialpad_container;
} else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) {
return R.id.main;
} else if (TAG_CALLCARD_FRAGMENT.equals(tag)) {
return R.id.main;
}
throw new IllegalStateException("Unexpected fragment: " + tag);
}
2.CallCardFragment.java CallCardPresenter.java主要主要功能,显示通话信息(联系人信息,通话状态时间等),挂断电话,第二通话信息管理,录音时间显示功能:
2.1CallCardFragment.java主要主要功能,显示通话信息(联系人信息,通话状态时间等),挂断电话,第二通话信息管理,录音时间显示功能。
设置通话显示信息接口(通话状态,姓名,号码,通话时间,联系人头像,归属地。。。)。
public void setPrimary(String number, String name, boolean nameIsNumber, String label,
Drawable photo, boolean isSipCall, boolean isContactPhotoShown, boolean isWorkCall)
第二路通话信息显示接口(通话状态,姓名,号码,归属地。。。)。
public void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
String providerLabel, boolean isConference, boolean isVideoCall, boolean isFullscreen)
更新背景颜色
public void updateColors()
显示多方通话管理按钮
public void showManageConferenceCallButton(boolean visible)
更新录音时间
private void updateRecordTimeState(boolean isRecording)
2.2.CallCardPresenter.java查询联系人信息,更新通话状态
查询通话联系人信息
private void startContactInfoSearch(final Call call, final boolean isPrimary,boolean isIncoming)
private void onContactInfoComplete(String callId, ContactCacheEntry entry, boolean isPrimary)
查询联系人头像
private void onImageLoadComplete(String callId, ContactCacheEntry entry)
private void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry)
更新UI显示
public void onUiReady(CallCardUi ui)
更新通话状态
public void onStateChange(InCallState oldState, InCallState newState, CallList callList)
3.CallButtonFragment.java CallButtonPresenter.java 通话各种功能按钮控制显示,外音,静音,录音,键盘,呼叫保持,多方通话,电话切换,多方通话管理,视频通话
3.1.CallButtonFragment.java功能控制,UI按钮显示。
功能按钮值对应
public interface Buttons {
public static final int BUTTON_AUDIO = 0;
public static final int BUTTON_MUTE = 1;
public static final int BUTTON_DIALPAD = 2;
public static final int BUTTON_HOLD = 3;
public static final int BUTTON_SWAP = 4;
public static final int BUTTON_UPGRADE_TO_VIDEO = 5;
public static final int BUTTON_SWITCH_CAMERA = 6;
public static final int BUTTON_DOWNGRADE_TO_AUDIO = 7;
/// M: [Hide and Downgrade button] @{
public static final int BUTTON_HIDE_LOCAL_VIDEO = 8;
/// @}
public static final int BUTTON_ADD_CALL = 9;
public static final int BUTTON_MERGE = 10;
public static final int BUTTON_PAUSE_VIDEO = 11;
public static final int BUTTON_MANAGE_VIDEO_CONFERENCE = 12;
/// M: [Voice Record]
public static final int BUTTON_SWITCH_VOICE_RECORD = 13;
/// M: add other feature. @{
public static final int BUTTON_SET_ECT = 14;
public static final int BUTTON_HANGUP_ALL_CALLS = 15;
public static final int BUTTON_HANGUP_ALL_HOLD_CALLS = 16;
public static final int BUTTON_HANGUP_ACTIVE_AND_ANSWER_WAITING = 17;
/// @}
public static final int BUTTON_COUNT = 18;
}
初始化功能按钮,不同电话状态显示功能不同
public void updateButtonStates()
//添加按钮
public void showButton(int buttonId, boolean show) {
mButtonVisibilityMap.put(buttonId, show ? BUTTON_VISIBLE : BUTTON_HIDDEN);
}
//添加菜单栏显示按钮
private void addToOverflowMenu(int id, View button, PopupMenu menu) {
button.setVisibility(View.GONE);
menu.getMenu().add(Menu.NONE, id, Menu.NONE, button.getContentDescription());
mButtonVisibilityMap.put(id, BUTTON_MENU);
}
//菜单按钮功能按钮点击事件
public boolean onMenuItemClick(MenuItem item) {
Log.d(this, "- onMenuItemClick: " + item);
Log.d(this, " id: " + item.getItemId());
Log.d(this, " title: '" + item.getTitle() + "'");
// add for plug in. @{
if (mRCSeExt.handleMenuItemClick(item)) {
return true;
}
// add for plug in. @}
int mode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
int resId = item.getItemId();
if (resId == R.id.audio_mode_speaker) {
mode = CallAudioState.ROUTE_SPEAKER;
} else if (resId == R.id.audio_mode_earpiece || resId == R.id.audio_mode_wired_headset) {
// InCallCallAudioState.ROUTE_EARPIECE means either the handset earpiece,
// or the wired headset (if connected.)
mode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
} else if (resId == R.id.audio_mode_bluetooth) {
mode = CallAudioState.ROUTE_BLUETOOTH;
} else {
Log.e(this, "onMenuItemClick: unexpected View ID " + item.getItemId()
+ " (MenuItem = '" + item + "')");
}
getPresenter().setAudioMode(mode);
return true;
}
//其它功能键按钮点击事件
public void onClick(View view) {
int id = view.getId();
Log.d(this, "onClick(View " + view + ", id " + id + ")...");
///M: when current call is video call, click callbutton we should
//disable VideoCallFullScreen.
InCallPresenter.getInstance().notifyDisableVideoCallFullScreen();
if (id == R.id.audioButton) {
onAudioButtonClicked();
} else if (id == R.id.addButton) {
getPresenter().addCallClicked();
} else if (id == R.id.muteButton) {
getPresenter().muteClicked(!mMuteButton.isSelected());
} else if (id == R.id.mergeButton) {
getPresenter().mergeClicked();
mMergeButton.setEnabled(false);
} else if (id == R.id.holdButton) {
getPresenter().holdClicked(!mHoldButton.isSelected());
} else if (id == R.id.swapButton) {
getPresenter().swapClicked();
} else if (id == R.id.dialpadButton) {
getPresenter().showDialpadClicked(!mShowDialpadButton.isSelected());
} else if (id == R.id.changeToVideoButton) {
getPresenter().changeToVideoClicked();
} else if (id == R.id.changeToVoiceButton) {
getPresenter().changeToVoiceClicked();
/// M: [Hide button] @{
} else if (id == R.id.hideOrShowLocalVideo) {
onHideVideoCallPreviewClick(!mHideOrShowLocalVideoButton.isSelected());
/// @}
} else if (id == R.id.switchCameraButton) {
getPresenter().switchCameraClicked(
mSwitchCameraButton.isSelected() /* useFrontFacingCamera */);
} else if (id == R.id.pauseVideoButton) {
getPresenter().pauseVideoClicked(
!mPauseVideoButton.isSelected() /* pause */);
} else if (id == R.id.overflowButton) {
if (mOverflowPopup != null) {
/// M: For ALPS01961019, Rapid continuous click twice. @{
mOverflowPopup.dismiss();
/// @}
mOverflowPopup.show();
}
} else if (id == R.id.manageVideoCallConferenceButton) {
onManageVideoCallConferenceClicked();
/// M: for call button feature. @{
} else if (id == R.id.setEctButton) {
getPresenter().onEctMenuSelected();
} else if (id == R.id.hangupAllCallsButton) {
getPresenter().hangupAllClicked();
} else if (id == R.id.hangupAllHoldCallsButton) {
getPresenter().hangupAllHoldCallsClicked();
} else if (id == R.id.hangupActiveAndAnswerWaitingButton) {
getPresenter().hangupActiveAndAnswerWaitingClicked();
/// M: for [Voice Record]
} else if (id == R.id.switch_voice_record) {
onVoiceRecordClick((CompoundButton)view);
/// @}
} else {
Log.wtf(this, "onClick: unexpected");
return;
}
view.performHapticFeedback(
HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
3.2CallButtonPresenter.java 具体功能实现
电话状态改变
public void onStateChange(InCallState oldState, InCallState newState, CallList callList)
更新ui显示
private void updateUi(InCallState state, Call call)
更新界面功能按钮
private void updateButtonsState(Call call) {
Log.v(this, "updateButtonsState");
final CallButtonUi ui = getUi();
final boolean isVideo = VideoUtils.isVideoCall(call);
// Common functionality (audio, hold, etc).
// Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
// (1) If the device normally can hold, show HOLD in a disabled state.
// (2) If the device doesn't have the concept of hold/swap, remove the button.
final boolean showSwap = call.can(
android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
/// M: [Video Call]move show hold controller to updateVideoButtonUI @{
/*boolean showHold = !showSwap
//&& call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
//&& call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
if (isVideo) {
showHold &= call.getVideoFeatures().supportsHold();
}*/
///@}
final boolean isCallOnHold = call.getState() == Call.State.ONHOLD;
final boolean showAddCall = TelecomAdapter.getInstance().canAddCall()
&& UserManagerCompat.isUserUnlocked(ui.getContext());
final boolean showMerge = call.can(
android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
/* google code,move to updateVideoButtonUI.
final boolean showUpgradeToVideo = !isVideo && hasVideoCallCapabilities(call);
final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
*/
final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
/// M: add other feature. @{
final boolean canSetEct = InCallUtils.canSetEct();
final boolean canHangupAllCalls = InCallUtils.canHangupAllCalls();
final boolean canHangupAllHoldCalls = InCallUtils.canHangupAllHoldCalls();
final boolean canHangupActiveAndAnswerWaiting = InCallUtils
.canHangupActiveAndAnswerWaiting();
Log.d(this, "[updateButtonsState] Swap:" + showSwap + " OnHold:"
+ isCallOnHold + " AddCall:" + showAddCall + " Merge:"
+ showMerge + " Mute:" + showMute + " SetEct:" + canSetEct
+ " Hangup[AllCalls:" + canHangupAllCalls + " AllHoldCalls:"
+ canHangupAllHoldCalls + " ActiveAndAnswerWaiting:"
+ canHangupActiveAndAnswerWaiting + "]");
/// @}
ui.showButton(BUTTON_AUDIO, true);
ui.showButton(BUTTON_SWAP, showSwap);
/// M: [Video Call]move show hold controller to updateVideoButtonUI @{
//ui.showButton(BUTTON_HOLD, showHold);
//ui.setHold(isCallOnHold);
ui.showButton(BUTTON_MUTE, showMute);
ui.showButton(BUTTON_ADD_CALL, showAddCall);
updateVideoButtonUI(call, isVideo);
ui.setHold(isCallOnHold);
/// @}
/* google code,move to updateVideoButtonUI.
ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
ui.showButton(BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
ui.showButton(BUTTON_SWITCH_CAMERA, isVideo);
ui.showButton(BUTTON_PAUSE_VIDEO, isVideo);
*/
ui.showButton(BUTTON_DIALPAD, true);
ui.showButton(BUTTON_MERGE, showMerge);
/// M: add other feature. @{
ui.showButton(BUTTON_SET_ECT, canSetEct);
ui.enableButton(BUTTON_SET_ECT,canSetEct);
ui.showButton(BUTTON_HANGUP_ALL_CALLS, canHangupAllCalls);
ui.enableButton(BUTTON_HANGUP_ALL_CALLS, canHangupAllCalls);
ui.showButton(BUTTON_HANGUP_ALL_HOLD_CALLS, canHangupAllHoldCalls);
ui.enableButton(BUTTON_HANGUP_ALL_HOLD_CALLS, canHangupAllHoldCalls);
ui.showButton(BUTTON_HANGUP_ACTIVE_AND_ANSWER_WAITING, canHangupActiveAndAnswerWaiting);
ui.enableButton(BUTTON_HANGUP_ACTIVE_AND_ANSWER_WAITING, canHangupActiveAndAnswerWaiting);
/// @}
ui.updateButtonStates();
}
各种功能接口实现
public void muteClicked(boolean checked)
public void holdClicked(boolean checked)
public void toggleSpeakerphone()
public void swapClicked()
public void mergeClicked()
public void addCallClicked()
public void changeToVoiceClicked()
public void showDialpadClicked(boolean checked)
public void changeToVideoClicked()
public void switchCameraClicked(boolean useFrontFacingCamera)
public void pauseVideoClicked(boolean pause)
4.多方通话功能管理
4.1初始化UI管理
ConferenceManagerFragment.java
初始化UI
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View parent =
inflater.inflate(R.layout.conference_manager_fragment, container, false);
mConferenceParticipantList = (ListView) parent.findViewById(R.id.participantList);
mContactPhotoManager =
ContactPhotoManager.getInstance(getActivity().getApplicationContext());
mActionBarElevation =
(int) getResources().getDimension(R.dimen.incall_action_bar_elevation);
mInflater = LayoutInflater.from(getActivity().getApplicationContext());
/// M: for volte @{
initAddMemberButton(parent);
/// @}
return parent;
}
ConferenceManagerPresenter.java 管理多个电话通路
4.3管理多方通话逐条挂断电话等功能
ConferenceParticipantListAdapter.java
添加通话
public void onStateChange(InCallState oldState, InCallState newState, CallList callList)
private void updateAddMemberButtonForVolte() {
final Call currentCall = CallList.getInstance().getActiveOrBackgroundCall();
if (currentCall == null) {
return;
}
if (currentCall != null && currentCall.isConferenceCall()) {
boolean addMemberVisibility = currentCall
.can(android.telecom.Call.Details.CAPABILITY_INVITE_PARTICIPANTS);
if (!FeatureOptionWrapper.LOCAL_OPTION_ENABLE_ADD_MEMBER) {
addMemberVisibility = false;
}
Log.d(this, "updateAddMemberButtonForVolte() should show add member button : "
+ addMemberVisibility);
getUi().showAddMemberButton(addMemberVisibility);
AddMemberScreenController.getInstance().updateConferenceCallId(currentCall.getId());
}
}
逐条挂断电话
private View.OnClickListener mDisconnectListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
View parent = (View) v.getParent();
String callId = (String) parent.getTag();
/// M: [log optimize]
Log.op(CallList.getInstance().getCallById(callId),
Log.CcOpAction.REMOVE_MEMBER, "disconnect member from conference");
TelecomAdapter.getInstance().disconnectCall(callId);
}
};
逐条分开电话
private View.OnClickListener mSeparateListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
View parent = (View) v.getParent();
String callId = (String) parent.getTag();
/// M: [log optimize]
Log.op(CallList.getInstance().getCallById(callId),
Log.CcOpAction.SEPARATE, "Separate member:" + callId + " from conference");
TelecomAdapter.getInstance().separateCall(callId);
}
};
5.键盘管理
5.1DialpadFragment.java
//所有按钮
private final int[] mButtonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three,
R.id.four, R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star,
R.id.pound};
static {
// Map the buttons to the display characters
mDisplayMap.put(R.id.one, '1');
mDisplayMap.put(R.id.two, '2');
mDisplayMap.put(R.id.three, '3');
mDisplayMap.put(R.id.four, '4');
mDisplayMap.put(R.id.five, '5');
mDisplayMap.put(R.id.six, '6');
mDisplayMap.put(R.id.seven, '7');
mDisplayMap.put(R.id.eight, '8');
mDisplayMap.put(R.id.nine, '9');
mDisplayMap.put(R.id.zero, '0');
mDisplayMap.put(R.id.pound, '#');
mDisplayMap.put(R.id.star, '*');
}
//按键颜色
public void updateColors() {
int textColor = InCallPresenter.getInstance().getThemeColors().mPrimaryColor;
if (mCurrentTextColor == textColor) {
return;
}
DialpadKeyButton dialpadKey;
for (int i = 0; i < mButtonIds.length; i++) {
dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);
}
mCurrentTextColor = textColor;
}
5.2DialpadPresenter.java
处理dtmf按钮
public final void processDtmf(char c) {
Log.d(this, "Processing dtmf key " + c);
// if it is a valid key, then update the display and send the dtmf tone.
if (PhoneNumberUtils.is12Key(c) && mCall != null) {
Log.d(this, "updating display and sending dtmf tone for '" + c + "'");
// Append this key to the "digits" widget.
getUi().appendDigitsToField(c);
// Plays the tone through Telecom.
TelecomAdapter.getInstance().playDtmfTone(mCall.getId(), c);
} else {
Log.d(this, "ignoring dtmf request for '" + c + "'");
}
}
停止dtmf按钮
/**
* Stops the local tone based on the phone type.
*/
public void stopDtmf() {
if (mCall != null) {
Log.d(this, "stopping remote tone");
TelecomAdapter.getInstance().stopDtmfTone(mCall.getId());
}
}
6.来电管理
6.1GlowPadAnswerFragment.java继承AnswerFragment.java
短信拒接功能
AnswerFragment.java 接口public void showMessageDialog()
GlowPadAnswerFragment.java 拒接电话,短信拒接,视频接听,电话接听
public void showTargets(int targetSet, int videoState) {
final int targetResourceId;
final int targetDescriptionsResourceId;
final int directionDescriptionsResourceId;
final int handleDrawableResourceId;
mGlowpad.setVideoState(videoState);
switch (targetSet) {
case TARGET_SET_FOR_AUDIO_WITH_SMS:
targetResourceId = R.array.incoming_call_widget_audio_with_sms_targets;
targetDescriptionsResourceId =
R.array.incoming_call_widget_audio_with_sms_target_descriptions;
directionDescriptionsResourceId =
R.array.incoming_call_widget_audio_with_sms_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
break;
case TARGET_SET_FOR_VIDEO_WITHOUT_SMS:
targetResourceId = R.array.incoming_call_widget_video_without_sms_targets;
targetDescriptionsResourceId =
R.array.incoming_call_widget_video_without_sms_target_descriptions;
directionDescriptionsResourceId =
R.array.incoming_call_widget_video_without_sms_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_video_handle;
break;
case TARGET_SET_FOR_VIDEO_WITH_SMS:
targetResourceId = R.array.incoming_call_widget_video_with_sms_targets;
targetDescriptionsResourceId =
R.array.incoming_call_widget_video_with_sms_target_descriptions;
directionDescriptionsResourceId =
R.array.incoming_call_widget_video_with_sms_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_video_handle;
break;
case TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST:
targetResourceId =
R.array.incoming_call_widget_video_request_targets;
targetDescriptionsResourceId =
R.array.incoming_call_widget_video_request_target_descriptions;
directionDescriptionsResourceId = R.array
.incoming_call_widget_video_request_target_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_video_handle;
break;
/**
* M: [video call]3G Video call doesn't support answer as audio, and reject via SMS. @{
*/
case TARGET_SET_FOR_VIDEO_WITHOUT_SMS_AUDIO:
targetResourceId = R.array.mtk_incoming_call_widget_video_without_sms_audio_targets;
targetDescriptionsResourceId =
R.array.
mtk_incoming_call_widget_video_without_sms_audio_target_descriptions;
directionDescriptionsResourceId =
R.array.
mtk_incoming_call_widget_video_without_sms_audio_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_video_handle;
break;
/** @}*/
case TARGET_SET_FOR_AUDIO_WITHOUT_SMS:
default:
targetResourceId = R.array.incoming_call_widget_audio_without_sms_targets;
targetDescriptionsResourceId =
R.array.incoming_call_widget_audio_without_sms_target_descriptions;
directionDescriptionsResourceId =
R.array.incoming_call_widget_audio_without_sms_direction_descriptions;
handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
break;
}
if (targetResourceId != mGlowpad.getTargetResourceId()) {
mGlowpad.setTargetResources(targetResourceId);
mGlowpad.setTargetDescriptionsResourceId(targetDescriptionsResourceId);
mGlowpad.setDirectionDescriptionsResourceId(directionDescriptionsResourceId);
mGlowpad.setHandleDrawable(handleDrawableResourceId);
mGlowpad.reset(false);
/// M: Force layout to avoid UI abnormally.
mGlowpad.requestLayout();
}
}
6.2来电动画
GlowPadWrapper.java
public void onTrigger(View v, int target) {
Log.d(this, "onTrigger() view=" + v + " target=" + target);
final int resId = getResourceIdForTarget(target);
if (resId == R.drawable.ic_lockscreen_answer) {
mAnswerFragment.onAnswer(VideoProfile.STATE_AUDIO_ONLY, getContext());
mTargetTriggered = true;
} else if (resId == R.drawable.ic_lockscreen_decline) {
mAnswerFragment.onDecline(getContext());
mTargetTriggered = true;
} else if (resId == R.drawable.ic_lockscreen_text) {
mAnswerFragment.onText();
mTargetTriggered = true;
} else if (resId == R.drawable.ic_videocam || resId == R.drawable.ic_lockscreen_answer_video) {
mAnswerFragment.onAnswer(mVideoState, getContext());
mTargetTriggered = true;
} else if (resId == R.drawable.ic_lockscreen_decline_video) {
mAnswerFragment.onDeclineUpgradeRequest(getContext());
mTargetTriggered = true;
} else {
// Code should never reach here.
Log.e(this, "Trigger detected on unhandled resource. Skipping.");
}
}
7.视频通话管理
VideoCallFragment.java
VideoCallPresenter.java
VideoPauseController.java
VideoPauseController.java
8.下面重点管理类
8.1.TelecomAdapter.java通话各种管理功能
TelecomAdapter.java
void rejectCall(String callId, boolean rejectWithMessage, String message)
void disconnectCall(String callId)
void holdCall(String callId)
void unholdCall(String callId)
void mute(boolean shouldMute)
private void switchMuteLed(boolean on)
void separateCall(String callId) .......
8.2StatusBarNotifier.java状态栏通知管理(通话挂后台通知栏,静音通知栏,外音通知栏。。。。)
private void createIncomingCallNotification(Call call, int state, Notification.Builder builder)
private void showNotification(final Call call)
8.3ProximitySensor.java 通话靠近灭屏功能
//监听通话状态
public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
// We ignore incoming state because we do not want to enable proximity
// sensor during incoming call screen. We check hasLiveCall() because a disconnected call
// can also put the in-call screen in the INCALL state.
boolean hasOngoingCall = InCallState.INCALL == newState && callList.hasLiveCall();
boolean isOffhook = (InCallState.OUTGOING == newState) || hasOngoingCall;
if(InCallState.INCOMING != newState && mActFlag){
mActFlag = false;
am.setRingerMode(previousMuteMode);
am.setStreamVolume(AudioManager.STREAM_RING,previousRing, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}
if (isOffhook != mIsPhoneOffhook) {
mIsPhoneOffhook = isOffhook;
mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
mAccelerometerListener.enable(mIsPhoneOffhook);
updateProximitySensorMode();
}
}
//更新靠近灭屏功能
private synchronized void updateProximitySensorMode() {
final int audioMode = mAudioModeProvider.getAudioMode();
CallList callList = CallList.getInstance();
boolean isVideoCall = callList.getFirstCall() != null
&& callList.getFirstCall().isVideoCall(mContext);
boolean screenOnImmediately = (CallAudioState.ROUTE_WIRED_HEADSET == audioMode
|| CallAudioState.ROUTE_SPEAKER == audioMode
|| CallAudioState.ROUTE_BLUETOOTH == audioMode
|| mIsHardKeyboardOpen);
screenOnImmediately |= mDialpadVisible && horizontal;
Log.v(this, "screenonImmediately: ", screenOnImmediately);
Log.i(this, Objects.toStringHelper(this)
.add("keybrd", mIsHardKeyboardOpen ? 1 : 0)
.add("dpad", mDialpadVisible ? 1 : 0)
.add("offhook", mIsPhoneOffhook ? 1 : 0)
.add("hor", horizontal ? 1 : 0)
.add("ui", mUiShowing ? 1 : 0)
.add("aud", CallAudioState.audioRouteToString(audioMode))
.toString());
Log.i(this, "isVideoCall: " + isVideoCall);
/// M: disable Proximity Sensor during VT Call
if (mIsPhoneOffhook && !screenOnImmediately && !isVideoCall && mUiShowing) {
Log.d(this, "Turning on proximity sensor");
// acquire wake lock in this case
if (!shouldSkipAcquireProximityLock()) {
turnOnProximitySensor();
}
} else {
Log.d(this, "Turning off proximity sensor");
// Screen on immediately for incoming call, this give user a chance to notice
// the new incoming call when speaking on an existed call.
if (InCallPresenter.getInstance().getPotentialStateFromCallList(callList)
== InCallState.INCOMING) {
Log.d(this, "Screen on immediately for incoming call");
screenOnImmediately = true;
}
turnOffProximitySensor(screenOnImmediately);
}
}
//打开靠近灭屏功能
private void turnOnProximitySensor() {
if (mProximityWakeLock != null) {
if (!mProximityWakeLock.isHeld()) {
Log.i(this, "Acquiring proximity wake lock");
mProximityWakeLock.acquire();
} else {
Log.i(this, "Proximity wake lock already acquired");
}
}
}
//关闭靠近灭屏功能
private void turnOffProximitySensor(boolean screenOnImmediately) {
if (mProximityWakeLock != null) {
if (mProximityWakeLock.isHeld()) {
Log.i(this, "Releasing proximity wake lock");
int flags =
(screenOnImmediately ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
mProximityWakeLock.release(flags);
} else {
Log.i(this, "Proximity wake lock already released");
}
}
}
9.主要布局介绍
call_button_fragment.xml //功能按钮布局
call_card_fragment.xml //总控制布局
answer_fragment.xml //来电布局
conference_manager_fragment.xml //多方通话布局
incall_dialpad_fragment.xml //键盘布局
primary_call_info.xml //通话信息显示布局
secondary_call_info.xml //第二路通话信息
video_call_fragment.xml //视频通话布局