大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > Android技巧 > 教你Android来去电通话自动录音的方法

教你Android来去电通话自动录音的方法(1)

关键词:Android通话录音  阅读(1716) 赞(16)

[摘要]本文是对Android来去电通话自动录音的方法的讲解,对学习Android编程技术有所帮助,与大家分享。

我们在使用Android手机打电话时,有时可能会需要对来去电通话自动录音,本文就详细讲解实现Android来去电通话自动录音的方法,大家按照文中的方法编写程序就可以完成此功能。

来去电自动录音的关键在于如何监听手机电话状态的转变:

1)来电的状态的转换如下(红色标记是我们要用到的状态)

空闲(IDEL)——> 响铃(RINGING)——> 接听(ACTIVE)——> 挂断(经历DISCONNECTING——DISCONNECTED)——> 空闲(IDEL)

或者 空闲(IDEL)——> 响铃(RINGING)——> 拒接 ——> 空闲(IDEL)

2)去电状态的转换如下

空闲(IDEL)——> 拨号 (DIALING)——> (对方)响铃(ALERTING) ——> 建立连接(ACTIVE)—— 挂断(经历DISCONNECTING——DISCONNECTED)——> 空闲(IDEL)

或者 空闲(IDEL)——> 拨号 (DIALING)——> (对方)响铃(ALERTING)——> 挂断/对方拒接 ——> 空闲(IDEL)

下面就分别就来电和去电这两种状态分析并实现。

1、先进行来电的分析和实现。

相对去电来说,来电状态的转换检测要简单些。android api 中的PhoneStateListener 类提供了相应的方法,但我们需要覆盖其中的 onCallStateChanged(int state, String incomingNumber) 方法即可实现来电状态的检测,并在此基础上添加录音功能即可。其中 state 参数就是各种电话状态,到时我们将它跟下面我们要用到的状态进行比较,若是电话处在我们想要的状态上,则进行一系列操作,否则就不管他。想要获取这些状态,还需要另一个电话相关类,那就是 TelephonyManager, 该类 提供了一些电话状态,其中我们要用到的是:TelephonyManager.CALL_STATE_IDLE(空闲)、TelephonyManager.CALL_STATE_OFFHOOK(摘机)和 TelephonyManager.CALL_STATE_RINGING(来电响铃)这三个状态。判别这三种状态,可以继承 android.telephony.PhoneStateListener 类,实现上面提到的 onCallStateChanged(int state, String incomingNumber) 方法,请看如下代码:

Java代码
  1. publicclassTelListenerextendsPhoneStateListener{
  2. @Override
  3. publicvoidonCallStateChanged(intstate,StringincomingNumber){
  4. super.onCallStateChanged(state,incomingNumber);
  5. switch(state){
  6. caseTelephonyManager.CALL_STATE_IDLE://空闲状态,即无来电也无去电
  7. Log.i("TelephoneState","IDLE");
  8. //此处添加一系列功能代码
  9. break;
  10. caseTelephonyManager.CALL_STATE_RINGING://来电响铃
  11. Log.i("TelephoneState","RINGING");
  12. //此处添加一系列功能代码
  13. break;
  14. caseTelephonyManager.CALL_STATE_OFFHOOK://摘机,即接通
  15. Log.i("TelephoneState","OFFHOOK");
  16. //此处添加一系列功能代码
  17. break;
  18. }
  19. Log.i("TelephoneState",String.valueOf(incomingNumber));
  20. }
  21. }

有了以上来电状态监听代码还不足以实现监听功能,还需要在我们的一个Activity或者Service中实现监听,方法很简单,代码如下:

Java代码
  1. /**
  2. *在activity或者service中加入如下代码,以实现来电状态监听
  3. */
  4. TelephonyManagertelMgr=(TelephonyManager)context.getSystemService(
  5. Context.TELEPHONY_SERVICE);
  6. telMgr.listen(newTelListener(),PhoneStateListener.LISTEN_CALL_STATE);

这样就实现了来电状态监听功能,但要能够在设备中跑起来,这还不够,它还需要两个获取手机电话状态的权限:

XML/HTML代码
  1. <uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
  2. <uses-permissionandroid:name="android.permission.PROCESS_OUTGOING_CALLS"/>

这样的话就可以跑起来了。

说到这,我想如果你可以实现录音功能的话,在此基础上实现来电自动录音就应该没什么问题了,不过请容我简单罗嗦几句。既然是来电,那么要想录音的话,那么应该就是在监听到 TelephonyManager.CALL_STATE_OFFHOOK 的状态时开启录音机开始录音, 在监听到TelephonyManager.CALL_STATE_IDLE 的状态时关闭录音机停止录音。这样,来电录音功能就完成了,不要忘记录音功能同样需要权限:

XML/HTML代码
  1. <uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>
  2. <!--要存储文件或者创建文件夹的话还需要以下两个权限-->
  3. <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  4. <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2、介绍完了来电自动录音,下面就来介绍去电自动录音的实现方法。

上面说过,相比来电状态的监听,去电的要麻烦些,甚至这种方法不是通用的,这个主要是因为android api 中没有提供去电状态监听的相应类和方法(也许我刚接触,没有找到)。刚开始网上搜索了一通也没有找到对应的解决方法,大多是 来电监听的,也就是上面的方法。不过中途发现一篇博文(后来就搜不到了),记得是查询系统日志的方式,从中找到去电过程中的各个状态的关键词。无奈之中,最终妥协了此方法。

我的(联想A65上的)去电日志内容如下:

过滤关键词为 mforeground

Java代码
  1. 01-0616:29:54.225:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  2. 01-0616:29:54.245:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  3. 01-0616:29:54.631:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  4. 01-0616:29:54.645:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  5. 01-0616:29:54.742:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  6. 01-0616:29:54.766:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  7. 01-0616:29:54.873:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  8. 01-0616:29:54.877:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  9. 01-0616:29:55.108:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  10. 01-0616:29:55.125:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DIALING
  11. 01-0616:29:57.030:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():ACTIVE
  12. 01-0616:29:57.155:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():ACTIVE
  13. 01-0616:29:57.480:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():ACTIVE
  14. 01-0616:29:57.598:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():ACTIVE
  15. 01-0616:29:59.319:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DISCONNECTING
  16. 01-0616:29:59.373:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():DISCONNECTING
  17. 01-0616:30:00.392:D/InCallScreen(251):-onDisconnect:currentlyIdle:true;mForegroundCall.getState():DISCONNECTED
  18. 01-0616:30:00.399:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):-onDisconnect:currentlyIdle:true;mForegroundCall.getState():DISCONNECTED
  19. 01-0616:30:01.042:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():IDLE
  20. 01-0616:30:01.070:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():IDLE
  21. 01-0616:30:01.558:D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():IDLE
  22. 01-0616:30:01.572:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mForegroundCall.getState():IDLE

过滤关键词 mbackground

Java代码
  1. 01-0616:29:54.226:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  2. 01-0616:29:54.256:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  3. 01-0616:29:54.638:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  4. 01-0616:29:54.652:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  5. 01-0616:29:54.743:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  6. 01-0616:29:54.770:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  7. 01-0616:29:54.875:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  8. 01-0616:29:54.882:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  9. 01-0616:29:55.109:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  10. 01-0616:29:55.142:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  11. 01-0616:29:57.031:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  12. 01-0616:29:57.160:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  13. 01-0616:29:57.481:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  14. 01-0616:29:57.622:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  15. 01-0616:29:59.319:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  16. 01-0616:29:59.373:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  17. 01-0616:30:01.042:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  18. 01-0616:30:01.070:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  19. 01-0616:30:01.559:D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE
  20. 01-0616:30:01.573:V/LogInfoOutGoingCall(2492):D/InCallScreen(251):onPhoneStateChanged:mBackgroundCall.getState():IDLE

从上面的日志可以看到,每一行的末尾的大写英文词就是去电的状态,状态说明如下:

DIALING 拨号,对方还未响铃
ACTIVE 对方接通,通话建立
DISCONNECTING 通话断开时
DISCONNECTED 通话已断开,可以认为是挂机了

由于我拨打的是10010,没有响铃过程(电脑自动接通的够快),还少了一个状态,状态是ALERTING,这个就是对方正在响铃的状态。

有了这几个去电状态就好办了,现在我们要做的就是读取系统日志,然后找到这些状态,提取的关键词就是上面提到的 mforeground(前台通话状态) 和 mbackground (后台通话状态)(可能不一样的设备生成的不一样,根据自己具体设备设置,这里只提取前台的),如果读取的这一行日志中 包含 mforground ,再看看是否包含上面的状态的单词。既然说的如此,那么看看读取系统日志的代码吧。

Java代码
  1. packagecom.sdvdxl.phonerecorder;
  2. importjava.io.BufferedReader;
  3. importjava.io.IOException;
  4. importjava.io.InputStream;
  5. importjava.io.InputStreamReader;
  6. importcom.sdvdxl.outgoingcall.OutgoingCallState;
  7. importandroid.content.Context;
  8. importandroid.content.Intent;
  9. importandroid.util.Log;
  10. /**
  11. *
  12. *@authorsdvdxl
  13. *找到日志中的
  14. *onPhoneStateChanged:mForegroundCall.getState()这个是前台呼叫状态
  15. *mBackgroundCall.getState()后台电话
  16. *若是DIALING则是正在拨号,等待建立连接,但对方还没有响铃,
  17. *ALERTING呼叫成功,即对方正在响铃,
  18. *若是ACTIVE则已经接通
  19. *若是DISCONNECTED则本号码呼叫已经挂断
  20. *若是IDLE则是处于空闲状态
  21. *
  22. */
  23. publicclassReadLogextendsThread{
  24. privateContextctx;
  25. privateintlogCount;
  26. privatestaticfinalStringTAG="LogInfoOutGoingCall";
  27. /**
  28. *前后台电话
  29. *@authorsdvdxl
  30. *
  31. */
  32. privatestaticclassCallViewState{
  33. publicstaticfinalStringFORE_GROUND_CALL_STATE="mForeground";
  34. }
  35. /**
  36. *呼叫状态
  37. *@authorsdvdxl
  38. *
  39. */
  40. privatestaticclassCallState{
  41. publicstaticfinalStringDIALING="DIALING";
  42. publicstaticfinalStringALERTING="ALERTING";
  43. publicstaticfinalStringACTIVE="ACTIVE";
  44. publicstaticfinalStringIDLE="IDLE";
  45. publicstaticfinalStringDISCONNECTED="DISCONNECTED";
  46. }
  47. publicReadLog(Contextctx){
  48. this.ctx=ctx;
  49. }
  50. /**
  51. *读取Log流
  52. *取得呼出状态的log
  53. *从而得到转换状态
  54. */
  55. @Override
  56. publicvoidrun(){
  57. Log.d(TAG,"开始读取日志记录");
  58. String[]catchParams={"logcat","InCallScreen*:s"};
  59. String[]clearParams={"logcat","-c"};
  60. try{
  61. Processprocess=Runtime.getRuntime().exec(catchParams);
  62. InputStreamis=process.getInputStream();
  63. BufferedReaderreader=newBufferedReader(newInputStreamReader(is));
  64. Stringline=null;
  65. while((line=reader.readLine())!=null){
  66. logCount++;
  67. //输出所有
  68. Log.v(TAG,line);
  69. //日志超过512条就清理
  70. if(logCount>512){
  71. //清理日志
  72. Runtime.getRuntime().exec(clearParams)
  73. .destroy();//销毁进程,释放资源
  74. logCount=0;
  75. Log.v(TAG,"-----------清理日志---------------");
  76. }
  77. /*---------------------------------前台呼叫-----------------------*/
  78. //空闲
  79. if(line.contains(ReadLog.CallViewState.FORE_GROUND_CALL_STATE)
  80. &&line.contains(ReadLog.CallState.IDLE)){
  81. Log.d(TAG,ReadLog.CallState.IDLE);
  82. }
  83. //正在拨号,等待建立连接,即已拨号,但对方还没有响铃,
  84. if(line.contains(ReadLog.CallViewState.FORE_GROUND_CALL_STATE)
  85. &&line.contains(ReadLog.CallState.DIALING)){
  86. Log.d(TAG,ReadLog.CallState.DIALING);
  87. }
  88. //呼叫对方正在响铃
  89. if(line.contains(ReadLog.CallViewState.FORE_GROUND_CALL_STATE)
  90. &&line.contains(ReadLog.CallState.ALERTING)){
  91. Log.d(TAG,ReadLog.CallState.ALERTING);
  92. }
  93. //已接通,通话建立
  94. if(line.contains(ReadLog.CallViewState.FORE_GROUND_CALL_STATE)
  95. &&line.contains(ReadLog.CallState.ACTIVE)){
  96. Log.d(TAG,ReadLog.CallState.ACTIVE);
  97. }
  98. //断开连接,即挂机
  99. if(line.contains(ReadLog.CallViewState.FORE_GROUND_CALL_STATE)
  100. &&line.contains(ReadLog.CallState.DISCONNECTED)){
  101. Log.d(TAG,ReadLog.CallState.DISCONNECTED);
  102. }
  103. }//ENDwhile
  104. }catch(IOExceptione){
  105. e.printStackTrace();
  106. }//ENDtry-catch
  107. }//ENDrun
  108. }//ENDclassReadLog

以上代码中,之所以用线程,是为了防止读取日志过程中阻滞主方法的其他方法的执行,影响到程序捕捉对应的电话状态。

好了,捕捉到了去电过程中各个状态的转变,那么,如何通知给程序呢,我采用的方法是捕获后立马给系统发送广播,然后程序进行广播接收,接收后再处理录音事件。要发送广播,就要发送一个唯一的广播,为此,建立如下类:

Java代码
  1. packagecom.sdvdxl.outgoingcall;
  2. importcom.sdvdxl.phonerecorder.ReadLog;
  3. importandroid.content.Context;
  4. importandroid.util.Log;
  5. publicclassOutgoingCallState{
  6. Contextctx;
  7. publicOutgoingCallState(Contextctx){
  8. this.ctx=ctx;
  9. }
  10. /**
  11. *前台呼叫状态
  12. *@authorsdvdxl
  13. *
  14. */
  15. publicstaticfinalclassForeGroundCallState{
  16. publicstaticfinalStringDIALING=
  17. "com.sdvdxl.phonerecorder.FORE_GROUND_DIALING";
  18. publicstaticfinalStringALERTING=
  19. "com.sdvdxl.phonerecorder.FORE_GROUND_ALERTING";
  20. publicstaticfinalStringACTIVE=
  21. "com.sdvdxl.phonerecorder.FORE_GROUND_ACTIVE";
  22. publicstaticfinalStringIDLE=
  23. "com.sdvdxl.phonerecorder.FORE_GROUND_IDLE";
  24. publicstaticfinalStringDISCONNECTED=
  25. "com.sdvdxl.phonerecorder.FORE_GROUND_DISCONNECTED";
  26. }
  27. /**
  28. *开始监听呼出状态的转变,
  29. *并在对应状态发送广播
  30. */
  31. publicvoidstartListen(){
  32. newReadLog(ctx).start();
  33. Log.d("Recorder","开始监听呼出状态的转变,并在对应状态发送广播");
  34. }
  35. }

程序需要读取系统日志权限:

«上一页12下一页»


相关评论