android实现通话自动录音服务

本文实例为大家分享了android实现通话自动录音服务的具体代码,供大家参考,具体内容如下

需求:

①:通话自动录音;

②:无界面,只是一个service;

③:录音自动压缩上传;

④:当用户清理后台的时候,要求service不可以被杀死;

⑤:稳定性:1、无网络的情况下;2、上传失败;3、服务报错。

解决方案:

①:通话自动录音

启动一个service,监听用户手机通话状态,当检测到用户处于通话状态下,立即开始录音,通话结束后,停止录音,并保存文件。

此功能的前提条件:

1、录音权限、读写存储空间的权限、读取通话状态的权限;

2、Service不可以被停止,否则无法录音。

3、开机启动(不可以让用户每次开机都主动去打开服务)

②:无界面,只是一个service

方案①

普通的service,监听开机广播,当用户开机的时候,启动service。但是,发现service并没有启动。想要启动一个service,必须要有一个activity,即使你不打开这个activity。

在真正做项目的时候,PM会提出各种你不能理解的需求,比如说本系统,PM要求本应用只是一个录音服务,不可以有任何界面,也不可以在手机桌面上出现应用图标。因此,方案①不可行。

方案②

Android手机在设置里面都一个辅助功能(个别手机也叫:无障碍),利用这个我们可以实现一些强大的功能,前提是用户开启我们的辅助功能,抢红包软件就是利用辅助功能实现的。

③:录音自动压缩上传

我们只需要在上传之前对文件进行压缩处理,然后再上传即可。

④:当用户清理后台的时候,要求service不可以被杀死

不会被杀死的服务,或许只有系统服务吧。当然类似于QQ、微信他们做的这种全家桶也可以做到。大公司是可以和厂商合作的,他们的应用可以不那么容易被杀死。当然也不提倡这样做,这样就是垃圾软件,破坏了Android开发的美好环境。

其实,如果可以把服务设置成系统服务,那么只要用户不主动在辅助功能页面关掉服务,后台是清理不掉改服务的。本人在小米手机上测试过,设置成系统级别的服务后,当清理后台的时候,即使服务被杀死,也会非常快的重新启动。(感兴趣的同学可以试一下)

⑤:稳定性:1、无网络的情况下;2、上传失败;3、服务报错

思路:

当无网络的情况下,把录音文件的地址保存下来(保存的方式有很多:Sqlite、Sharedpreferences等等),上传失败也一样,失败的原因可能有很多种:网络断开、接口报错等,当网络恢复的时候,可以重新上传,这样就不会丢失录音文件。

代码很简单,注释很详细:

项目的结构:

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

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

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

<!-- 要存储文件或者创建文件夹的话还需要以下两个权限 -->

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

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

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

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

<!--允许读取网络状态-->

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

<!--允许读取wifi网络状态-->

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

<service

android:name=".service.RecorderService"

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">

<intent-filter>

<action android:name="android.accessibilityservice.AccessibilityService" />

</intent-filter>

<meta-data

android:name="android.accessibilityservice"

android:resource="@xml/accessible_service_config" />

</service>

/**

* 电话自动录音辅助服务(去电、来电自动录音并上传)。

* Created by wang.ao in 2017/2/24.

*/

public class RecorderService extends AccessibilityService {

private static final String TAG = "RecorderService";

private static final String TAG1 = "手机通话状态";

/**

* 音频录制

*/

private MediaRecorder recorder;

private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

/**

* 监听拨号广播,以便获取用户拨出的电话号码

*/

private OutCallReceiver outCallReceiver;

private IntentFilter intentFilter;

/**

* 网络状态改变广播,当网络畅通的状态下,把用户未上传的录音文件都上传掉

*/

private NetworkConnectChangedReceiver networkConnectChangedReceiver;

private IntentFilter intentFilter2;

/**

* 当前通话对象的电话号码

*/

private String currentCallNum = "";

/**

* 区分来电和去电

*/

private int previousStats = 0;

/**

* 当前正在录制的文件

*/

private String currentFile = "";

/**

* 保存未上传的录音文件

*/

private SharedPreferences unUploadFile;

private String dirPath = "";

private boolean isRecording = false;

@Override

protected void onServiceConnected() {

Log.i(TAG, "onServiceConnected");

Toast.makeText(getApplicationContext(), "自动录音服务已启动", Toast.LENGTH_LONG).show();

}

@Override

public void onAccessibilityEvent(AccessibilityEvent event) {

// TODO Auto-generated method stub

Log.i(TAG, "eventType " + event.getEventType());

}

@Override

public void onInterrupt() {

// TODO Auto-generated method stub

Log.i(TAG, "onServiceConnected");

}

@Override

public boolean onUnbind(Intent intent) {

return super.onUnbind(intent);

}

@Override

public void onCreate() {

super.onCreate();

TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

// 监听电话状态

tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);

outCallReceiver = new OutCallReceiver();

intentFilter = new IntentFilter();

//设置拨号广播过滤

intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");

registerReceiver(outCallReceiver, intentFilter);

//注册拨号广播接收器

networkConnectChangedReceiver = new NetworkConnectChangedReceiver();

intentFilter2 = new IntentFilter();

//设置网络状态改变广播过滤

intentFilter2.addAction("android.net.conn.CONNECTIVITY_CHANGE");

intentFilter2.addAction("android.net.wifi.WIFI_STATE_CHANGED");

intentFilter2.addAction("android.net.wifi.STATE_CHANGE");

//注册网络状态改变广播接收器

registerReceiver(networkConnectChangedReceiver, intentFilter2);

unUploadFile = getSharedPreferences("un_upload_file", 0);

unUploadFile.edit().putString("description", "未上传的录音文件存放路径").commit();

dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/com.ct.phonerecorder/";

}

@Override

public void onDestroy() {

super.onDestroy();

Toast.makeText(getApplicationContext(), "进程被关闭,无法继续录音,请打开录音服务", Toast.LENGTH_LONG).show();

if (outCallReceiver != null) {

unregisterReceiver(outCallReceiver);

}

if (networkConnectChangedReceiver != null) {

unregisterReceiver(networkConnectChangedReceiver);

}

}

class MyListener extends PhoneStateListener {

@Override

public void onCallStateChanged(int state, String incomingNumber) {

// TODO Auto-generated method stub

Log.d(TAG1, "空闲状态" + incomingNumber);

switch (state) {

case TelephonyManager.CALL_STATE_IDLE:

Log.d(TAG1, "空闲");

if (recorder != null && isRecording) {

recorder.stop();// 停止录音

recorder.release();

recorder = null;

Log.d("电话", "通话结束,停止录音");

uploadFile(currentFile);

}

isRecording = false;

break;

case TelephonyManager.CALL_STATE_RINGING:

Log.d(TAG1, "来电响铃" + incomingNumber);

// 进行初始化

break;

case TelephonyManager.CALL_STATE_OFFHOOK:

Log.d(TAG1, "摘机" + (!incomingNumber.equals("") ? incomingNumber : currentCallNum));

initRecord(!incomingNumber.equals("") ? incomingNumber : currentCallNum);

// 开始录音

if (recorder != null) {

recorder.start();

isRecording = true;

}

default:

break;

}

super.onCallStateChanged(state, incomingNumber);

}

}

/**

* 当录音结束后,自动上传录音文件。

* ①网络可用:直接上传;

* ②网络不可用:保存文件路径,待网络可用的时候再进行上传;

* ③上传失败的文件,也保存文件路径,或者重新上传。

*/

public void uploadFile(String file) {

ZipUtils.zipFile(dirPath + file, dirPath + file + ".zip");

if (NetWorkUtils.isNetworkConnected(getApplicationContext())) {

//上传文件

// OkHttpUtils.postFile()

} else {

saveUnUploadFIles(dirPath + file + ".zip");

}

}

/**

* 保存未上传的录音文件

*

* @param file 未上传的录音文件路径

*/

private void saveUnUploadFIles(String file) {

String files = unUploadFile.getString("unUploadFile", "");

if (files.equals("")) {

files = file;

} else {

StringBuilder sb = new StringBuilder(files);

files = sb.append(";").append(file).toString();

}

unUploadFile.edit().putString("unUploadFile", files).commit();

}

/**

* 上传因为网络或者其他原因,暂未上传或者上传失败的文件,重新上传

*/

public void uploadUnUploadedFiles() {

//获取当前还未上传的文件,并把这些文件上传

String files = unUploadFile.getString("unUploadFile", "");

unUploadFile.edit().putString("unUploadFile", "").commit();

if (files.equals("")) {

return;

}

String[] fileArry = files.split(";");

int len = fileArry.length;

for (String file : fileArry) {

upload(file);

}

}

/**

* 文件上传

*

* @param file 要上传的文件

*/

public void upload(final String file) {

File file1 = new File(file);

if (file1 == null || !file1.exists()) {

//文件不存在

return;

}

if (!NetWorkUtils.isNetworkConnected(getApplicationContext())) {

saveUnUploadFIles(file);

return;

}

Map<String, String> map = new HashMap<String, String>();

map.put("type", "1");

final String url = "http://192.168.1.158:8082/uploader";

OkHttpUtils.post()//

.addFile("mFile", file1.getName(), file1)//

.url(url)//

.params(map).build()//

.execute(new StringCallback() {

@Override

public void onResponse(String response, int id) {

Log.e(TAG, "成功 response=" + response);

}

@Override

public void onError(Call call, Exception e, int id) {

Log.e(TAG, "失败 response=" + e.toString());

saveUnUploadFIles(file);

}

});

}

/**

* 初始化录音机,并给录音文件重命名

*

* @param incomingNumber 通话号码

*/

private void initRecord(String incomingNumber) {

previousStats = TelephonyManager.CALL_STATE_RINGING;

recorder = new MediaRecorder();

recorder.setAudioSource(MediaRecorder.AudioSource.MIC);// Microphone

recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);// 设置输出3gp格式

File out = new File(dirPath);

if (!out.exists()) {

out.mkdirs();

}

recorder.setOutputFile(dirPath

+ getFileName((previousStats == TelephonyManager.CALL_STATE_RINGING ? incomingNumber : currentCallNum))

);

recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 设置音频编码格式

try {

recorder.prepare();// 做好准备

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 获取录音文件的名称

*

* @param incomingNumber 通话号码

* @return 获取录音文件的名称

*/

private String getFileName(String incomingNumber) {

Date date = new Date(System.currentTimeMillis());

currentFile = incomingNumber + " " + dateFormat.format(date) + ".mp3";

return currentFile;

}

/**

* 拨号广播接收器,并获取拨号号码

*/

public class OutCallReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

Log.d(TAG1, "当前手机拨打了电话:" + currentCallNum);

if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {

currentCallNum = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);

Log.d(TAG1, "当前手机拨打了电话:" + currentCallNum);

} else {

Log.d(TAG1, "有电话,快接听电话");

}

}

}

/**

* 网络状态change广播接收器

*/

public class NetworkConnectChangedReceiver extends BroadcastReceiver {

private static final String TAG = "network status";

@Override

public void onReceive(Context context, Intent intent) {

/**

* 这个监听网络连接的设置,包括wifi和移动数据的打开和关闭。.

* 最好用的还是这个监听。wifi如果打开,关闭,以及连接上可用的连接都会接到监听。见log

* 这个广播的最大弊端是比上边两个广播的反应要慢,如果只是要监听wifi,我觉得还是用上边两个配合比较合适

*/

if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {

ConnectivityManager manager = (ConnectivityManager) context

.getSystemService(Context.CONNECTIVITY_SERVICE);

Log.i(TAG, "CONNECTIVITY_ACTION");

NetworkInfo activeNetwork = manager.getActiveNetworkInfo();

if (activeNetwork != null) { // connected to the internet

if (activeNetwork.isConnected()) {

//当前网络可用

if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {

// connected to wifi

Log.e(TAG, "当前WiFi连接可用 ");

} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {

// connected to the mobile provider's data plan

Log.e(TAG, "当前移动网络连接可用 ");

}

uploadUnUploadedFiles();

} else {

Log.e(TAG, "当前没有网络连接,请确保你已经打开网络 ");

}

} else { // not connected to the internet

Log.e(TAG, "当前没有网络连接,请确保你已经打开网络 ");

}

}

}

}

}

/**

* 获取网络连接状态工具类

* Created by wang.ao in 2017/2/24.

*/

public class NetWorkUtils {

/**

* 判断是否有网络连接

* @param context

* @return

*/

public static boolean isNetworkConnected(Context context) {

if (context != null) {

ConnectivityManager mConnectivityManager = (ConnectivityManager) context

.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();

if (mNetworkInfo != null) {

return mNetworkInfo.isAvailable();

}

}

return false;

}

/**

* 判断WIFI网络是否可用

* @param context

* @return

*/

public static boolean isWifiConnected(Context context) {

if (context != null) {

ConnectivityManager mConnectivityManager = (ConnectivityManager) context

.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo mWiFiNetworkInfo = mConnectivityManager

.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

if (mWiFiNetworkInfo != null) {

return mWiFiNetworkInfo.isAvailable();

}

}

return false;

}

/**

* 判断MOBILE网络是否可用

* @param context

* @return

*/

public static boolean isMobileConnected(Context context) {

if (context != null) {

ConnectivityManager mConnectivityManager = (ConnectivityManager) context

.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo mMobileNetworkInfo = mConnectivityManager

.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);

if (mMobileNetworkInfo != null) {

return mMobileNetworkInfo.isAvailable();

}

}

return false;

}

/**

* 获取当前网络连接的类型信息

* @param context

* @return

*/

public static int getConnectedType(Context context) {

if (context != null) {

ConnectivityManager mConnectivityManager = (ConnectivityManager) context

.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();

if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {

return mNetworkInfo.getType();

}

}

return -1;

}

/**

* 获取当前的网络状态 :没有网络0:WIFI网络1:3G网络2:2G网络3

*

* @param context

* @return

*/

public static int getAPNType(Context context) {

int netType = 0;

ConnectivityManager connMgr = (ConnectivityManager) context

.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

if (networkInfo == null) {

return netType;

}

int nType = networkInfo.getType();

if (nType == ConnectivityManager.TYPE_WIFI) {

netType = 1;// wifi

} else if (nType == ConnectivityManager.TYPE_MOBILE) {

int nSubType = networkInfo.getSubtype();

TelephonyManager mTelephony = (TelephonyManager) context

.getSystemService(Context.TELEPHONY_SERVICE);

if (nSubType == TelephonyManager.NETWORK_TYPE_UMTS

&& !mTelephony.isNetworkRoaming()) {

netType = 2;// 3G

} else {

netType = 3;// 2G

}

}

return netType;

}

}

public class ZipUtils {

private static final int BUFF_SIZE = 1024;

/**

* @param zos 压缩流

* @param parentDirName 父目录

* @param file 待压缩文件

* @param buffer 缓冲区

*

* @return 只要目录中有一个文件压缩失败,就停止并返回

*/

private static boolean zipFile(ZipOutputStream zos, String parentDirName, File file, byte[] buffer) {

String zipFilePath = parentDirName + file.getName();

if (file.isDirectory()) {

zipFilePath += File.separator;

for (File f : file.listFiles()) {

if (!zipFile(zos, zipFilePath, f, buffer)) {

return false;

}

}

return true;

} else {

try {

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

ZipEntry zipEntry = new ZipEntry(zipFilePath);

zipEntry.setSize(file.length());

zos.putNextEntry(zipEntry);

while (bis.read(buffer) != -1) {

zos.write(buffer);

}

bis.close();

return true;

} catch (FileNotFoundException ex) {

ex.printStackTrace();

} catch (IOException ex) {

ex.printStackTrace();

}

return false;

}

}

/**

* @param srcPath 待压缩的文件或目录

* @param dstPath 压缩后的zip文件

* @return 只要待压缩的文件有一个压缩失败就停止压缩并返回(等价于windows上直接进行压缩)

*/

public static boolean zipFile(String srcPath, String dstPath) {

File srcFile = new File(srcPath);

if (!srcFile.exists()) {

return false;

}

byte[] buffer = new byte[BUFF_SIZE];

try {

ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dstPath));

boolean result = zipFile(zos, "", srcFile, buffer);

zos.close();

return result;

} catch (FileNotFoundException ex) {

ex.printStackTrace();

} catch (IOException ex) {

ex.printStackTrace();

}

return false;

}

/**

* @param srcPath 待解压的zip文件

* @param dstPath zip解压后待存放的目录

* @return 只要解压过程中发生错误,就立即停止并返回(等价于windows上直接进行解压)

*/

public static boolean unzipFile(String srcPath, String dstPath) {

if (TextUtils.isEmpty(srcPath) || TextUtils.isEmpty(dstPath)) {

return false;

}

File srcFile = new File(srcPath);

if (!srcFile.exists() || !srcFile.getName().toLowerCase(Locale.getDefault()).endsWith("zip")) {

return false;

}

File dstFile = new File(dstPath);

if (!dstFile.exists() || !dstFile.isDirectory()) {

dstFile.mkdirs();

}

try {

ZipInputStream zis = new ZipInputStream(new FileInputStream(srcFile));

BufferedInputStream bis = new BufferedInputStream(zis);

ZipEntry zipEntry = null;

byte[] buffer = new byte[BUFF_SIZE];

if (!dstPath.endsWith(File.separator)) {

dstPath += File.separator;

}

while ((zipEntry = zis.getNextEntry()) != null) {

String fileName = dstPath + zipEntry.getName();

File file = new File(fileName);

File parentDir = file.getParentFile();

if (!parentDir.exists()) {

parentDir.mkdirs();

}

FileOutputStream fos = new FileOutputStream(file);

while (bis.read(buffer) != -1) {

fos.write(buffer);

}

fos.close();

}

bis.close();

zis.close();

return true;

} catch (FileNotFoundException ex) {

ex.printStackTrace();

} catch (IOException ex) {

ex.printStackTrace();

}

return false;

}

}

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

以上是 android实现通话自动录音服务 的全部内容, 来源链接: utcz.com/p/243080.html

回到顶部