简介
Android版本:12
芯片平台:展锐
如下图为通话中的UI,打电话出去时显示的UI与此也差不多,但来电时UI是不一样的
这个界面是InCallActivity,InCallActivity 是 Android 系统中负责通话界面显示的宿主Activity,其核心功能包括管理多个Fragment的显示与隐藏,并协调不同通话状态(如来电、通话中、拨号等)的界面切换。
VideoCallFragment
VideoCallFragment是 InCallActivity 中用于显示视频通话的独立Fragment,主要负责视频通话界面的布局与交互逻辑。其布局文件为@layout/incall_video_call_fragment,通常包含视频预览窗口、控制按钮等组件。 InCallFragment
InCallFragment是InCallActivity中负责显示联系人信息和通话状态的核心Fragment,布局文件为@layout/in_call_fragment。它通过 ButtonController 动态管理通话按键的显示与状态更新,并持有 InCallButtonGridFragment 对象以控制按钮布局。 DialpadFragment
DialpadFragment是InCallActivity中用于拨号键盘的独立Fragment,布局文件为@layout/incall_dialpad_fragment。在通话界面中,它提供数字按键输入功能,支持横屏和竖屏模式下的拨号操作。 核心交互逻辑
InCallActivity通过 InCallPresenter 管理不同Fragment的显示状态,并根据通话状态(如来电、通话中、挂断等)动态调整界面布局。例如,当用户结束通话后返回桌面,再重新启动 Dialer 应用时,系统会检查当前通话状态并决定是否直接跳转至 InCallActivity
代码
要定位其代码,可以通过UI上的文本来查找
中间按钮
比如左上角的"通话录音"
通过搜索,知道文本在这个xml文件中
vendor/sprd/platform/packages/apps/SprdDialer/java/com/android/incallui/sprd/res/values-zh-rCN/strings_ex.xml
<string name="call_recording_setting_title">"通话录音"</string>
<string name="record_menu_title">通话录音</string>
<string name="record_menu_title_recording">录音中</string>
通过record_menu_title可定位到通话中的窗口
vendor/sprd/platform/packages/apps/SprdDialer/java/com/android/incallui/video/impl/VideoCallFragment.java
@Override
public void updateButtonStates() {
LogUtil.i("VideoCallFragment.updateButtonState", null);
//speakerButtonController.updateButtonState();
switchOnHoldCallController.updateButtonState();
/* UNISOC: Add video call option menu@{ */
/* UNISOC:modify for bug608545 @ { */
if (mOverflowPopup != null && mOverflowPopup.getMenu() != null && mOverflowPopup.getMenu().hasVisibleItems()) {
mOverflowPopup.dismiss();
}
/* @} */
mOverflowPopup = new PopupMenu(getActivity(), mOverflowButton);
mOverflowPopup.getMenuInflater().inflate(R.menu.videocall_option_menu, mOverflowPopup.getMenu());
Menu menu = mOverflowPopup.getMenu();
int count = menu.size();
for (int i = 0; i < count; i++) {
MenuItem item = menu.getItem(i);
boolean visible = false;
switch (item.getItemId()) {
case R.id.add_call_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_ADD_CALL) == BUTTON_VISIBLE ? true : false;
break;
case R.id.merge_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_MERGE) == BUTTON_VISIBLE ? true : false;
break;
case R.id.hold_call_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_HOLD) == BUTTON_VISIBLE ? true : false;
break;
case R.id.swap_call_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_SWAP) == BUTTON_VISIBLE ? true : false;
break;
case R.id.changeto_audio_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO) == BUTTON_VISIBLE ? true : false;
break;
// UNISOC: add for bug1143842
case R.id.call_record_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_RECORD) == BUTTON_VISIBLE ? true : false;
break;
// UNISOC: add for bug1142881
case R.id.manager_conference_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_MANAGE_VIDEO_CONFERENCE) == BUTTON_VISIBLE ? true : false;
break;
// UNISOC: add for bug1152075
case R.id.dialpad_menu:
visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_DIALPAD) == BUTTON_VISIBLE ? true : false;
break;
}
item.setVisible(visible);
}
mOverflowPopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
selectMenuItem(item);
return true;
}
});
mOverflowButton.setVisibility((mOverflowPopup != null && mOverflowPopup.getMenu() != null && mOverflowPopup.getMenu().hasVisibleItems()) ? View.VISIBLE : View.GONE);
/*@}*/
}
@Override
public void setRecord(boolean value) {
LogUtil.i("VideoCallFragment.setRecord", "value: " + value);
isRecording = value;
if(mOverflowPopup == null){//UNISOC:add for bug1143842
LogUtil.e(
"VideoCallFragment", "setRecord mOverflowPopup is null return");
return;
}
// 这个R.id.call_record_menu就是对应左上角的“通话录音”的按钮
MenuItem menuItem = mOverflowPopup.getMenu().findItem(R.id.call_record_menu);
if(!value){
menuItem.setTitle(R.string.record_menu_title); // 通话录音
}else{
menuItem.setTitle(R.string.record_menu_title_recording); // 录音中
}
}
R.id.call_record_menu就是对应左上角的“通话录音”的按钮, call_record_menu在
java/com/android/incallui/sprd/res/menu/videocall_option_menu.xml
文件中
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<item
android:id="@+id/add_call_menu"
android:title="@string/incall_label_add_call"/>
<item
android:id="@+id/merge_menu"
android:title="@string/incall_content_description_merge_calls"/>
<item
android:id="@+id/hold_call_menu"
android:title="@string/incall_content_description_hold"/>
<item
android:id="@+id/swap_call_menu"
android:title="@string/incall_content_description_swap_calls"/>
<item
android:id="@+id/changeto_audio_menu"
android:title="@string/incall_label_audiocall"/>
<item
android:id="@+id/call_record_menu"
android:title="@string/record_menu_title"/>
<item
android:id="@+id/manager_conference_menu"
android:title="@string/incall_label_manage"/>
<item
android:id="@+id/dialpad_menu"
android:title="@string/incall_label_dialpad"/>
</group>
</menu>
可以看到通话时窗口中间的几个按钮是由这个videocall_option_menu.xml指定的
如果要屏蔽不显示这些按钮,要改
vendor/sprd/platform/packages/apps/SprdDialer/java/com/android/incallui/incall/impl/InCallFragment.java
isSupportedButton函数指定哪些按钮可用可显示,把不要的按钮的id返回false就可以
private static boolean isSupportedButton(@InCallButtonIds int id) {
return id == InCallButtonIds.BUTTON_AUDIO
|| id == InCallButtonIds.BUTTON_MUTE
|| id == InCallButtonIds.BUTTON_DIALPAD
|| id == InCallButtonIds.BUTTON_HOLD
|| id == InCallButtonIds.BUTTON_SWAP
|| id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO
|| id == InCallButtonIds.BUTTON_ADD_CALL
|| id == InCallButtonIds.BUTTON_MERGE
|| id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE
|| id == InCallButtonIds.BUTTON_SWAP_SIM
|| id == InCallButtonIds.BUTTON_UPGRADE_TO_RTT
|| id == InCallButtonIds.BUTTON_RECORD
|| id == InCallButtonIds.BUTTON_SEND_MESSAGE
|| id == InCallButtonIds.BUTTON_HANGUP_ALL
|| id == InCallButtonIds.BUTTON_ECT
|| id == InCallButtonIds.BUTTON_INVITE
|| id == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY; //UNISOC: add for bug1201283
如下直接返回false则是所有按钮都不显示
private static boolean isSupportedButton(@InCallButtonIds int id) {
return false;
/*
return id == InCallButtonIds.BUTTON_AUDIO
|| id == InCallButtonIds.BUTTON_MUTE
|| id == InCallButtonIds.BUTTON_DIALPAD
|| id == InCallButtonIds.BUTTON_HOLD
|| id == InCallButtonIds.BUTTON_SWAP
|| id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO
|| id == InCallButtonIds.BUTTON_ADD_CALL
|| id == InCallButtonIds.BUTTON_MERGE
|| id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE
|| id == InCallButtonIds.BUTTON_SWAP_SIM
|| id == InCallButtonIds.BUTTON_UPGRADE_TO_RTT
|| id == InCallButtonIds.BUTTON_RECORD
|| id == InCallButtonIds.BUTTON_SEND_MESSAGE
|| id == InCallButtonIds.BUTTON_HANGUP_ALL
|| id == InCallButtonIds.BUTTON_ECT
|| id == InCallButtonIds.BUTTON_INVITE
|| id == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY; //UNISOC: add for bug1201283
*/
}
Fragment
InCallActivity(容器) → 持有 → InCallScreen(逻辑协调) → 管理 → {
InCallFragment(语音通话界面),
VideoInCallFragment(视频通话界面)
}
创建VideoCallFragment
private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
if (didShowVideoCallScreen) {
// 不显示,移除Fragment
VideoCallScreen videoCallScreen = getVideoCallScreen();
if (videoCallScreen.getCallId().equals(call.getId())) {
return false;
}
LogUtil.i(
"InCallActivity.showVideoCallScreenFragment",
"video call fragment exists but arguments do not match");
hideVideoCallScreenFragment(transaction);
}
LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
// 创建Fragment添加到Activity中进行显示
VideoCallScreen videoCallScreen =
VideoBindings.createVideoCallScreen(
call.getId(), (call.getVideoTech() != null && call.getVideoTech().shouldUseSurfaceView()));
transaction.add(
R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN); // 这里会与Tags.VIDEO_CALL_SCREEN绑定
Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
didShowVideoCallScreen = true;
return true;
}
videoCallScreen.getVideoCallScreenFragment()返回VideoCallFragment的实例
VideoCallFragment.java
@Override
public Fragment getVideoCallScreenFragment() {
return this;
}
Fragment管理
findFragmentByTag() 是 FragmentManager 提供的一个方法,用于通过标签(tag)查找已添加到布局中的 Fragment 实例。它是管理和操作 Fragment 的常用手段
private InCallScreen getCurrentInCallScreen(){
if(didShowVideoCallScreen){
// 前面显示创建的时候已经与Tags.VIDEO_CALL_SCREEN绑定了VideoCalFragment这里通过tags可以访问到它的实例
return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);
}else {
return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);
}
}
添加按键挂断电话功能
在InCallFragment与VideoCallFragment中都有挂断电话的按钮,点击按钮就会进行挂断并关闭通话窗口,要添加按键挂断功能,可以在Fragment中监听按键并进行处理。这里实现按返回键进行挂断。
- 先看一下挂断电话的按钮处理函数
InCallFragment.java中挂断在onClick函数中处理
@Override
public void onClick(View view) {
if (view == endCallButton) {
LogUtil.i("InCallFragment.onClick", "end call button clicked");
Logger.get(getContext())
.logImpression(DialerImpression.Type.IN_CALL_DIALPAD_HANG_UP_BUTTON_PRESSED);
inCallScreenDelegate.onEndCallClicked();
} else {
LogUtil.e("InCallFragment.onClick", "unknown view: " + view);
Assert.fail();
}
}
VideoCallFragment.java也有自己的onClick处理函数
@Override
public void onClick(View v) {
if (v == endCallButton) {
LogUtil.i("VideoCallFragment.onClick", "end call button clicked");
// 这三行是挂断处理的代码,后面会抽到endCall函数
inCallButtonUiDelegate.onEndCallClicked();
videoCallScreenDelegate.resetAutoFullscreenTimer();
PostCall.onDisconnectPressed(context);//add for bug1145284
} else if (v == swapCameraButton) {
if (swapCameraButton.getDrawable() instanceof Animatable) {
/* UNISOC: add for bug1177044(1111450) {@*/
Animatable swapAnime = (Animatable) swapCameraButton.getDrawable();
if (swapAnime.isRunning()) {
swapAnime.stop();
}
swapAnime.start();
/* @} */
}
inCallButtonUiDelegate.toggleCameraClicked();
videoCallScreenDelegate.resetAutoFullscreenTimer();
} else if (v == mOverflowButton){ //UNISOC: Add video call option menu
if (mOverflowPopup != null) {
mOverflowPopup.show();
}
}
}
- 监听按键
在Frgment的onCreateView函数中添加
view.setFocusableInTouchMode(true);
view.requestFocus();
view.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// 处理返回键
endCall(); // 这里可以把原来的挂断代码抽出来放到新的endCall函数中
return true;
}
}
return false;
});
这里以VideoCallFragment为例,上面已经知道挂断的代码是三行,把这三行放到endCall中
private void endCall() {
inCallButtonUiDelegate.onEndCallClicked();
videoCallScreenDelegate.resetAutoFullscreenTimer();
PostCall.onDisconnectPressed(context);//add for bug1145284
}
相关文章
Android 11代码实现自动接听电话
安卓设备adb执行AT指令控制电话卡
Android CTA认证电话号码7位就能错误匹配问题
作者:帅得不敢出门