android12 CRM电话助手优化

在上一个版本中, 详细介绍了如何利用 广播与接收、Worker、Service 等实现电话的触发与状态变化的监听.  在这个基础之上, 实际使用中发现很不少可以优化的部分. 下面把比较重要的展示一下: 

1. 首先是获取号码:

在高版本中, 获取号码需要传入卡槽id,  如果使用DEFAULT_SUBSCRIPTION_ID 默认ID的话, 会有一定的问题, 即当卡拔掉后, 获取的号码还是之前插卡对应的号码.  不能获取实时数据. 这在用户换卡时候容易造成歧义. 因此需要真实的当前卡槽1的ID, 这样每次请求号码就都是真实的. 完整代码: 

    @SuppressLint("MissingPermission")
    public static String getLine1Num(Context context, String TAG){
        String line1Num;

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            line1Num = telephonyManager.getLine1Number();
        }else{
            SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
            int[] ids = subscriptionManager.getSubscriptionIds(0);
            int slot0_subId = (ids == null || ids.length == 0) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : ids[0];
            Log.d(TAG, "slot0 id: "+ slot0_subId);
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                line1Num = subscriptionManager.getPhoneNumber(slot0_subId);
            }else{
                SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(0);
                line1Num = info == null ? "" : info.getNumber();
            }
        }
        Log.i(TAG, "line1Number:"+line1Num);
        return line1Num == null ? "" : line1Num;
    }

2. 如何获取通话记录及录音:

之前的逻辑是在电话监听到挂断的时候, 延时调用getContentResolver查询最新一通录音. 这个逻辑能用,但不是最好的, 后来发现ContentResolver是可以注册回调的(Observer),  这就方便多了, 在服务启动的时候就可以注册上, 然后一旦录音生成了, 就会自动触发回调(Observer). 然后在后续的研究中,发现除了通话记录, 录音也可以通过这样的方式来获取到(但很多手机这里录音后可能没有写ContentResolver数据,导致无法如此查询, 目前发现可以完美适配的有OPPO的Color OS 12+).  详细代码展示:

    private void monitorCallLogChange(Context context) {
        // 获取ContentResolver实例
        ContentResolver contentResolver = context.getContentResolver();
        // 注册一个观察者来监听通话记录的变化
        contentResolver.registerContentObserver(CallLog.Calls.CONTENT_URI, false, new ContentObserver(null) {
            @Override
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
                Cursor cursor = null;
                try {
                    // 查询最新的通话记录
                    cursor = contentResolver.query(CallLog.Calls.CONTENT_URI, null, null, null, CallLog.Calls.DATE + " DESC");
                    int numberIndex = cursor.getColumnIndex(CallLog.Calls.NUMBER);
                    int typeIndex = cursor.getColumnIndex(CallLog.Calls.TYPE);
                    int dateIndex = cursor.getColumnIndex(CallLog.Calls.DATE);
                    int durationIndex = cursor.getColumnIndex(CallLog.Calls.DURATION);
                    int idindex = cursor.getColumnIndex(CallLog.Calls._ID);
                    int lastmodifiedIndex = cursor.getColumnIndex(CallLog.Calls.LAST_MODIFIED);
                    if (cursor != null && cursor.moveToFirst()) {
                        /*
                        int i = cursor.getColumnCount();
                        for (int j = 0; j < i; j++) {
                            int type = cursor.getType(j);
                            if(type < 4)
                                Log.i(TAG, "CallLog: " + cursor.getColumnName(j)+":" + cursor.getString(j));
                            else
                                Log.i(TAG, "CallLog: " + cursor.getColumnName(j)+":" + cursor.getBlob(j).length);
                        }
                         */
                        String phoneNumber = cursor.getString(numberIndex);
                        String callDate = cursor.getString(dateIndex);
                        int callType = cursor.getInt(typeIndex);
                        long callDuration = cursor.getInt(durationIndex);
                        int id = cursor.getInt(idindex);
                        long lastmodified = cursor.getLong(lastmodifiedIndex);
                        String uuid = sharedPreferences.getString("call_uuid", "");
                        Log.d(TAG, "uuid:"+uuid+"| modified:"+lastmodified/1000+"| phoneNumber:"+phoneNumber+" |callType:"+callType+" |callDate:"+callDate+" |callDuration:"+callDuration);
                        Data.Builder dataBuilder = new Data.Builder();
                        dataBuilder.putString("action",CRMWorker.localBroadcastActionForCallLog);
                        dataBuilder.putString("call_uuid",uuid);
                        dataBuilder.putString("callType",String.valueOf(callType));
                        dataBuilder.putString("date_modified",String.valueOf(lastmodified/1000));
                        dataBuilder.putString("callDuration",String.valueOf(callDuration));
                        dataBuilder.putString("callNumber",phoneNumber);
                        CRMWorker.Sync2CRM(context,dataBuilder.build());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (cursor != null) {
                        cursor.close();
                    }
                }
            }
        });

        contentResolver.registerContentObserver(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, new ContentObserver(null) {
            @Override
            public void onChange(boolean selfChange,@Nullable Uri uri, int flags) {
                super.onChange(selfChange,uri,flags);
                Log.i(TAG, "MediaStore [External] Audio onChange: " + uri +"| flags:"+flags);
                String media_id = uri.getLastPathSegment();
                Log.i(TAG, "ID: " + media_id);
                if(flags == ContentResolver.NOTIFY_UPDATE || flags == 0) { //finish writing files
                    String selection = MediaStore.Audio.Media._ID + " = ?"
                           // +" and " + MediaStore.Audio.Media.IS_RECORDING + "= ?"
                            ;
                    String[] selectionArgs = new String[]{
                            String.valueOf(media_id),
                            //String.valueOf(1)
                    };
                    Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, selection, selectionArgs, null);
                    if (cursor != null) {
                        int filePath_index = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
                        int modified_index = cursor.getColumnIndex(MediaStore.Audio.Media.DATE_MODIFIED);
                        if (cursor.moveToFirst()) {
                            /*
                            int i = cursor.getColumnCount();
                            for (int j = 0; j < i; j++) {
                                int type = cursor.getType(j);
                                if(type < 4)
                                    Log.i(TAG, "Recording: " + cursor.getColumnName(j)+":" + cursor.getString(j));
                                else
                                    Log.i(TAG, "Recording: " + cursor.getColumnName(j)+":" + cursor.getBlob(j).length);
                            }
                            */

                            String filePath = cursor.getString(filePath_index);
                            long modified = cursor.getLong(modified_index);
                            String uuid = sharedPreferences.getString("call_uuid", "");

                            Log.d("Recordings", "call_uuid:"+uuid+", modified: " + modified + ", Path: " + filePath);
                            Data.Builder dataBuilder = new Data.Builder();
                            dataBuilder.putString("action",CRMWorker.localBroadcastActionForCallRecording);
                            dataBuilder.putString("call_uuid",uuid);
                            dataBuilder.putString("date_modified",String.valueOf(modified));
                            dataBuilder.putString("filePath",filePath);
                            CRMWorker.Sync2CRM(context,dataBuilder.build());

                        }
                        cursor.close();
                    }else{
                        Log.d("Recordings", "no record");
                    }
                }
            }

        });



    }

Calllog 与 Record 对应问题:

之前只有Callog, 可以直接提交CRM入库就完了.  但现在有了录音, 就不能这么粗暴对待了.  因为Calllog 与 Record 都是被动触发的.  如果当电话未接听(有Calllog, 无Record), 后面又打了一次接听的, 产生的record 就不好对应了. 如果录音再比calllog先出现的话, 就更乱套了. 所以现在需要一个ID来关联 Calllog 与 record.  这个ID也是CRM侧需要的, 当发起一通外呼时, CRM要先生成一条Calllog记录, 然后等待手机端回来数据更新. 所以这个ID对应很重要. 

CRM主动发起外呼或手机拨号-> 手机发起呼叫 -> 挂机 ->CallLog [+ record]

手机被叫响铃 -> 发送来电消息到CRM实现弹屏-> 手机接听或挂机 -> CallLog [+ record]

主叫与被叫, 要找机会向CRM请求通话ID, 可以在第二部完成, 外呼的呼叫阶段和被叫响铃阶段. 这两个正好是电话监听的两个状态, 完整代码:

int callState = 0; // 0:待机/挂机 1:响铃 2:外呼 3:接听

    @Override
    public void onReceive(Context context, Intent intent) {

        if(intent.getAction().equals(PHONE_STATE_RECEIVED)) {
            // 如果是来电
            String incoming_number = intent.getExtras().getString("incoming_number");
            if(incoming_number == null) return;
            Log.i(TAG, "onReceive: "+intent.getAction());
            if(tManager == null) tManager = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
            //电话的状态
            int lastCallState = callState;
            Log.i(TAG, "lastCallState: " + lastCallState);
            switch (tManager.getCallState()) {
                case TelephonyManager.CALL_STATE_RINGING:
                    callState = 1;
                    Log.i(TAG, "Ringing:"+incoming_number);
                    CRMWorker.Sync2CRM(context,getIncomingCall(incoming_number));

                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    //接听状态

                    if(lastCallState == 0) {
                        callState = 2;
                        Log.i(TAG, "CallOut:"+incoming_number);
                        CRMWorker.Sync2CRM(context, getCallOutData(incoming_number));
                    }else {
                        callState = 3;
                        Log.i(TAG, "Answer:"+incoming_number);
                    }

                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    //挂断状态
                    callState = 0;
                    Log.i(TAG, "Hangup:"+incoming_number);

                    break;
            }
        }
        else if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
            Log.i(TAG, "onReceive: "+intent.getAction());
            Log.i(TAG, "BOOT_COMPLETED启动服务:TelephonyManagerService");
            if(!TelephonyManagerService.isServiceRunning(context, TelephonyManagerService.class)) {
                context.startService(new Intent(context, TelephonyManagerService.class));
            }
        }
    }

通过这两个事件, 调用Worker请求CRM, 回来的数据再通过本地广播发给service, 去标记全局的通话ID(我用的是SharedPreferences).  这样就实现了, 在电话打通之前先获取到与CRM同步的通话ID, 后面CallLog 和 Record 拿到后, 就可以带着这个ID发给CRM去更新对应的记录了. 

最后还有一个意外惊喜, 手机开启OTG功能后, 可以使用主动降噪的USB耳机. 完美适配了😄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值