package com.example.hgx.phoneinfo60.Recording;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast; /**
* Created by hgx on 2016/6/13.
*/
public class PhoneCallReceiver extends BroadcastReceiver {
private int lastCallState = TelephonyManager.CALL_STATE_IDLE;
private boolean isIncoming = false;
private static String contactNum;
Intent audioRecorderService; public PhoneCallReceiver() {
} @Override
public void onReceive(Context context, Intent intent) {
//如果是去电
if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){
contactNum = intent.getExtras().getString(Intent.EXTRA_PHONE_NUMBER);
}else //android.intent.action.PHONE_STATE.查了下android文档,貌似没有专门用于接收来电的action,所以,非去电即来电.
{
String state = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String phoneNumber = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER); int stateChange = 0; if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)){
//空闲状态
stateChange =TelephonyManager.CALL_STATE_IDLE;
if (isIncoming){
onIncomingCallEnded(context,phoneNumber);
}else {
onOutgoingCallEnded(context,phoneNumber);
}
}else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
//摘机状态
stateChange = TelephonyManager.CALL_STATE_OFFHOOK;
if (lastCallState != TelephonyManager.CALL_STATE_RINGING){
//如果最近的状态不是来电响铃的话,意味着本次通话是去电
isIncoming =false;
onOutgoingCallStarted(context,phoneNumber);
}else {
//否则本次通话是来电
isIncoming = true;
onIncomingCallAnswered(context, phoneNumber);
}
}else if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
//来电响铃状态
stateChange = TelephonyManager.CALL_STATE_RINGING;
lastCallState = stateChange;
onIncomingCallReceived(context,contactNum);
} } } protected void onIncomingCallStarted(Context context,String number){
Toast.makeText(context,"Incoming call is started",Toast.LENGTH_LONG).show();
context.startService(new Intent(context,AudioRecorderService.class)); } protected void onOutgoingCallStarted(Context context,String number){
Toast.makeText(context, "Outgoing call is started", Toast.LENGTH_LONG).show();
context.startService(new Intent(context, AudioRecorderService.class));
} protected void onIncomingCallEnded(Context context,String number){
Toast.makeText(context, "Incoming call is ended", Toast.LENGTH_LONG).show();
context.startService(new Intent(context, AudioRecorderService.class));
} protected void onOutgoingCallEnded(Context context,String number){
Toast.makeText(context, "Outgoing call is ended", Toast.LENGTH_LONG).show();
context.startService(new Intent(context, AudioRecorderService.class));
} protected void onIncomingCallReceived(Context context,String number){
Toast.makeText(context, "Incoming call is received", Toast.LENGTH_LONG).show();
}
protected void onIncomingCallAnswered(Context context, String number) {
Toast.makeText(context, "Incoming call is answered", Toast.LENGTH_LONG).show();
}
}

  

下面是AudioRecorderService的java实现:

 package com.example.hgx.phoneinfo60.Recording;
import android.app.Service;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.IBinder;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast; import com.example.hgx.phoneinfo60.MyApplication; import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Created by hgx on 2016/6/13.
*/ public class AudioRecorderService extends Service {
private static int RECORD_RATE = 0;
private static int RECORD_BPP = 32;
private static int RECORD_CHANNEL = AudioFormat.CHANNEL_IN_MONO;
private static int RECORD_ENCODER = AudioFormat.ENCODING_PCM_16BIT;
private AudioRecord audioRecorder = null;
private Thread recordT = null;
private Boolean isRecording = false;
private int bufferEle = 1024, bytesPerEle = 2;// want to play 2048 (2K) since 2 bytes we use only 1024 2 bytes in 16bit format
private static int[] recordRate ={44100 , 22050 , 11025 , 8000};
int bufferSize = 0;
File uploadFile; @Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
//maintain the relationship between the caller activity and the callee service, currently useless here
return null;
} @Override
public void onDestroy() {
if (isRecording){
stopRecord();
}else{
Toast.makeText(MyApplication.getContext(), "Recording is already stopped",Toast.LENGTH_SHORT).show();
}
super.onDestroy();
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!isRecording){
startRecord();
}else {
Toast.makeText(MyApplication.getContext(), "Recording is already started",Toast.LENGTH_SHORT).show();
}
return 1;
} private void startRecord(){
audioRecorder = initializeRecord();
if (audioRecorder != null){
Toast.makeText(MyApplication.getContext(), "Recording is started",Toast.LENGTH_SHORT).show();
audioRecorder.startRecording();
}else
return; isRecording = true;
recordT = new Thread(new Runnable() {
@Override
public void run() {
writeToFile();
}
},"Recording Thread");
recordT.start(); } private void writeToFile(){
byte bDate[] = new byte[bufferEle];
FileOutputStream fos =null;
File recordFile = createTempFile();
try {
fos = new FileOutputStream(recordFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
} while (isRecording){
audioRecorder.read(bDate,0,bufferEle);
} try {
fos.write(bDate);
} catch (IOException e) {
e.printStackTrace();
} try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
} //Following function converts short data to byte data
private byte[] writeShortToByte(short[] sData) {
int size = sData.length;
byte[] byteArrayData = new byte[size * 2];
for (int i = 0; i < size; i++) {
byteArrayData[i * 2] = (byte) (sData[i] & 0x00FF);
byteArrayData[(i * 2) + 1] = (byte) (sData[i] >> 8);
sData[i] = 0;
} return byteArrayData;
} //Creates temporary .raw file for recording
private File createTempFile() {
File tempFile = new File(Environment.getExternalStorageDirectory(), "aditi.raw");
return tempFile;
} //Create file to convert to .wav format
private File createWavFile() {
File wavFile = new File(Environment.getExternalStorageDirectory(), "aditi_" + System.currentTimeMillis() + ".wav");
return wavFile;
} /*
* Convert raw to wav file
* @param java.io.File temporay raw file
* @param java.io.File destination wav file
* @return void
*
* */
private void convertRawToWavFile(File tempFile, File wavFile) {
FileInputStream fin = null;
FileOutputStream fos = null;
long audioLength = 0;
long dataLength = audioLength + 36;
long sampleRate = RECORD_RATE;
int channel = 1;
long byteRate = RECORD_BPP * RECORD_RATE * channel / 8;
String fileName = null; byte[] data = new byte[bufferSize];
try {
fin = new FileInputStream(tempFile);
fos = new FileOutputStream(wavFile);
audioLength = fin.getChannel().size();
dataLength = audioLength + 36;
createWaveFileHeader(fos, audioLength, dataLength, sampleRate, channel, byteRate); while (fin.read(data) != -1) {
fos.write(data);
} uploadFile = wavFile.getAbsoluteFile();
} catch (FileNotFoundException e) {
//Log.e("MainActivity:convertRawToWavFile",e.getMessage());
} catch (IOException e) {
//Log.e("MainActivity:convertRawToWavFile",e.getMessage());
} catch (Exception e) {
//Log.e("MainActivity:convertRawToWavFile",e.getMessage());
}
} /*
* To create wav file need to create header for the same
*
* @param java.io.FileOutputStream
* @param long
* @param long
* @param long
* @param int
* @param long
* @return void
*/
private void createWaveFileHeader(FileOutputStream fos, long audioLength, long dataLength, long sampleRate, int channel, long byteRate) { byte[] header = new byte[44]; header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (dataLength & 0xff);
header[5] = (byte) ((dataLength >> 8) & 0xff);
header[6] = (byte) ((dataLength >> 16) & 0xff);
header[7] = (byte) ((dataLength >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f'; // 'fmt ' chunk
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1; // format = 1
header[21] = 0;
header[22] = (byte) channel;
header[23] = 0;
header[24] = (byte) (sampleRate & 0xff);
header[25] = (byte) ((sampleRate >> 8) & 0xff);
header[26] = (byte) ((sampleRate >> 16) & 0xff);
header[27] = (byte) ((sampleRate >> 24) & 0xff);
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
header[32] = (byte) (2 * 16 / 8); // block align
header[33] = 0;
header[34] = 16; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (audioLength & 0xff);
header[41] = (byte) ((audioLength >> 8) & 0xff);
header[42] = (byte) ((audioLength >> 16) & 0xff);
header[43] = (byte) ((audioLength >> 24) & 0xff); try {
fos.write(header, 0, 44);
} catch (IOException e) {
// TODO Auto-generated catch block
//Log.e("MainActivity:createWavFileHeader()",e.getMessage());
} } /*
* delete created temperory file
* @param
* @return void
*/
private void deletTempFile() {
File file = createTempFile();
file.delete();
} /*
* Initialize audio record
*
* @param
* @return android.media.AudioRecord
*/
private AudioRecord initializeRecord() {
short[] audioFormat = new short[]{AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_PCM_8BIT};
short[] channelConfiguration = new short[]{AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO};
for (int rate : recordRate) {
for (short aFormat : audioFormat) {
for (short cConf : channelConfiguration) {
//Log.d("MainActivity:initializeRecord()","Rate"+rate+"AudioFormat"+aFormat+"Channel Configuration"+cConf);
try {
int buffSize = AudioRecord.getMinBufferSize(rate, cConf, aFormat);
bufferSize = buffSize; if (buffSize != AudioRecord.ERROR_BAD_VALUE) {
AudioRecord aRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, rate, cConf, aFormat, buffSize); if (aRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
RECORD_RATE = rate;
//Log.d("MainActivity:InitializeRecord - AudioFormat",String.valueOf(aFormat));
//Log.d("MainActivity:InitializeRecord - Channel",String.valueOf(cConf));
//Log.d("MainActivity:InitialoizeRecord - rceordRate", String.valueOf(rate));
return aRecorder;
}
}
} catch (Exception e) {
//Log.e("MainActivity:initializeRecord()",e.getMessage());
}
}
}
}
return null;
} /*
* Method to stop and release audio record
*
* @param
* @return void
*/
private void stopRecord() {
if (null != audioRecorder) {
isRecording = false;
audioRecorder.stop();
audioRecorder.release();
audioRecorder = null;
recordT = null;
Toast.makeText(getApplicationContext(), "Recording is stopped", Toast.LENGTH_LONG).show();
}
convertRawToWavFile(createTempFile(), createWavFile());
if (uploadFile.exists()) {
//Log.d("AudioRecorderService:stopRecord()", "UploadFile exists");
}
new UploadFile().execute(uploadFile);
deletTempFile();
} /*
* Asynchronous task to upload audio file in background
*
*
*/
private class UploadFile extends AsyncTask<File, Void, Void> {
protected Void doInBackground(File... files) { if (files[0] == null)
return null;
try {
File fileToUpload = files[0];
String boundary = "*****";
String lineEnd = "\r\n";
String twoHyphens = "--";
int maxBufferSize = 1 * 1024 * 1024;
String fileName = fileToUpload.getAbsolutePath();
FileInputStream fis = new FileInputStream(new File(fileName));
URL serverUrl = new URL("http://192.168.78.128/UploadToServer.php");
HttpURLConnection connection = (HttpURLConnection) serverUrl.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("ENCTYPE", "multipart/form-data");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
connection.setRequestProperty("uploaded_file", fileName);
DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + fileName + "\"" + lineEnd);
dos.writeBytes(lineEnd); // create a buffer of maximum size
int bytesAvailable = fis.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize]; // read file and write it into form...
int bytesRead = fis.read(buffer, 0, bufferSize); while (bytesRead > 0) { dos.write(buffer, 0, bufferSize);
bytesAvailable = fis.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fis.read(buffer, 0, bufferSize); } dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// Responses from the server (code and message)
int serverResponseCode = connection.getResponseCode();
String serverResponseMessage = connection.getResponseMessage(); // /Log.d("AudioRecorderService:AsyncTask",String.valueOf(serverResponseCode));
// /Log.d("AudioRecorderService:AsyncTask",serverResponseMessage); if (serverResponseCode == 200) { Toast.makeText(getApplicationContext(), "File is uploaded successfully", Toast.LENGTH_SHORT).show(); } //close the streams //
fis.close();
dos.flush();
dos.close(); } catch (Exception e) {
Log.e("AudioRecorder:Asynctask", e.getMessage());
} return null; }
}
}

  原文地址:http://blog.csdn.net/gyhgx/article/details/51669892

Android 6.0 双向通话自动录音的更多相关文章

  1. 手把手教你Android来去电通话自动录音的方法

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

  2. Android 8.0 功能和 API

    Android 8.0 为用户和开发者引入多种新功能.本文重点介绍面向开发者的新功能. 用户体验 通知 在 Android 8.0 中,我们已重新设计通知,以便为管理通知行为和设置提供更轻松和更统一的 ...

  3. Android 8.0/9.0 wifi 自动连接评分机制

    前言 Android N wifi auto connect流程分析 Android N selectQualifiedNetwork分析 Wifi自动连接时的评分机制 今天了解了一下Wifi自动连接 ...

  4. Android 6.0(棉花糖)新特性

    1.支持4K显示 Android 6.0本身已经支持4K显示,会通过一定优化形式使4K内容更加清晰. 2. 启动验证 (更完整的应用权限管理) Android 6.0在开机时会自动运行验证代码,检测设 ...

  5. Android 6.0 新功能及主要 API 变更

    运行时权限 这个版本中引入了新的权限模型,现在用户可以在运行时直接管理应用程序的权限.这个模型基于用户对权限控制的更多可见性,同时为应用程序的开发者提供更流畅的应用安装和自动升级.用户可以为已安装的每 ...

  6. Android 4.0源码目录结构

    转:http://blog.csdn.net/xiangjai/article/details/9012387 在学习Android的过程中,学习写应用还好,一开始不用管太多代码,直接调用函数就可以了 ...

  7. Android 6.0 Changes

    原文链接:http://developer.android.com/about/versions/marshmallow/android-6.0-changes.html 伴随着新特性和功能,Andr ...

  8. Android 6.0 动态权限申请注意事项

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/uana_777/article/details/54136255 Part One 权限区分 And ...

  9. Android 6.0 变更

    Android 6.0(API 级别 23)除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更.本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更. 如果您之前发布过 Andr ...

随机推荐

  1. 201521123049 《JAVA程序设计》 第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) ...

  2. Android之View绘制流程开胃菜---setContentView(...)详细分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 1 为什么要分析setContentView方法 作为安卓开发者相信大部分都有意或者无意看过如下图示:PhoneWindow,DecorView这些 ...

  3. Spring第七篇【Spring的JDBC模块】

    前言 上一篇Spring博文主要讲解了如何使用Spring来实现AOP编程,本博文主要讲解Spring的对JDBC的支持- 对于JDBC而言,我们肯定不会陌生,我们在初学的时候肯定写过非常非常多的JD ...

  4. Hibernate第六篇【多对多映射、一对一映射】

    前言 前面已经讲解了一对多和多对一的映射是怎么配置了,也讲解了inverse和cascade属性对关联关系的影响,本博文讲解多对多的映射和一对一的映射! 多对多映射 需求:一个项目由多个员工开发,一个 ...

  5. Mysql中的like模糊查询

    MySql的like语句中的通配符:百分号.下划线和escape %代表任意多个字符 _代表一个字符 escape,转义字符后面的%或_,使其不作为通配符,而是普通字符匹配   数据库数据如下: 1. ...

  6. .NET Excel导出方法及其常见问题详解

    摘要:.NET Excel导出方法及其常见问题详解. 一.Excel导出的实现方法 在.net 程序开发中,对于Excel文件的导出我们一共有三种导出方式: 利用文件输出流进行读写操作 这种方式的导出 ...

  7. python pyinstaller打包exe暗坑1

    环境 python2.7.9 win-xp 今天打包了一个小脚本,结果打开报错

  8. 反射结合xml简单的模拟spring创建bean

    框架最底层其实就是通过反射实现的,就像spring,当你配置各种各样的bean时都是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,这儿写一个简单的 ...

  9. 解决linux下部署科大讯飞时的版本过低问题

    在将项目部     署到阿里云服务器之后,需要将科大讯飞SDK下的libmsc64.so依赖库文件上传至/usr/java/jdk1.8.0_121/jre/lib/amd64路径下   在Windo ...

  10. JS设计模式(一) 单例模式

    命名空间 单例模式是JavaScript中最常见的一种模式,通过这种模式可以为我们提供一个命名空间,例如jQuery库的命名空间为jQuery或$.命名空间的使用是为了让代码更加整洁,在多人协作开发的 ...