zoukankan      html  css  js  c++  java
  • Android MTK平台 客制化系统来电界面(屏蔽 InCallUI 提供接口给客户自行展示来电去电页面)

    OS: Android 8.1

    需求分析

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

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

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

    上代码

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

    vendormediatekproprietarypackagesservicesTelecommsrccomandroidserver elecomRinger.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 的管理中心,来电去电都经过这处理,所以我们在此处修改比较容易

    vendormediatekproprietarypackagesappsDialerjavacomandroidincalluiInCallPresenter.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);

    vendormediatekproprietarypackagesappsDialerjavacomandroidincalluiInCallPresenter.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 参数,经过验证确实来电不带此参数,去电带参数

    vendormediatekproprietarypackagesappsDialerjavacomandroidincalluiInCallPresenter.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 显示

    vendormediatekproprietarypackagesappsDialerjavacomandroidincalluiStatusBarNotifier.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 为客户

    vendormediatekproprietarypackagesservicesTelecommsrccomandroidserver elecomuiMissedCallNotifierImpl.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、按下音量键和电源键静音

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

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

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

    vendormediatekproprietarypackagesservicesTelecommsrccomandroidserver elecomTelecomServiceImpl.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();
              }
          }
    
  • 相关阅读:
    codeAnalyze_函数赋值给另一个函数的形参
    js_new关键字创建对象的五个步骤
    codeRecord_bind
    js_活动对象与变量对象的区别
    将linux的随机ip固定为设置的固定ip
    Springcloud总结
    Jackson的使用
    Lucene的初步了解和学习
    Shiro安全框架
    关于xpath中的tbody
  • 原文地址:https://www.cnblogs.com/cczheng-666/p/13424485.html
Copyright © 2011-2022 走看看