OS: Android 8.1

需求分析

1、禁止系统来电铃声,提供接口给客户自己播放铃声

2、禁止系统拉起来去电页面(InCallActivity),消息通知客户拉起自己的来去电页面

3、禁止来电消息 Notification 显示(包括未接来电),点击跳转至 InCallActivity(未接来电消息可通知客户或者将 PendingIntent 改成客户的)

上代码

1、系统来电铃声播放在 Telecomm 应用中,我们发现一般都是先听到铃声才看到 UI 被拉起,那是因为铃声在 Telecomm 中,UI 需要通过 InCallService 通知到 Dialer 中。

vendor\mediatek\proprietary\packages\services\Telecomm\src\com\android\server\telecom\Ringer.java

	public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {

		//cczhegn add return for customer self control ringing, system ignore
if (true) {
return ;
} if (foregroundCall == null) {
/// M: ALPS03787956 Fix wtf log warning. @{
/// Hand up the call immediately when ringing. Then the foreground call will
/// change to null, but call audio is start ringing at the same time.
/// Log.wtf will occur in this case.
/// Solution:
/// Call audio can handle this case, so change Log.wtf to Log.i here.
Log.i(this, "startRinging called with null foreground call.");
/// @}
return false;
} AudioManager audioManager =
(AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
boolean isSelfManaged = foregroundCall.isSelfManaged(); ....
} public void stopRinging() {
//cczheng add return for customer self control ringing, system ignore
if (true) {
return ;
} if (mRingingCall != null) {
Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
mRingingCall = null;
} if (mIsVibrating) {
Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
mVibrator.cancel();
mIsVibrating = false;
mVibratingCall = null;
}
}

只需要将 startRinging() 和 stopRinging() 直接 return 即可,系统就不会响铃了。

2、提供播放铃声工具类 RingUtil 给客户

public class RingUtil {
private static final String TAG = "RingUtil";
private static Ringtone ringtone;
private static Ringtone getRing(Context context){
if (null == ringtone){
Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE);
ringtone = RingtoneManager.getRingtone(context, defaultRingtoneUri);
}
return ringtone;
}
public static void startRing(Context context){
getRing(context);
if (!ringtone.isPlaying()){
ringtone.play();
}
}
public static void stopRing(Context context){
getRing(context);
if (ringtone.isPlaying()){
ringtone.stop();
}
}
}

3、禁止系统拉起来去电页面

刚刚上面说到 Telecom 和 Dialer 主要通过 InCallService 通信,重要的两个方法 onCallAdded、onCallRemoved,从字面意思很容易理解,当 call 加入和移除时回调。

InCallPresenter 可以说是 Call 的管理中心,来电去电都经过这处理,所以我们在此处修改比较容易

vendor\mediatek\proprietary\packages\apps\Dialer\java\com\android\incallui\InCallPresenter.java

//来电拉起 InCallActivity 的地方
public void showInCall(boolean showDialpad, boolean newOutgoingCall) {
LogUtil.d("InCallPresenter.showInCall", "Showing InCallActivity");
//cczheng annotaion don't show sysytem InCallActivity
//mContext.startActivity(
//InCallActivity.getIntent(
//mContext, showDialpad, newOutgoingCall, false /* forFullScreen */));
} //去电拉起 InCallActivity 的地方
public void maybeStartRevealAnimation(Intent intent) {
LogUtil.e("InCallPresenter.OutgoingCall", "maybeStartRevealAnimation");
if (intent == null || mInCallActivity != null) {
return;
}
final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
if (extras == null) {
// Incoming call, just show the in-call UI directly.
return;
} if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
// Account selection dialog will show up so don't show the animation.
return;
} final PhoneAccountHandle accountHandle =
intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT); InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle); //cczheng annotaion don't show sysytem InCallActivity
LogUtil.e("InCallPresenter.OutgoingCall", "OutgoingCall is preper start inCallActivity.");
//final Intent activityIntent =
//InCallActivity.getIntent(mContext, false, true, false /* forFullScreen */);
//activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
//mContext.startActivity(activityIntent);
}

4、消息通知客户来去电消息

为了简单我们采用广播的方式,由于 8.1 中静态注册广播有限制,为了突破限制,我们需要在发送广播时加上 intent.addFlags(0x01000000);

vendor\mediatek\proprietary\packages\apps\Dialer\java\com\android\incallui\InCallPresenter.java

 String number;
public void onCallAdded(final android.telecom.Call call) {
LatencyReport latencyReport = new LatencyReport(call);
if (shouldAttemptBlocking(call)) {
maybeBlockCall(call, latencyReport);
} else {
if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
mExternalCallList.onCallAdded(call);
} else {
latencyReport.onCallBlockingDone();
mCallList.onCallAdded(mContext, call, latencyReport);
}
} // Since a call has been added we are no longer waiting for Telecom to send us a call.
setBoundAndWaitingForOutgoingCall(false, null);
call.registerCallback(mCallCallback); //cczheng add when incall or outcall added notify user can start there incallui
number = TelecomCallUtil.getNumber(call);
if (isOutGoingCall) {
LogUtil.e("InCallPresenter.OutgoingCall", "OutgoingCall number=="+number);
Intent intent = new Intent("com.android.outgoingcall.wireless");
intent.putExtra("outgoingNumber", number);
intent.addFlags(0x01000000);
mContext.sendBroadcast(intent);
}else{
LogUtil.i("InCallPresenter.IncomingCall", "IncomingCall number=="+number);
Intent intentcc = new Intent("com.android.incomingcall.wireless");
intentcc.putExtra("incomingNumber", number);
intentcc.addFlags(0x01000000);
mContext.sendBroadcast(intentcc);
}
}

在 onCallAdded() 方法中,可以看到我们添加了 isOutGoingCall 变量判读是来电还是去电,分别对应不用的 action,通过 TelecomCallUtil 获取当前 call 的 number 顺带发送

5、来去电类型 isOutGoingCall 区分

回到刚刚的屏蔽去电拉起页面的方法 maybeStartRevealAnimation() 中,其中有获取 EXTRA_OUTGOING_CALL_EXTRAS 参数,经过验证确实来电不带此参数,去电带参数

vendor\mediatek\proprietary\packages\apps\Dialer\java\com\android\incallui\InCallPresenter.java

//cczheng add for distinguish incall or outgogingcall
private boolean isOutGoingCall; public void maybeStartRevealAnimation(Intent intent) {
LogUtil.e("InCallPresenter.OutgoingCall", "maybeStartRevealAnimation");
if (intent == null || mInCallActivity != null) {
return;
}
final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
if (extras == null) {
// Incoming call, just show the in-call UI directly.
isOutGoingCall = false;//cczheng add for incomingcall
return;
} if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
// Account selection dialog will show up so don't show the animation.
return;
} final PhoneAccountHandle accountHandle =
intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT); InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle); LogUtil.e("InCallPresenter.OutgoingCall", "OutgoingCall is preper start inCallActivity.");
isOutGoingCall = true;//cczhegn add for outgongcall
//final Intent activityIntent =
//InCallActivity.getIntent(mContext, false, true, false /* forFullScreen */);
//activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
//mContext.startActivity(activityIntent);
}

6、禁止来电消息 Notification 显示

vendor\mediatek\proprietary\packages\apps\Dialer\java\com\android\incallui\StatusBarNotifier.java

@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public void updateNotification(CallList callList) {
///cczheng annotaion for don't show incallnotification when pstnoverlayactivity in font
//updateInCallNotification(callList);
}

直接注释 updateInCallNotification()

7、未接来电消息处理

回到 Telecomm 中,MissedCallNotifierImpl 负责管理未接来电消息,如果将 android 自己的 Notification 屏蔽,通知客户自己去显示,略微麻烦,用户需要自己建数据库保存通知已读未读状态,

因为重启需要查询判断是否需要再次显示。这样你可以在 showMissedCallNotification() 中直接发送通知后 return。我们此处采用第二种方式,修改系统 Notification 的 PendingIntent 为客户

vendor\mediatek\proprietary\packages\services\Telecomm\src\com\android\server\telecom\ui\MissedCallNotifierImpl.java

private PendingIntent createCallLogPendingIntent(UserHandle userHandle) {
//cczheng add for jump to customer app
try {
Intent intentc = mContext.getPackageManager().getLaunchIntentForPackage("your customer app packageName");
return PendingIntent.getActivity(mContext, 11, intentc, 0);
}catch (Exception e){
e.printStackTrace();
Intent intent = new Intent(Intent.ACTION_VIEW, null);
intent.setType(Calls.CONTENT_TYPE); TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(mContext);
taskStackBuilder.addNextIntent(intent); return taskStackBuilder.getPendingIntent(0, 0, null, userHandle);
}
}

8、按下音量键和电源键静音

系统播放来电铃声时,此时按下音量键或电源键,默认都会停止播放铃声。但这是系统播放铃声的情况下,现在我们已经开发给

客户自己去播放铃声了,所以原来的逻辑就会失效,只能客户自己去停止铃声,但是来电页面响铃中,客户是监听不到音量键或

电源键事件的,只能在系统通知了。

vendor\mediatek\proprietary\packages\services\Telecomm\src\com\android\server\telecom\TelecomServiceImpl.java

@Override
public void silenceRinger(String callingPackage) {
try {
Log.startSession("TSI.sR");
synchronized (mLock) {
enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage); long token = Binder.clearCallingIdentity();
try {
Log.i(this, "Silence Ringer requested by %s", callingPackage);
//cczheng add for notify custmer press valume/power need silence ringer
mContext.sendBroadcast(new Intent("com.android.key.silence.ringer"));
mCallsManager.getCallAudioManager().silenceRingers();
mCallsManager.getInCallController().silenceRinger();
} finally {
Binder.restoreCallingIdentity(token);
}
}
} finally {
Log.endSession();
}
}

Android MTK平台 客制化系统来电界面(屏蔽 InCallUI 提供接口给客户自行展示来电去电页面)的更多相关文章

  1. MTK Android [输入法]客制化系统默认输入法-搜狗输入法

    1.frameworks/base/packages/SettingsProvider/res/values/defaults.xml <!--Sogou input method is use ...

  2. Android MTK6580 客制化关机充电动画

    1.客制化关机充电图片 vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo/xxx 找到对应分辨率替换 2.调整显示图片位置.大小 ...

  3. Dynamics AX 2012 R2 客制化RDP报表参数对话框

    当我们在使用RDP报表时,AX会根据Data Contract,自动生成报表参数对话框上的字段控件.一般情况下,该对话框能够满足我们的需求,但是如果有较为复杂或特殊的需求,就要我们对该对话框进行客制化 ...

  4. BEvent_客制化BusinessEvent通过PLSQL Procedurer接受消息传递(案例)

    2014-06-27 Created By BaoXinjian

  5. Form_通过Zoom客制化跳转页面功能(案例)

    2012-09-08 Created By BaoXinjian

  6. 转:FORM:客制化Form的菜单栏和右鍵菜單

    Oracle EBS还允许客制化Form的菜单栏. 用户最多可以定义45个form-level的trigger,名称必须为SPECIALn, 其中SPECIAL1 to SPECIAL15属于Tool ...

  7. Android RRO机制的运用-----google开机向导客制化

    上周五的时候领导分了一个任务,客户让在google开机向导里面增加一页,首先就想到了android的Overlay,然后网上搜了下,发下有很多人写了这方面的技术.而且写的都还不错,所以本篇只当记录作用 ...

  8. Android MTK平台最完备的开机动画修改教程

    修改手机的开机动画不是什么难事儿. 但修改一款很冷门的"山寨机",就不太好修改第一屏了. 手机是MTK的一款手机,虽然比较贵(价格超过三星Note3),但在我看来跟山寨机木有啥区别 ...

  9. Report_客制化以PLSQL输出XLS标记实现Excel报表(案例)

    2015-02-12 Created By BaoXinjian

随机推荐

  1. Windows Socket编程精华《TCP通信服务器》

    1.网络中进程之间如何通信? 首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的.其实TCP/IP协议族已经帮我们解决了这个问 ...

  2. python 并发专题(一):并发基础相关概念,术语等

    一.线程 1.概念 线程是程序执行流的最小执行单位,是行程中的实际运作单位. 进程是一个动态的过程,是一个活动的实体.简单来说,一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执 ...

  3. Python之堡垒机

    本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功 ...

  4. bzoj3043IncDec Sequence*

    bzoj3043IncDec Sequence 题意: n个数,每次可以将区间l到r里的数+1或-1,问将它们变成同个数的最小操作次数和保证最小操作次数前提下有多少中可能.n≤100000. 题解: ...

  5. 十分钟快速搭建Python+Selenium自动化测试环境(含视频教程)

    文章首发于微信公众号:爱码小哥 准备安装包: 一:安装python:   双击python-3.7.6.exe执行文件 2.点击下一步正在安装: 3.如图所示表示安装完成: 校验环境是否安装成功:   ...

  6. day10:函数的实参&形参

    函数的参数 (参数:配合函数运算的值)参数:      (1)形参:形式参数,在函数的定义处      (2)实参:实际参数,在函数的调用处 形参: 普通形参(位置) , 默认形参 , 普通收集形参 ...

  7. vue邪道玩法 : 把vue实例存在别的地方,以及可能会遇到的问题

    一般来说,VUE项目中,this指向VUE实例. 但有的时候,某些代码会改变this的指向. 这时,可以用一个临时变量存储VUE实例. test1(){ var _this = this // 把vu ...

  8. css :clip rect 正确的使用方法

    CSS clip 是一个极少使用,但又确实存在的属性. 而且,它其实在CSS2时代就有了. w3school 上有这么一句话: clip 属性剪裁绝对定位元素. 也就是说,只有 position:ab ...

  9. Windows搭建Redis集群-详细教程

    一.集群知识 1.集群的概念 所谓的集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定.高效的状态. 2.使用redis集群的必要性 问题:我们已经部署好了redis,并且能启动 ...

  10. 拿不到offer?阿里HR教你简历应该这样写

    首先,要明白公司看重的是你的什么?为什么要聘请你?当然是看重你的能力,聘请你来是要来干活的. 都知道简历是敲门砖,那么这一块砖要怎么才能敲得响,怎么才能敲得开大厂的门. 1 能力萃取!什么是能力萃取? ...