OS: Android 8.1
需求分析
1袱蚓、禁止系統(tǒng)來電鈴聲,提供接口給客戶自己播放鈴聲
2几蜻、禁止系統(tǒng)拉起來去電頁面(InCallActivity),消息通知客戶拉起自己的來去電頁面
3喇潘、禁止來電消息 Notification 顯示(包括未接來電)体斩,點擊跳轉至 InCallActivity(未接來電消息可通知客戶或者將 PendingIntent 改成客戶的)
上代碼
1、系統(tǒng)來電鈴聲播放在 Telecomm 應用中响蓉,我們發(fā)現(xiàn)一般都是先聽到鈴聲才看到 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 即可源武,系統(tǒng)就不會響鈴了。
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粱栖、禁止系統(tǒng)拉起來去電頁面
剛剛上面說到 Telecom 和 Dialer 主要通過 InCallService 通信,重要的兩個方法 onCallAdded脏毯、onCallRemoved闹究,從字面意思很容易理解,當 call 加入和移除時回調食店。
InCallPresenter 可以說是 Call 的管理中心渣淤,來電去電都經(jīng)過這處理,所以我們在此處修改比較容易
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 中靜態(tài)注冊廣播有限制,為了突破限制自娩,我們需要在發(fā)送廣播時加上 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 順帶發(fā)送
5脐彩、來去電類型 isOutGoingCall 區(qū)分
回到剛剛的屏蔽去電拉起頁面的方法 maybeStartRevealAnimation() 中,其中有獲取 EXTRA_OUTGOING_CALL_EXTRAS 參數(shù)姊扔,經(jīng)過驗證確實來電不帶此參數(shù)惠奸,去電帶參數(shù)
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 屏蔽删豺,通知客戶自己去顯示,略微麻煩愧怜,用戶需要自己建數(shù)據(jù)庫保存通知已讀未讀狀態(tài)呀页,
因為重啟需要查詢判斷是否需要再次顯示。這樣你可以在 showMissedCallNotification() 中直接發(fā)送通知后 return拥坛。我們此處采用第二種方式蓬蝶,修改系統(tǒng) 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尘分、按下音量鍵和電源鍵靜音
系統(tǒng)播放來電鈴聲時,此時按下音量鍵或電源鍵丸氛,默認都會停止播放鈴聲培愁。但這是系統(tǒng)播放鈴聲的情況下,現(xiàn)在我們已經(jīng)開發(fā)給
客戶自己去播放鈴聲了缓窜,所以原來的邏輯就會失效定续,只能客戶自己去停止鈴聲,但是來電頁面響鈴中禾锤,客戶是監(jiān)聽不到音量鍵或
電源鍵事件的私股,只能在系統(tǒng)通知了。
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();
}
}