Android MTK平臺 客制化系統(tǒng)來電界面(屏蔽 InCallUI 提供接口給客戶自行展示來電去電頁面)

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();
          }
      }
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末恩掷,一起剝皮案震驚了整個濱河市倡鲸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌黄娘,老刑警劉巖峭状,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異逼争,居然都是意外死亡优床,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門氮凝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羔巢,“玉大人,你說我怎么就攤上這事罩阵「透眩” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵稿壁,是天一觀的道長幽钢。 經(jīng)常有香客問我,道長傅是,這世上最難降的妖魔是什么匪燕? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮喧笔,結果婚禮上帽驯,老公的妹妹穿的比我還像新娘。我一直安慰自己书闸,他們只是感情好尼变,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浆劲,像睡著了一般嫌术。 火紅的嫁衣襯著肌膚如雪哀澈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天度气,我揣著相機與錄音割按,去河邊找鬼。 笑死磷籍,一個胖子當著我的面吹牛适荣,可吹牛的內容都是我干的。 我是一名探鬼主播择示,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼束凑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了栅盲?” 一聲冷哼從身側響起汪诉,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谈秫,沒想到半個月后扒寄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體完沪,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡然走,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了认境。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硕淑。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡课竣,死狀恐怖,靈堂內的尸體忽然破棺而出置媳,到底是詐尸還是另有隱情于樟,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布拇囊,位于F島的核電站迂曲,受9級特大地震影響,放射性物質發(fā)生泄漏寥袭。R本人自食惡果不足惜路捧,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望传黄。 院中可真熱鬧杰扫,春花似錦、人聲如沸膘掰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炭序。三九已至啤覆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惭聂,已是汗流浹背窗声。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辜纲,地道東北人笨觅。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像耕腾,于是被迫代替她去往敵國和親见剩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355