最近需要做一个类似于电话客户的功能,要求拨打电话能自动录音。所以写了一个dome,希望能够帮到大家。

主要思路就是监听手机通话状态在监听到接听时开始录音,结束停止录音。

AndroidManifest中配置

<!-- 权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
 <uses-permission android:name="android.permission.RECORD_AUDIO" />
 <uses-permission android:name="android.permission.VIBRATE" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.READ_CONTACTS" />
 <uses-permission android:name="android.permission.WRITE_CONTACTS" />
 <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
 <uses-permission android:name="android.permission.READ_CALL_LOG" />
 <uses-permission android:name="android.permission.CALL_PHONE" />
 <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
 <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
 <uses-permission android:name="android.permission.GET_ACCOUNTS" />
 <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

当然了还要在清单文件中注册service

public abstract class CommonAdapter<T> extends BaseAdapter{

 protected Context mContext;
 protected List<T> mList;
 protected int mLayoutId;

 public CommonAdapter(Context context, List<T> list, int layoutId) {
  mContext=context;
  mList=list;
  mLayoutId=layoutId;
 }

 //刷新数据
 public void refresh(List<T> list){
  mList=list;
  notifyDataSetChanged();
 }

 @Override
 public int getCount() {
  return mList.size();
 }

 @Override
 public T getItem(int position) {
  return mList.get(position);
 }

 @Override
 public long getItemId(int position) {
  return position;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  ViewHolder holder = ViewHolder.getHolder(mContext, mLayoutId, convertView, parent);
  convertView(holder,mList.get(position));
  return holder.getConvertView();
 }

 public abstract void convertView(ViewHolder holder,T t);
}
public class RBOutPhoneCallState { 
 
 Context ctx; 
 
 public RBOutPhoneCallState(Context ctx) { 
  this.ctx = ctx; 
 } 
  
 /** 
  * 前台呼叫状态 
  * 
  */ 
 public static final class ForeGroundCallState { 
  public static final String DIALING =  
    "com.sdvdxl.phonerecorder.FORE_GROUND_DIALING"; 
  public static final String ALERTING =  
    "com.sdvdxl.phonerecorder.FORE_GROUND_ALERTING"; 
  public static final String ACTIVE =  
    "com.sdvdxl.phonerecorder.FORE_GROUND_ACTIVE"; 
  public static final String IDLE =  
    "com.sdvdxl.phonerecorder.FORE_GROUND_IDLE"; 
  public static final String DISCONNECTED =  
    "com.sdvdxl.phonerecorder.FORE_GROUND_DISCONNECTED"; 
 } 
  
 /** 
  * 开始监听呼出状态的转变, 
  * 并在对应状态发送广播 
  */ 
 public void startListen() { 
  new RBReadPhoneLog(ctx).start(); 
  Log.d("Recorder", "开始监听呼出状态的转变,并在对应状态发送广播"); 
 } 
  
} 
public class RBPhoneListener extends PhoneStateListener {

 public RBRecorder recorder;
 
 @Override  
 public void onCallStateChanged(int state, String incomingNumber) {  
  super.onCallStateChanged(state, incomingNumber);  
  
  switch (state) {  
  case TelephonyManager.CALL_STATE_IDLE: // 空闲状态,即无来电也无去电  
   Log.i("TelephoneState", "IDLE"); 
   
   //此处添加一系列功能代码 
   if (recorder != null && !recorder.isCommingNumber() && recorder.isStarted()) {
    
    Log.i("TelephoneState", "STOP RECORDER"); 
    recorder.stop();
   }
   
   break;  
  case TelephonyManager.CALL_STATE_RINGING: // 来电响铃  
   Log.i("TelephoneState", "RINGING");  
   //此处添加一系列功能代码 
   break;  
  case TelephonyManager.CALL_STATE_OFFHOOK: // 摘机,即接通 
   Log.i("TelephoneState", "OFFHOOK");  
   //此处添加一系列功能代码 
   
   if (recorder == null) {
    recorder = new RBRecorder();
   } 
   
   if (!recorder.isStarted()) {
    Log.i("TelephoneState", "START RECORDER");
    if (incomingNumber != null && incomingNumber.length() >= 8) {
     //CALLID
     recorder.setPhoneNumber(String.valueOf(incomingNumber));
    }
    
    if (!recorder.isCommingNumber() && !recorder.isStarted()) {
     recorder.start();
    }
   }
   
   break;  
  }  
  
  Log.i("TelephoneState", String.valueOf(incomingNumber));  
 }  
}
public class RBReadPhoneLog extends Thread { 
 private Context ctx; 
 private int logCount; 
  
 private static final String TAG = "LogInfo OutGoing Call"; 
  
 /** 
  * 前后台电话 
  *  
  */ 
 private static class CallViewState { 
  public static final String FORE_GROUND_CALL_STATE = "mForeground"; 
 } 
  
 /** 
  * 呼叫状态  
  * 
  */ 
 private static class CallState { 
  public static final String DIALING = "DIALING"; 
  public static final String ALERTING = "ALERTING"; 
  public static final String ACTIVE = "ACTIVE"; 
  public static final String IDLE = "IDLE"; 
  public static final String DISCONNECTED = "DISCONNECTED"; 
 } 
  
 public RBReadPhoneLog(Context ctx) { 
  this.ctx = ctx; 
 } 
  
 /** 
  * 读取Log流 
  * 取得呼出状态的log 
  * 从而得到转换状态 
  */ 
 @Override 
 public void run() { 
  Log.d(TAG, "开始读取日志记录"); 
   
  String[] catchParams = {"logcat", "InCallScreen *:s"}; 
  String[] clearParams = {"logcat", "-c"}; 
   
  try { 
   Process process=Runtime.getRuntime().exec(catchParams); 
   InputStream is = process.getInputStream(); 
   BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
    
   String line = null; 
   while ((line=reader.readLine())!=null) { 
    logCount  ; 
    //输出所有 
   Log.v(TAG, line); 
     
    //日志超过512条就清理 
    if (logCount>512) { 
     //清理日志 
     Runtime.getRuntime().exec(clearParams) 
      .destroy();//销毁进程,释放资源 
     logCount = 0; 
     Log.v(TAG, "-----------清理日志---------------"); 
    }  
     
    /*---------------------------------前台呼叫-----------------------*/ 
    //空闲 
    if (line.contains(RBReadPhoneLog.CallViewState.FORE_GROUND_CALL_STATE) 
      && line.contains(RBReadPhoneLog.CallState.IDLE)) { 
     Log.d(TAG, RBReadPhoneLog.CallState.IDLE); 
    } 
     
    //正在拨号,等待建立连接,即已拨号,但对方还没有响铃, 
    if (line.contains(RBReadPhoneLog.CallViewState.FORE_GROUND_CALL_STATE) 
      && line.contains(RBReadPhoneLog.CallState.DIALING)) { 
     //发送广播 
     Intent dialingIntent = new Intent(); 
     dialingIntent.setAction(RBOutPhoneCallState.ForeGroundCallState.DIALING); 
     ctx.sendBroadcast(dialingIntent); 
      
     Log.d(TAG, RBReadPhoneLog.CallState.DIALING); 
    } 
     
    //呼叫对方 正在响铃 
    if (line.contains(RBReadPhoneLog.CallViewState.FORE_GROUND_CALL_STATE) 
      && line.contains(RBReadPhoneLog.CallState.ALERTING)) { 
     //发送广播 
     Intent dialingIntent = new Intent(); 
     dialingIntent.setAction(RBOutPhoneCallState.ForeGroundCallState.ALERTING); 
     ctx.sendBroadcast(dialingIntent); 
      
     Log.d(TAG, RBReadPhoneLog.CallState.ALERTING); 
    } 
     
    //已接通,通话建立 
    if (line.contains(RBReadPhoneLog.CallViewState.FORE_GROUND_CALL_STATE) 
      && line.contains(RBReadPhoneLog.CallState.ACTIVE)) { 
     //发送广播 
     Intent dialingIntent = new Intent(); 
     dialingIntent.setAction(RBOutPhoneCallState.ForeGroundCallState.ACTIVE); 
     ctx.sendBroadcast(dialingIntent); 
      
     Log.d(TAG, RBReadPhoneLog.CallState.ACTIVE); 
    } 
     
    //断开连接,即挂机 
    if (line.contains(RBReadPhoneLog.CallViewState.FORE_GROUND_CALL_STATE) 
      && line.contains(RBReadPhoneLog.CallState.DISCONNECTED)) { 
     //发送广播 
     Intent dialingIntent = new Intent(); 
     dialingIntent.setAction(RBOutPhoneCallState.ForeGroundCallState.DISCONNECTED); 
     ctx.sendBroadcast(dialingIntent); 
      
     Log.d(TAG, RBReadPhoneLog.CallState.DISCONNECTED); 
    } 
     
   } 
    
  } catch (IOException e) { 
   e.printStackTrace(); 
  }  
} 
public class RBRecorder {
 private String phoneNumber;
 private MediaRecorder mrecorder;
 private boolean started = false; // 录音机是否启动
 private boolean isCommingNumber = false;// 是否是来电
 private String TAG = "Recorder";

 public RBRecorder(String phoneNumber) {
  this.setPhoneNumber(phoneNumber);
 }

 public RBRecorder() {
 }

 public void start() { 
  started = true;
  mrecorder = new MediaRecorder();

  String fileName = new SimpleDateFormat("yy-MM-dd_HH-mm-ss")
    .format(new Date(System.currentTimeMillis()))   ".mp3";

  String fileSavePath = getFilePath(fileName);

  File recordName = new File(fileSavePath);

  try {
   recordName.createNewFile();
   Log.d("recorder", "创建文件"   recordName.getName());
  } catch (IOException e) {
   e.printStackTrace();
  }

  mrecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
  mrecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
  mrecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);

  mrecorder.setOutputFile(recordName.getAbsolutePath());

  try {
   mrecorder.prepare();
  } catch (IllegalStateException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
  mrecorder.start();
  started = true;
  Log.d(TAG, "录音开始");
 }

 public void stop() {
  try {
   if (mrecorder != null) {
    mrecorder.stop();
    // reset
    mrecorder.release();
    mrecorder = null;
   }
   started = false;
  } catch (IllegalStateException e) {
   e.printStackTrace();
  }

  Log.d(TAG, "录音结束");
 }

 public void pause() {

 }

 public String getPhoneNumber() {
  return phoneNumber;
 }

 public void setPhoneNumber(String phoneNumber) {
  this.phoneNumber = phoneNumber;
 }

 public boolean isStarted() {
  return started;
 }

 public void setStarted(boolean hasStarted) {
  this.started = hasStarted;
 }

 public boolean isCommingNumber() {
  return isCommingNumber;
 }

 public void setIsCommingNumber(boolean isCommingNumber) {
  this.isCommingNumber = isCommingNumber;
 }

 private String getFilePath(String fileName) {
  File sdcardDir = null;
  boolean sdcardExist = Environment.getExternalStorageState().equals(
    android.os.Environment.MEDIA_MOUNTED);
  if (sdcardExist) {
   sdcardDir = Environment.getExternalStorageDirectory();
  }
  String filePath = sdcardDir.toString()   "/Recorder/Recorder";
  File file = new File(filePath);
  if (!file.exists()) {
   file.mkdirs();
  }
  return filePath   "/"   fileName;
 }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持Devmax。

Android实现通话自动录音的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部