国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android6.0編程實現雙向通話自動錄音功能的方法詳解

2019-10-22 18:32:42
字體:
來源:轉載
供稿:網友

本文實例講述了Android6.0編程實現雙向通話自動錄音功能的方法。分享給大家供大家參考,具體如下:

項目中需要實現基于Android 6.0 的雙向通話自動錄音功能,在查閱相關android電話狀態監聽文章以及Git上的開源錄音項目后,整理出此文

首先,介紹一下android 電話狀態的監聽(來電和去電):
http://www.survivalescaperooms.com/article/32433.htm

實現手機電話狀態的監聽,主要依靠兩個類:

TelephoneMangerPhoneStateListener

TelephonseManger提供了取得手機基本服務的信息的一種方式。因此應用程序可以使用TelephonyManager來探測手機基本服務的情況。應用程序可以注冊listener來監聽電話狀態的改變。

我們不能對TelephonyManager進行實例化,只能通過獲取服務的形式:

Context.getSystemService(Context.TELEPHONY_SERVICE);

注意:對手機的某些信息進行讀取是需要一定許可(permission)的。
主要靜態成員常量:(它們對應PhoneStateListener.LISTEN_CALL_STATE所監聽到的內容)

int CALL_STATE_IDLE  //空閑狀態,沒有任何活動。int CALL_STATE_OFFHOOK //摘機狀態,至少有個電話活動。該活動或是撥打(dialing)或是通話,或是 on hold。并且沒有電話是ringing or waitingint CALL_STATE_RINGING //來電狀態,電話鈴聲響起的那段時間或正在通話又來新電,新來電話不得不等待的那段時間。

項目中使用服務來監聽通話狀態,所以需要弄清楚手機通話狀態在廣播中的對應值:

EXTRA_STATE_IDLE //它在手機通話狀態改變的廣播中,用于表示CALL_STATE_IDLE狀態,即空閑狀態。EXTRA_STATE_OFFHOOK //它在手機通話狀態改變的廣播中,用于表示CALL_STATE_OFFHOOK狀態,即摘機狀態。EXTRA_STATE_RINGING //它在手機通話狀態改變的廣播中,用于表示CALL_STATE_RINGING狀態,即來電狀態ACTION_PHONE_STATE_CHANGED //在廣播中用ACTION_PHONE_STATE_CHANGED這個Action來標示通話狀態改變的廣播(intent)。//注:需要許可READ_PHONE_STATE。String EXTRA_INCOMING_NUMBER //在手機通話狀態改變的廣播,用于從extra取來電號碼。String EXTRA_STATE //在通話狀態改變的廣播,用于從extra取來通話狀態。

如何實現電話監聽呢?

Android在電話狀態改變是會發送action為android.intent.action.PHONE_STATE的廣播,而撥打電話時會發送action為

public static final String ACTION_NEW_OUTGOING_CALL =      "android.intent.action.NEW_OUTGOING_CALL";

的廣播。通過自定義廣播接收器,接受上述兩個廣播便可。

下面給出Java代碼:(其中的Toast均為方便測試而添加)

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();  }}

希望本文所述對大家Android程序設計有所幫助。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 太仓市| 大港区| 英德市| 凤山市| 岳西县| 吉首市| 增城市| 武鸣县| 隆昌县| 镇雄县| 蒙自县| 汝阳县| 察雅县| 游戏| 永平县| 彭泽县| 车致| 河南省| 蛟河市| 个旧市| 左云县| 双峰县| 乃东县| 温泉县| 钟山县| 苍梧县| 开远市| 北海市| 富顺县| 中江县| 广州市| 溧阳市| 磐石市| 绥德县| 乐平市| 固安县| 汶上县| 平邑县| 内江市| 汝南县| 桐柏县|