安卓监听打电话

安卓监听打电话(去电)

最近公司接到一个需求:需要安卓端监听用户打出去的电话,并记录在自己的数据库中,也就是说不能查询通话记录。现在网上的东西千篇一律,所以写点自己的理解。


目录

需要用到的工具:

TelephonyManager
PhoneStateListener
BroadcastReceiver
Intent.ACTION_NEW_OUTGOING_CALL
android.permission.PROCESS_OUTGOING_CALLS
android.permission.READ_PHONE_STATE

实现方案

首先注册广播接收者去电监听,用TelephonyManager监听电话状态

greendao Bean字段

@Entity
public class VisitPhoneRecord {
    @Id(autoincrement = true)
    private Long id;
    @NotNull
    private long startTime;
    @NotNull
    private long endTime;
    @NotNull
    private String phoneNumber;
}

广播接收者代码

logger工具com.orhanobut:logger
包含数据库存入操作

public class PhoneReceiver extends BroadcastReceiver {
    private boolean isListen = false;
    private String phoneNumber;
    private long startTime;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!isListen) {
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
            if (tm != null) {
                tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
                isListen = true;
            }
        }
        Logger.t("xxxx").d(intent.getAction());
        //去电
        if (Objects.equals(intent.getAction(), Intent.ACTION_NEW_OUTGOING_CALL)) {
            String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            if (!TextUtils.isEmpty(phoneNumber)) {//只有不是空号码的时候我才记在内存中
                startTime = System.currentTimeMillis();
                this.phoneNumber = phoneNumber;
            }
            Logger.t("xxxx").d("call OUT:" + phoneNumber);
        }
    }

    PhoneStateListener listener = new PhoneStateListener() {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            switch (state) {
                case TelephonyManager.CALL_STATE_IDLE:
                    Logger.t("xxxx").d("挂断" + incomingNumber);
                    if (TextUtils.equals(phoneNumber, incomingNumber)) {//这边只要有初始时间,有结束时间,是同一个号码那么就在数据库里面加一条数据
                        VisitPhoneRecordDao recordDao = HomeApp.getInstance().getDaoSession().getVisitPhoneRecordDao();
                        long endTime = System.currentTimeMillis();
                        recordDao.insertOrReplace(new VisitPhoneRecord(null, startTime, endTime, incomingNumber));
                    }
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    //如果是来电,这个必须点击接听按钮才会调用
                    //如果是拨打,那么一开始就会调用,并不是打通了之后才会调用
                    Logger.t("xxxx").d("摘机状态,接听或者拨打" + incomingNumber);
                    break;
                case TelephonyManager.CALL_STATE_RINGING:
                    Logger.t("xxxx").d("响铃:来电号码:" + incomingNumber);
                    break;
            }
        }
    };
}

activity中的注册代码

    private PhoneReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
        receiver = new PhoneReceiver();
        registerReceiver(receiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ...
        unregisterReceiver(receiver);
    }

权限

Manifest中

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

权限我用的RxPermissions

RxPermissions rxPermissions = new RxPermissions(this); 
rxPermissions.request(
    Manifest.permission.PROCESS_OUTGOING_CALLS,
    Manifest.permission.READ_PHONE_STATE,) //权限名称,多个权限之间逗号分隔开
                .subscribe(granted -> {
                    // 在android 6.0之前会默认返回true
                    if (granted) {
                        //权限通过后,检查APP是否需要更新,并且直接登陆
                        goLogin();
                    } else {
                        // 权限未通过
                        AlertDialog alertDialog = new AlertDialog.Builder(this)
                                .setTitle("您没有授权该权限,请在设置中打开授权,重新进入APP登录")
                                .setPositiveButton("确定", (dialog, which) -> {
                                    Uri packageURI = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
                                    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
                                    startActivity(intent);
                                    HomeApp.exitApp();
                                })
                                .setNegativeButton("取消", (dialog, which) -> MyApplication.exitApp()).create();
                        alertDialog.setCanceledOnTouchOutside(false);
                        alertDialog.setOnKeyListener((dialog, keyCode, event) -> keyCode == KeyEvent.KEYCODE_BACK);
                        alertDialog.show();
                    }
                });

坑点

1.TelephonyManager

首先来讲一下TelephonyManager,这个东西有点坑

public void listen(PhoneStateListener listener, int events)

实测,TelephonyManager如果是在activity中获取的,上面这个方法在你调用过后
PhoneStateListener 的

public void onCallStateChanged(int state, String incomingNumber)

这个方法只会被调用一次,而如果TelephonyManager是通过BroadcastReceiver中onReceive的Context获取的那么就会多次调用。

TelephonyManager的第二个坑爹的地方是没有办法注销listener。经过实验发现注册这个广播的activity挂了之后onCallStateChanged方法也只会被调用一次了,所以并不用担心注册了之后需要注销。所以尽量不要静态注册这个广播接收者。
错误示范

<receiver android:name=".PhoneReceiver">   
    <intent-filter>   
        <action android:name="android.intent.action.PHONE_STATE"/>   
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />   
    </intent-filter>   
</receiver>   

2.PhoneStateListener

错误示范

//注意,方法必须写在super方法后面,否则incomingNumber无法获取到值。
super.onCallStateChanged(state, incomingNumber);

首先这行代码并不需要调用,源码父类都是空实现,上面的注解更是无稽之谈

3.BroadcastReceiver

第一:这个玩意里面你才能够用TelephonyManager
第二:这个尽量动态注册
第三:

<action android:name="android.intent.action.PHONE_STATE"/>

这个可以不需要,你不需要用这个action让Receiver每次都收到电话状态的变化。因为PhoneStateListener已经跟你监听了,除非你想自己用Receiver写而不用PhoneStateListener监听。**
第四:去电监听只会在你拨打电话号码得时候才会被回掉,如果你想监听来电请不要像我这样写你还是需要注册PHONE_STATE这个action来初始化PhoneStateListener

总结

网上抄手多,想要弄清楚要么自己试,要么看文档。以上算是我记录一些坑点,毕竟文档也写得很粗糙。

猜你喜欢

转载自blog.csdn.net/arios171/article/details/79678188
今日推荐