Android12 Framework电话功能UI定制
文章目录
- 简介
- 代码
- 中间按钮
- Fragment
- 创建VideoCallFragment
- Fragment管理
- 添加按键挂断电话功能
- 相关文章
简介
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
@Overridepublic 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 bug1143842case R.id.call_record_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_RECORD) == BUTTON_VISIBLE ? true : false;break;// UNISOC: add for bug1142881case R.id.manager_conference_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_MANAGE_VIDEO_CONFERENCE) == BUTTON_VISIBLE ? true : false;break;// UNISOC: add for bug1152075case R.id.dialpad_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_DIALPAD) == BUTTON_VISIBLE ? true : false;break;}item.setVisible(visible);}mOverflowPopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {@Overridepublic boolean onMenuItemClick(MenuItem item) {selectMenuItem(item);return true;}});mOverflowButton.setVisibility((mOverflowPopup != null && mOverflowPopup.getMenu() != null && mOverflowPopup.getMenu().hasVisibleItems()) ? View.VISIBLE : View.GONE);/*@}*/}@Overridepublic void setRecord(boolean value) {LogUtil.i("VideoCallFragment.setRecord", "value: " + value);isRecording = value;if(mOverflowPopup == null){//UNISOC:add for bug1143842LogUtil.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><itemandroid:id="@+id/add_call_menu"android:title="@string/incall_label_add_call"/><itemandroid:id="@+id/merge_menu"android:title="@string/incall_content_description_merge_calls"/><itemandroid:id="@+id/hold_call_menu"android:title="@string/incall_content_description_hold"/><itemandroid:id="@+id/swap_call_menu"android:title="@string/incall_content_description_swap_calls"/><itemandroid:id="@+id/changeto_audio_menu"android:title="@string/incall_label_audiocall"/><itemandroid:id="@+id/call_record_menu"android:title="@string/record_menu_title"/><itemandroid:id="@+id/manager_conference_menu"android:title="@string/incall_label_manage"/><itemandroid: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) {// 不显示,移除FragmentVideoCallScreen 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
@Overridepublic 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函数中处理
@Overridepublic 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处理函数
@Overridepublic 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 menuif (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位就能错误匹配问题
作者:帅得不敢出门