Android12 Framework电话功能UI定制

发布于:2025-08-14 ⋅ 阅读:(17) ⋅ 点赞:(0)

简介

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中监听按键并进行处理。这里实现按返回键进行挂断。

  1. 先看一下挂断电话的按钮处理函数
    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();
        }
    }
  }
  1. 监听按键
    在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位就能错误匹配问题
作者:帅得不敢出门


网站公告

今日签到

点亮在社区的每一天
去签到