MTK-Android13-定制Dialer-拨打固定电话显示动画效果
文章目录
前言-基本需求
基本需求就是:MTK-Android13-定制Dialer-拨打固定电话显示动画效果
如下:拨打指定号码播放动画、当然界面其它按钮位置、显示与不显示都是定制过了的。

一、参考资料
之前有Dialer 定制相关,可以借鉴下相关思路:
- 修改哪一部分内容、相关部分模块在哪里
- 如果要添加动画,理论上添加位置在哪里添加比较好
MTK-Android13-Dialer 通话界面定制修改(二)
Camera2和Camera1实现打开USB相机-实现Dialer 调用USB实现预览等视频通话
MTK-Android13-实现Dialer 调用USB实现预览等视频通话
二、修改文件
java
/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/incall/impl/res/raw/
/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml
/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/DialpadFragment.java
/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/InCallActivity.java
/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/incall/impl/InCallFragment.java
初步了解:
- raw 存放播放的动画 比如
.gif文件 dialpad_view_unthemed.xml按键框框去掉白色背景改为透明DialpadFragment.java数字按键框 在Java中的硬编码中把白色背景改为透明InCallActivity.java实现指定的电话判断,来默认弹出数据键盘布局InCallFragment.java、frag_incall_voice.xml各种UI的显示与影藏、根据指定号码是否播放动画
三、实现方案
准备需要播放的动画资源
路径:/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/incall/impl/res/raw/

去掉数字键盘弹框的背景-透明化
路径:/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml /vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/DialpadFragment.java


根据电话号码来判断是否弹框数字按键键盘

在onResume 方法中添加逻辑:
java
@Override
protected void onResume() {
Trace.beginSection("InCallActivity.onResume");
super.onResume();
if (!InCallPresenter.getInstance().isReadyForTearDown()) {
/// M: Update theme colors according to primary call. @{
InCallPresenter.getInstance().setThemeColors();
/// @}
updateTaskDescription();
}
// modify by fangchen start
DialerCall currentCall = CallList.getInstance().getFirstCall();
if (currentCall != null
&& "61520".equals(currentCall.getNumber())
&& !isDialpadVisible()
&& showDialpadRequest == DIALPAD_REQUEST_NONE) {
LogUtil.i("InCallActivity.onResume", "special number, request show dialpad");
showDialpadRequest = DIALPAD_REQUEST_SHOW;
animateDialpadOnShow = true;
}
// modify by fangchen end
.......原有逻辑.......
指定电话号码动画-UI显示与隐藏逻辑判断
动画业务逻辑
定义常量
java
// modify by fangchen start
private static final String SPECIAL_NUMBER = "61520";
// ★ GIF View
private ImageView specialCallGif;
private boolean specialGifLoaded = false;
private boolean specialGifPlaying = false;
// modify by fangchen end
初始化动画view
java
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) {
LogUtil.i("InCallFragment.onViewCreated", null);
super.onViewCreated(view, bundle);
// modify by fangcehen start
specialCallGif = view.findViewById(R.id.special_call_gif);
// modify by fangcehen end
......原有逻辑.......
判断-是否播放动画-判断时机-方法业务
路径:/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/incall/impl/InCallFragment.java
java
@Override
public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
LogUtil.d("InCallFragment.setPrimary", primaryInfo.toString());
setAdapterMedia(primaryInfo.multimediaData(), primaryInfo.showInCallButtonGrid());
contactGridManager.setPrimary(primaryInfo);
// modify by fangchen start
checkAndPlaySpecialGif(primaryInfo);
// modify by fangchen end
.........原有逻辑.......
}
// modify by fangchen start
private void checkAndPlaySpecialGif(PrimaryInfo primaryInfo) {
if (specialCallGif == null) {
return;
}
String number = primaryInfo.number();
LogUtil.d("InCallFragment.checkAndPlaySpecialGif", "number = " + number);
if (number == null) {
LogUtil.i("InCallFragment.checkAndPlaySpecialGif", "number == null return ");
return;
}
String normalized = number.replace(" ", "").replace("-", "");
if (normalized.startsWith("+86")) {
normalized = normalized.substring(3);
}
if (!SPECIAL_NUMBER.equals(normalized)) {
LogUtil.i("InCallFragment.checkAndPlaySpecialGif", "!SPECIAL_NUMBER.equals(normalized) return ");
stopSpecialGif();
return;
}
if (specialGifLoaded) {
LogUtil.i("InCallFragment.checkAndPlaySpecialGif", "specialGifLoaded is true so --> return ");
return;
}
specialGifLoaded = true;
/*if (getActivity() instanceof InCallActivity) {
((InCallActivity) getActivity()).showDialpadFragment(true, true);
}*/
playSpecialGif();
}
private void playSpecialGif() {
LogUtil.i("InCallFragment.playSpecialGif", "==========playSpecialGif ");
specialCallGif.setVisibility(View.VISIBLE);
specialGifPlaying = true;
Glide.with(this)
.asGif()
.load(R.raw.xiaoxing)
// .diskCacheStrategy(DiskCacheStrategy.NONE)
.into(specialCallGif);
}
private void stopSpecialGif() {
if (specialCallGif == null) {
return;
}
if (specialGifPlaying) {
Glide.with(this).clear(specialCallGif);
specialGifPlaying = false;
}
specialCallGif.setVisibility(View.GONE);
specialGifLoaded = false;
}
// modify by fangchen end
部分按键显示与隐藏
路径:java/com/android/incallui/incall/impl/res/layout/frag_incall_voice.xml、/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/incall/impl/InCallFragment.java
这里需要对照业务、定制化UI的需求来实现,对部分图表隐藏、更改位置等客制化需求,当然也有部分代码、布局文件在其它地方,可以参考之前笔记。 这里仅仅将源码罗列出来,自己对比看看。
java
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.incallui.incall.impl;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.telecom.CallAudioState;
import android.telephony.TelephonyManager;
import android.transition.TransitionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.android.dialer.common.Assert;
import com.android.dialer.common.FragmentUtils;
import com.android.dialer.common.LogUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.android.dialer.multimedia.MultimediaData;
import com.android.dialer.strictmode.StrictModeUtils;
import com.android.dialer.widget.LockableViewPager;
import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment;
import com.android.incallui.audioroute.AudioRouteSelectorDialogFragment.AudioRouteSelectorPresenter;
import com.android.incallui.call.CallList;
import com.android.incallui.call.DialerCall;
import com.android.incallui.call.state.DialerCallState;
import com.android.incallui.contactgrid.ContactGridManager;
import com.android.incallui.hold.OnHoldFragment;
import com.android.incallui.incall.impl.ButtonController.SpeakerButtonController;
import com.android.incallui.incall.impl.ButtonController.UpgradeToRttButtonController;
import com.android.incallui.incall.impl.InCallButtonGridFragment.OnButtonGridCreatedListener;
import com.android.incallui.incall.protocol.InCallButtonIds;
import com.android.incallui.incall.protocol.InCallButtonIdsExtension;
import com.android.incallui.incall.protocol.InCallButtonUi;
import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
import com.android.incallui.incall.protocol.InCallScreen;
import com.android.incallui.incall.protocol.InCallScreenDelegate;
import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
import com.android.incallui.incall.protocol.PrimaryCallState;
import com.android.incallui.incall.protocol.PrimaryCallState.ButtonState;
import com.android.incallui.incall.protocol.PrimaryInfo;
import com.android.incallui.incall.protocol.SecondaryInfo;
import java.util.ArrayList;
import java.util.List;
// modify by fangchen start
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.android.incallui.InCallActivity;
// modify by fangchen end
/** Fragment that shows UI for an ongoing voice call. */
public class InCallFragment extends Fragment
implements InCallScreen,
InCallButtonUi,
OnClickListener,
AudioRouteSelectorPresenter,
OnButtonGridCreatedListener {
private List<ButtonController> buttonControllers = new ArrayList<>();
private View endCallButton;
private InCallPaginator paginator;
private LockableViewPager pager;
private InCallPagerAdapter adapter;
private ContactGridManager contactGridManager;
private InCallScreenDelegate inCallScreenDelegate;
private InCallButtonUiDelegate inCallButtonUiDelegate;
private InCallButtonGridFragment inCallButtonGridFragment;
@Nullable private ButtonChooser buttonChooser;
private SecondaryInfo savedSecondaryInfo;
private int voiceNetworkType;
private int phoneType;
private boolean stateRestored;
// modify by fangchen start
private static final String SPECIAL_NUMBER = "61520";
// ★ GIF View
private ImageView specialCallGif;
private boolean specialGifLoaded = false;
private boolean specialGifPlaying = false;
// modify by fangchen end
/// M: ALPS05941240 AudioRouteSelectorDialogFragment would not dismiss when call
/// audio state changed. @{
private static final String TAG_AUDIOROUTE_SELECTOR_FRAGMENT = "tag_audioroute_selector_fragment";
/// @}
// Add animation to educate users. If a call has enriched calling attachments then we'll
// initially show the attachment page. After a delay seconds we'll animate to the button grid.
private final Handler handler = new Handler();
private final Runnable pagerRunnable =
new Runnable() {
@Override
public void run() {
pager.setCurrentItem(adapter.getButtonGridPosition());
}
};
private static boolean isSupportedButton(@InCallButtonIds int id) {
return
id == InCallButtonIds.BUTTON_AUDIO
//|| id == InCallButtonIds.BUTTON_DIALPAD
|| id == InCallButtonIds.BUTTON_MUTE;
//|| id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO;
/*return
// modify by fangchen start
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
/// M: [Voice Record]
// || id == InCallButtonIds.BUTTON_SWITCH_VOICE_RECORD
// modify by fangchen end
/// M: [Hang Up]
|| id == InCallButtonIds.BUTTON_HANG_UP_ALL
|| id == InCallButtonIds.BUTTON_HANG_UP_HOLD
/// M: [ECT(blind)]
|| id == InCallButtonIds.BUTTON_ECT
|| id == InCallButtonIds.BUTTON_BLIND_ECT
/// M: [One way video]
|| id == InCallButtonIds.BUTTON_ONE_WAY_VIDEO
/// M: ALPS03949921, add switch_to_secondary to enable and disable.
|| id == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY;*/
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (savedSecondaryInfo != null) {
setSecondary(savedSecondaryInfo);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
inCallButtonUiDelegate =
FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class)
.newInCallButtonUiDelegate();
if (savedInstanceState != null) {
inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState);
stateRestored = true;
}
}
@Nullable
@Override
@SuppressLint("MissingPermission")
public View onCreateView(
@NonNull LayoutInflater layoutInflater,
@Nullable ViewGroup viewGroup,
@Nullable Bundle bundle) {
LogUtil.i("InCallFragment.onCreateView", null);
getActivity().setTheme(R.style.Theme_InCallScreen);
// Bypass to avoid StrictModeResourceMismatchViolation
final View view =
StrictModeUtils.bypass(
() -> layoutInflater.inflate(R.layout.frag_incall_voice, viewGroup, false));
contactGridManager =
new ContactGridManager(
view,
(ImageView) view.findViewById(R.id.contactgrid_avatar),
getResources().getDimensionPixelSize(R.dimen.incall_avatar_size),
true /* showAnonymousAvatar */);
contactGridManager.onMultiWindowModeChanged(getActivity().isInMultiWindowMode());
paginator = (InCallPaginator) view.findViewById(R.id.incall_paginator);
pager = (LockableViewPager) view.findViewById(R.id.incall_pager);
pager.setOnTouchListener(
(v, event) -> {
handler.removeCallbacks(pagerRunnable);
return false;
});
endCallButton = view.findViewById(R.id.incall_end_call);
endCallButton.setOnClickListener(this);
//endCallButton.setVisibility(View.GONE); // modify by fangchen
if (ContextCompat.checkSelfPermission(getContext(), permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
} else {
LogUtil.d("InCallFragment.onCreateView", "getVoiceNetworkType");
voiceNetworkType =
getContext().getSystemService(TelephonyManager.class).getVoiceNetworkType();
}
// TODO(a bug): Change to use corresponding phone type used for current call.
/// M: Performance. @{
/// Google code:
/// phoneType = getContext().getSystemService(TelephonyManager.class).getPhoneType();
phoneType = TelephonyManager.getPhoneType(voiceNetworkType);
/// @}
// Workaround to adjust padding for status bar and navigation bar since fitsSystemWindows
// doesn't work well when switching with other fragments.
view.addOnAttachStateChangeListener(
new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
View container = v.findViewById(R.id.incall_ui_container);
int topInset = v.getRootWindowInsets().getSystemWindowInsetTop();
int bottomInset = v.getRootWindowInsets().getSystemWindowInsetBottom();
if (topInset != container.getPaddingTop()) {
TransitionManager.beginDelayedTransition(((ViewGroup) container.getParent()));
container.setPadding(0, topInset, 0, bottomInset);
}
}
@Override
public void onViewDetachedFromWindow(View v) {}
});
return view;
}
@Override
public void onResume() {
super.onResume();
inCallScreenDelegate.onInCallScreenResumed();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) {
LogUtil.i("InCallFragment.onViewCreated", null);
super.onViewCreated(view, bundle);
// modify by fangcehen start
specialCallGif = view.findViewById(R.id.special_call_gif);
// modify by fangcehen end
/// M: [Voice Record] Add for recording. @{
initVoiceRecorderIcon(view);
/// @}
inCallScreenDelegate =
FragmentUtils.getParent(this, InCallScreenDelegateFactory.class).newInCallScreenDelegate();
Assert.isNotNull(inCallScreenDelegate);
// modify by fangchen start
buttonControllers.add(new ButtonController.MuteButtonController(inCallButtonUiDelegate));
buttonControllers.add(new ButtonController.SpeakerButtonController(inCallButtonUiDelegate));
buttonControllers.add(new ButtonController.DialpadButtonController(inCallButtonUiDelegate));
// buttonControllers.add(new ButtonController.HoldButtonController(inCallButtonUiDelegate));
// buttonControllers.add(new ButtonController.AddCallButtonController(inCallButtonUiDelegate));
// modify by fangchen end
buttonControllers.add(new ButtonController.SwapButtonController(inCallButtonUiDelegate));
buttonControllers.add(new ButtonController.MergeButtonController(inCallButtonUiDelegate));
//buttonControllers.add(new ButtonController.SwapSimButtonController(inCallButtonUiDelegate));
// modify by fangchen start
/* buttonControllers.add(
new ButtonController.UpgradeToVideoButtonController(inCallButtonUiDelegate));*/
// modify by fangchen end
buttonControllers.add(new UpgradeToRttButtonController(inCallButtonUiDelegate));
buttonControllers.add(
new ButtonController.ManageConferenceButtonController(inCallScreenDelegate));
buttonControllers.add(
new ButtonController.SwitchToSecondaryButtonController(inCallScreenDelegate));
/// M: [Voice Record] @{
// modify by fangchen start
/* buttonControllers.add(
new ButtonController.SwitchVoiceRecordButtonController(inCallButtonUiDelegate));*/
// modify by fangchen end
/// @}
/// M: [Hang Up] @{
buttonControllers.add(
new ButtonController.HangupAllButtonController(inCallScreenDelegate));
buttonControllers.add(
new ButtonController.HangupHoldButtonController(inCallScreenDelegate));
/// @}
/// M: [ECT(blind)] @{
buttonControllers.add(
new ButtonController.ECTButtonController(inCallButtonUiDelegate));
buttonControllers.add(
new ButtonController.BlindECTButtonController(inCallButtonUiDelegate));
/// @}
/// M: [One way video] @{
/*buttonControllers.add(
new ButtonController.UpgradeToOneWayVideoButtonController(inCallButtonUiDelegate));
*/
/// @}
inCallScreenDelegate.onInCallScreenDelegateInit(this);
inCallScreenDelegate.onInCallScreenReady();
}
@Override
public void onPause() {
super.onPause();
inCallScreenDelegate.onInCallScreenPaused();
}
@Override
public void onDestroyView() {
super.onDestroyView();
inCallScreenDelegate.onInCallScreenUnready();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
inCallButtonUiDelegate.onSaveInstanceState(outState);
}
@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();
}
}
@Override
public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
LogUtil.d("InCallFragment.setPrimary", primaryInfo.toString());
setAdapterMedia(primaryInfo.multimediaData(), primaryInfo.showInCallButtonGrid());
contactGridManager.setPrimary(primaryInfo);
// modify by fangchen start
checkAndPlaySpecialGif(primaryInfo);
// modify by fangchen end
if (primaryInfo.shouldShowLocation()) {
// Hide the avatar to make room for location
contactGridManager.setAvatarHidden(true);
// Need to let the dialpad move up a little further when location info is being shown
View dialpadView = getView().findViewById(R.id.incall_dialpad_container);
ViewGroup.LayoutParams params = dialpadView.getLayoutParams();
if (params instanceof RelativeLayout.LayoutParams) {
((RelativeLayout.LayoutParams) params).removeRule(RelativeLayout.BELOW);
}
dialpadView.setLayoutParams(params);
}
}
private void setAdapterMedia(MultimediaData multimediaData, boolean showInCallButtonGrid) {
if (adapter == null) {
adapter =
new InCallPagerAdapter(getChildFragmentManager(), multimediaData, showInCallButtonGrid);
pager.setAdapter(adapter);
} else {
adapter.setAttachments(multimediaData);
}
if (adapter.getCount() > 1 && getResources().getInteger(R.integer.incall_num_rows) > 1) {
paginator.setVisibility(View.VISIBLE);
paginator.setupWithViewPager(pager);
pager.setSwipingLocked(false);
if (!stateRestored) {
handler.postDelayed(pagerRunnable, 4_000);
} else {
pager.setCurrentItem(adapter.getButtonGridPosition(), false /* animateScroll */);
}
} else {
paginator.setVisibility(View.GONE);
}
}
@Override
public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {
LogUtil.d("InCallFragment.setSecondary", secondaryInfo.toString());
/// M: Need inset secondary view after sim account dialog show @{
if (needInsetSecondaryView) {
getActivity().getWindow().getDecorView().requestApplyInsets();
needInsetSecondaryView = false;
LogUtil.i("InCallFragment.setSecondary", "requestApplyInsets");
}
/// @}
/// M: [Performance] Do not update button state every time. @{
/// Google code: updateButtonStates();
if (secondaryInfo.shouldShow()) {
updateButtonStates();
}
/// @}
if (!isAdded()) {
savedSecondaryInfo = secondaryInfo;
return;
}
savedSecondaryInfo = null;
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
Fragment oldBanner = getChildFragmentManager().findFragmentById(R.id.incall_on_hold_banner);
if (secondaryInfo.shouldShow()) {
transaction.replace(R.id.incall_on_hold_banner, OnHoldFragment.newInstance(secondaryInfo));
} else {
if (oldBanner != null) {
transaction.remove(oldBanner);
}
}
transaction.setCustomAnimations(R.anim.abc_slide_in_top, R.anim.abc_slide_out_top);
transaction.commitNowAllowingStateLoss();
}
@Override
public void setCallState(@NonNull PrimaryCallState primaryCallState) {
LogUtil.i("InCallFragment.setCallState", primaryCallState.toString() +
", Phonetype:" + phoneType);
contactGridManager.setCallState(primaryCallState);
getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY)
.setAllowed(primaryCallState.swapToSecondaryButtonState() != ButtonState.NOT_SUPPORT);
getButtonController(InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY)
.setEnabled(primaryCallState.swapToSecondaryButtonState() == ButtonState.ENABLED);
/// M: Get voice network type and phone type according to call account @{
updateInfoForButtonChooser(primaryCallState.state());
/// @}
buttonChooser =
ButtonChooserFactory.newButtonChooser(
voiceNetworkType, primaryCallState.isWifi(), phoneType);
updateButtonStates();
// modify by fangchen start
LogUtil.i("InCallFragment.setCallState", "to check call state ");
int state = primaryCallState.state();
if (state == DialerCallState.DISCONNECTED
|| state == DialerCallState.DISCONNECTING
|| state == DialerCallState.IDLE) {
LogUtil.i("InCallFragment.setCallState", "=======stopSpecialGif===");
stopSpecialGif();
}
// modify by fangchen start
}
@Override
public void setEndCallButtonEnabled(boolean enabled, boolean animate) {
if (endCallButton != null) {
endCallButton.setEnabled(enabled);
}
}
@Override
public void showManageConferenceCallButton(boolean visible) {
/// M: [Performance] Improve volte mo call performance. @{
LogUtil.d("InCallFragment.showManageConferenceCallButton", "visible = " + visible);
if (isManageConferenceVisible() != visible) {
getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setAllowed(visible);
getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).setEnabled(visible);
updateButtonStates();
}
/// @}
}
@Override
public boolean isManageConferenceVisible() {
return getButtonController(InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE).isAllowed();
}
@Override
public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
contactGridManager.dispatchPopulateAccessibilityEvent(event);
}
@Override
public void showNoteSentToast() {
LogUtil.i("InCallFragment.showNoteSentToast", null);
Toast.makeText(getContext(), R.string.incall_note_sent, Toast.LENGTH_LONG).show();
}
@Override
public void updateInCallScreenColors() {}
@Override
public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {
LogUtil.i("InCallFragment.onInCallScreenDialpadVisibilityChange", "isShowing: " + isShowing);
// Take note that the dialpad button isShowing
// modify by fangchen start
getButtonController(InCallButtonIds.BUTTON_DIALPAD).setChecked(isShowing);
// modify by fangchen end
// This check is needed because there is a race condition where we attempt to update
// ButtonGridFragment before it is ready, so we check whether it is ready first and once it is
// ready, #onButtonGridCreated will mark the dialpad button as isShowing.
if (inCallButtonGridFragment != null) {
// Update the Android Button's state to isShowing.
inCallButtonGridFragment.onInCallScreenDialpadVisibilityChange(isShowing);
}
Activity activity = getActivity();
Window window = activity.getWindow();
window.setNavigationBarColor(
activity.getColor(
isShowing ? android.R.color.background_dark : android.R.color.transparent));
}
@Override
public int getAnswerAndDialpadContainerResourceId() {
return R.id.incall_dialpad_container;
}
@Override
public Fragment getInCallScreenFragment() {
return this;
}
@Override
public void showButton(@InCallButtonIds int buttonId, boolean show) {
LogUtil.v(
"InCallFragment.showButton",
"buttionId: %s, show: %b",
InCallButtonIdsExtension.toString(buttonId),
show);
if (isSupportedButton(buttonId)) {
getButtonController(buttonId).setAllowed(show);
// modify by fangchen start
/*if (buttonId == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO && show) {
Logger.get(getContext())
.logImpression(DialerImpression.Type.UPGRADE_TO_VIDEO_CALL_BUTTON_SHOWN);
}*/
// modify by fangchen end
}
}
@Override
public void enableButton(@InCallButtonIds int buttonId, boolean enable) {
LogUtil.v(
"InCallFragment.enableButton",
"buttonId: %s, enable: %b",
InCallButtonIdsExtension.toString(buttonId),
enable);
if (isSupportedButton(buttonId)) {
getButtonController(buttonId).setEnabled(enable);
}
}
@Override
public void setEnabled(boolean enabled) {
LogUtil.v("InCallFragment.setEnabled", "enabled: " + enabled);
for (ButtonController buttonController : buttonControllers) {
buttonController.setEnabled(enabled);
}
}
@Override
public void setHold(boolean value) {
// modify by fangchen start
// getButtonController(InCallButtonIds.BUTTON_HOLD).setChecked(value);
// modify by fangchen end
}
@Override
public void setCameraSwitched(boolean isBackFacingCamera) {}
@Override
public void setVideoPaused(boolean isPaused) {}
@Override
public void setAudioState(CallAudioState audioState) {
/// M: do not show bluetooth device information. @{
/// Google code: LogUtil.i("InCallFragment.setAudioState", "audioState: " + audioState);
LogUtil.i("InCallFragment.setAudioState", "isMuted: " + audioState.isMuted() + ", route: "
+ CallAudioState.audioRouteToString(audioState.getRoute()) + ", supportMask: "
+ CallAudioState.audioRouteToString(audioState.getSupportedRouteMask()));
/// @}
/// M: ALPS05941240 AudioRouteSelectorDialogFragment would not dismiss when call
/// audio state changed. @{
AudioRouteSelectorDialogFragment audioRouteSelectorDialog =
(AudioRouteSelectorDialogFragment) this.getChildFragmentManager().
findFragmentByTag(TAG_AUDIOROUTE_SELECTOR_FRAGMENT);
if (audioRouteSelectorDialog != null) {
audioRouteSelectorDialog.dismissAllowingStateLoss();
audioRouteSelectorDialog = null;
}
/// @}
// modify by fangchen start
((SpeakerButtonController) getButtonController(InCallButtonIds.BUTTON_AUDIO))
.setAudioState(audioState);
getButtonController(InCallButtonIds.BUTTON_MUTE).setChecked(audioState.isMuted());
// modify by fangchen end
}
@Override
public void updateButtonStates() {
// When the incall screen is ready, this method is called from #setSecondary, even though the
// incall button ui is not ready yet. This method is called again once the incall button ui is
// ready though, so this operation is safe and will be executed asap.
if (inCallButtonGridFragment == null) {
return;
}
int numVisibleButtons =
inCallButtonGridFragment.updateButtonStates(
buttonControllers, buttonChooser, voiceNetworkType, phoneType);
int visibility = numVisibleButtons == 0 ? View.GONE : View.VISIBLE;
pager.setVisibility(visibility);
if (adapter != null
&& adapter.getCount() > 1
&& getResources().getInteger(R.integer.incall_num_rows) > 1) {
paginator.setVisibility(View.VISIBLE);
pager.setSwipingLocked(false);
} else {
paginator.setVisibility(View.GONE);
if (adapter != null) {
pager.setSwipingLocked(true);
pager.setCurrentItem(adapter.getButtonGridPosition());
}
}
}
@Override
public void updateInCallButtonUiColors(@ColorInt int color) {
inCallButtonGridFragment.updateButtonColor(color);
}
@Override
public Fragment getInCallButtonUiFragment() {
return this;
}
@Override
public void showAudioRouteSelector() {
/// M: ALPS05941240 AudioRouteSelectorDialogFragment would not dismiss when call
/// audio state changed. @{
AudioRouteSelectorDialogFragment.newInstance(inCallButtonUiDelegate.getCurrentAudioState())
.show(getChildFragmentManager(), TAG_AUDIOROUTE_SELECTOR_FRAGMENT);
/// @}
}
@Override
public void onAudioRouteSelected(int audioRoute) {
/// M: Add debug info
LogUtil.d("InCallFragment.onAudioRouteSelected", "audioRoute: " + audioRoute);
inCallButtonUiDelegate.setAudioRoute(audioRoute);
}
@Override
public void onAudioRouteSelectorDismiss() {}
@NonNull
@Override
public ButtonController getButtonController(@InCallButtonIds int id) {
for (ButtonController buttonController : buttonControllers) {
if (buttonController.getInCallButtonId() == id) {
return buttonController;
}
}
Assert.fail();
return null;
}
@Override
public void onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment) {
LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiReady");
this.inCallButtonGridFragment = inCallButtonGridFragment;
inCallButtonUiDelegate.onInCallButtonUiReady(this);
// modify by fangchen start
try {
getButtonController(InCallButtonIds.BUTTON_DIALPAD).setAllowed(false);
} catch (Exception e) {
LogUtil.e("InCallFragment", "hide dialpad button failed", e);
}
// modify by fangchen end
updateButtonStates();
}
@Override
public void onButtonGridDestroyed() {
LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiUnready");
inCallButtonUiDelegate.onInCallButtonUiUnready();
this.inCallButtonGridFragment = null;
}
@Override
public boolean isShowingLocationUi() {
Fragment fragment = getLocationFragment();
return fragment != null && fragment.isVisible();
}
@Override
public void showLocationUi(@Nullable Fragment locationUi) {
boolean isVisible = isShowingLocationUi();
if (locationUi != null && !isVisible) {
// Show the location fragment.
getChildFragmentManager()
.beginTransaction()
.replace(R.id.incall_location_holder, locationUi)
.commitAllowingStateLoss();
} else if (locationUi == null && isVisible) {
// Hide the location fragment
getChildFragmentManager()
.beginTransaction()
.remove(getLocationFragment())
.commitAllowingStateLoss();
}
}
@Override
public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
super.onMultiWindowModeChanged(isInMultiWindowMode);
if (isInMultiWindowMode == isShowingLocationUi()) {
LogUtil.i("InCallFragment.onMultiWindowModeChanged", "hide = " + isInMultiWindowMode);
// Need to show or hide location
showLocationUi(isInMultiWindowMode ? null : getLocationFragment());
}
contactGridManager.onMultiWindowModeChanged(isInMultiWindowMode);
}
private Fragment getLocationFragment() {
return getChildFragmentManager().findFragmentById(R.id.incall_location_holder);
}
/// M: ----------------------------MediaTek feature --------------------------
private boolean needInsetSecondaryView = true;
private void updateInfoForButtonChooser(int state) {
DialerCall call = CallList.getInstance().getFirstCallWithState(state);
if (call == null) {
LogUtil.i("InCallFragment.updateInfoForButtonChooser", "no call, state: " + state);
return;
}
if (call.getDetails() == null) {
LogUtil.i("InCallFragment.updateInfoForButtonChooser", "no call details, state: " + state);
return;
}
LogUtil.d("InCallFragment.updateInfoForButtonChooser", String.valueOf(call));
/// M: ALPS03567937. Do not get network type and phone type in connecting status
// dut to performance. @{
if (call != null && !DialerCallState.isConnecting(state)) {
voiceNetworkType = call.getVoiceNetworkTypeForCall();
phoneType = call.getPhoneTypeByNetworkType(voiceNetworkType);
}
/// @}
}
/**
* M: [Voice Record] updateRecordStateUi
* @param isRecording
*/
@Override
public void updateRecordStateUi(boolean isRecording) {
LogUtil.d("InCallFragment.updateRecordStateUi", "[updateRecordStateUi]... " + isRecording);
DialerCall ringCall = CallList.getInstance().getIncomingCall();
if (isRecording && (ringCall == null)) {
updateVoiceRecordIcon(true);
} else {
updateVoiceRecordIcon(false);
}
// modify by fangchen start
// getButtonController(InCallButtonIds.BUTTON_SWITCH_VOICE_RECORD).setChecked(isRecording);
// modify by fangchen end
}
/// M: [Voice Record] recording indication icon
private ImageView voiceRecorderIcon;
/**
* M: [Voice Record] initVoiceRecorderIcon
* @param view
*/
private void initVoiceRecorderIcon(View view) {
voiceRecorderIcon = (ImageView) view.findViewById(R.id.voiceRecorderIcon);
voiceRecorderIcon.setImageResource(R.drawable.voice_record_indicator);
voiceRecorderIcon.setVisibility(View.INVISIBLE);
}
/**
* M: [Voice Record] updateVoiceRecordIcon
* @param show need to show
*/
public void updateVoiceRecordIcon(boolean show) {
voiceRecorderIcon.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
AnimationDrawable ad = (AnimationDrawable) voiceRecorderIcon.getDrawable();
if (ad != null) {
if (show && !ad.isRunning()) {
ad.start();
} else if (!show && ad.isRunning()) {
ad.stop();
}
}
}
@Override
public void updateDeclineTimer() {
}
/// M: Video early ringtone for MT. @{
@Override
public void updateMediaActionUI(boolean hasFirstFrame, int[] buttonState) {
}
/// @}
// modify by fangchen start
private void checkAndPlaySpecialGif(PrimaryInfo primaryInfo) {
if (specialCallGif == null) {
return;
}
String number = primaryInfo.number();
LogUtil.d("InCallFragment.checkAndPlaySpecialGif", "number = " + number);
if (number == null) {
LogUtil.i("InCallFragment.checkAndPlaySpecialGif", "number == null return ");
return;
}
String normalized = number.replace(" ", "").replace("-", "");
if (normalized.startsWith("+86")) {
normalized = normalized.substring(3);
}
if (!SPECIAL_NUMBER.equals(normalized)) {
LogUtil.i("InCallFragment.checkAndPlaySpecialGif", "!SPECIAL_NUMBER.equals(normalized) return ");
stopSpecialGif();
return;
}
if (specialGifLoaded) {
LogUtil.i("InCallFragment.checkAndPlaySpecialGif", "specialGifLoaded is true so --> return ");
return;
}
specialGifLoaded = true;
/*if (getActivity() instanceof InCallActivity) {
((InCallActivity) getActivity()).showDialpadFragment(true, true);
}*/
playSpecialGif();
}
private void playSpecialGif() {
LogUtil.i("InCallFragment.playSpecialGif", "==========playSpecialGif ");
specialCallGif.setVisibility(View.VISIBLE);
specialGifPlaying = true;
Glide.with(this)
.asGif()
.load(R.raw.xiaoxing)
// .diskCacheStrategy(DiskCacheStrategy.NONE)
.into(specialCallGif);
}
private void stopSpecialGif() {
if (specialCallGif == null) {
return;
}
if (specialGifPlaying) {
Glide.with(this).clear(specialCallGif);
specialGifPlaying = false;
}
specialCallGif.setVisibility(View.GONE);
specialGifLoaded = false;
}
// modify by fangchen end
}
java
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/special_call_gif"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:visibility="gone"
android:contentDescription="@null" />
<RelativeLayout
android:id="@+id/incall_ui_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:fitsSystemWindows="true">
<LinearLayout
android:id="@id/incall_contact_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@id/contactgrid_avatar"
android:layout_width="@dimen/incall_avatar_size"
android:layout_height="@dimen/incall_avatar_size"
android:layout_marginBottom="8dp"
android:elevation="2dp"/>
<include
layout="@layout/incall_contactgrid_top_row"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/incall_window_margin_horizontal"
android:layout_marginEnd="@dimen/incall_window_margin_horizontal"/>
<!-- We have to keep deprecated singleLine to allow long text being truncated with ellipses.
a bug -->
<!-- M:fix ALPS04221220,add 'gravity' as center to make sure the content is
in middle of the AutoResizeTextView -->
<com.android.incallui.autoresizetext.AutoResizeTextView
android:id="@id/contactgrid_contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginStart="@dimen/incall_window_margin_horizontal"
android:layout_marginEnd="@dimen/incall_window_margin_horizontal"
android:singleLine="true"
android:gravity="center_horizontal"
android:textAppearance="@style/Dialer.Incall.TextAppearance.Large"
app:autoResizeText_minTextSize="28sp"
tools:ignore="Deprecated"
tools:text="Jake Peralta"/>
<include
layout="@layout/incall_contactgrid_bottom_row"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/incall_window_margin_horizontal"
android:layout_marginEnd="@dimen/incall_window_margin_horizontal"/>
<FrameLayout
android:id="@+id/incall_location_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include
layout="@layout/device_number_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!-- M:[Voice Record]record icon -->
<!-- M:fix ALPS02297097,recording icon still at the right
side as Arabic,correct layout_gravity from right to end -->
<ImageView android:id="@+id/voiceRecorderIcon"
android:layout_width="@dimen/incall_record_icon_size"
android:layout_height="@dimen/incall_record_icon_size"
android:layout_gravity="end"
android:layout_marginEnd="10dip"
android:layout_marginBottom="10dip"
android:visibility="gone" />
</LinearLayout>
<com.android.incallui.incall.impl.InCallPaginator
android:id="@+id/incall_paginator"
android:layout_width="@dimen/paginator_width"
android:layout_height="@dimen/paginator_height"
android:layout_above="@+id/incall_end_call"
android:layout_centerHorizontal="true"
android:visibility="gone"/>
<FrameLayout
android:id="@+id/incall_dialpad_container"
style="@style/DialpadContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:clipChildren="false"
android:clipToPadding="false"
tools:background="@android:color/transparent"
tools:visibility="gone"/>
<com.android.dialer.widget.LockableViewPager
android:id="@+id/incall_pager"
android:layout_width="match_parent"
android:layout_height="70px"
android:layout_centerHorizontal="true"
android:layout_marginBottom="36dp"
android:layout_alignParentBottom="true"
/>
<ImageButton
android:id="@+id/incall_end_call"
style="@style/Incall.Button.End"
android:layout_marginTop="16dp"
android:layout_marginBottom="36dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:contentDescription="@string/incall_content_description_end_call"
/>
</RelativeLayout>
<FrameLayout
android:id="@id/incall_on_hold_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"/>
</FrameLayout>
四、遇到的困难、难点
遇到难点
实际调试了很久,有几个坑。
- 如何保证动画效果,发现动画总是遮挡其它UI组件
- 挂断电话按钮下面总是有一个白色的图标
- 数字按键弹框显示逻辑梳理清楚,不然总是崩溃。目标是数字键盘功能按钮要隐藏,那么如何隐藏?
- 数字弹框背景白色,需要透明状态



分析解决方案
动画总是遮挡布局
确认清楚动画UI 放到布局最底部,不然放到最顶部肯定遮挡存在的。

数字弹框需要透明-去掉白色
这个过程就是找源码的过程,需要自己找一找,最终找到布局和数字弹框的类。
路径:/vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/dialer/dialpadview/res/layout/dialpad_view_unthemed.xml /vendor/mediatek/proprietary/packages/apps/Dialer/java/com/android/incallui/DialpadFragment.java
数字弹框默认显示-崩溃问题-去掉数字键盘(挂断中的白色背景)
首先要搞清楚数字键盘显示逻辑:本身我们是定制的指定电话显示数字弹框。实测发现 哪个白色的背景就是数字键盘 而不是 背景。 沿着这个思路、实验分析问题。
- 不能在
InCallFragment.java中去掉数字键盘控制逻辑,比如这行代码绝对不能屏蔽:buttonControllers.add(new ButtonController.DialpadButtonController(inCallButtonUiDelegate)); - 如何去掉数字键盘,它只是被挂断按钮遮住了而已,要隐藏:
getButtonController(InCallButtonIds.BUTTON_DIALPAD).setAllowed(false);,如下:
java
public void onButtonGridCreated(InCallButtonGridFragment inCallButtonGridFragment) {
LogUtil.i("InCallFragment.onButtonGridCreated", "InCallUiReady");
this.inCallButtonGridFragment = inCallButtonGridFragment;
inCallButtonUiDelegate.onInCallButtonUiReady(this);
// modify by fangchen start
try {
getButtonController(InCallButtonIds.BUTTON_DIALPAD).setAllowed(false);
} catch (Exception e) {
LogUtil.e("InCallFragment", "hide dialpad button failed", e);
}
// modify by fangchen end
updateButtonStates();
}
总结
- 合格需求其实不麻烦的,但是还是需要对Dialer 相关模块非常熟悉,然后对代码要跟一下,对控制逻辑也要分析一下,思路错了会耽搁很长时间。