本文实例为大家分享了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="" />
<uses-permission android:name="" />
<uses-permission android:name="" />
<!-- 要存储文件或者创建文件夹的话还需要以下两个权限 -->
<uses-permission android:name="" />
<uses-permission android:name="" />
<uses-permission android:name="" />
<uses-permission android:name=""/>
<!--允许读取网络状态-->
<uses-permission android:name=""/>
<!--允许读取wifi网络状态-->
<uses-permission android:name=""/>
<service
android:name="."
android:permission="">
<intent-filter>
<action android:name="" />
</intent-filter>
<meta-data
android:name=""
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() {
(TAG, "onServiceConnected");
(getApplicationContext(), "自动录音服务已启动", ).show();
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// TODO Auto-generated method stub
(TAG, "eventType " + ());
}
@Override
public void onInterrupt() {
// TODO Auto-generated method stub
(TAG, "onServiceConnected");
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onCreate() {
();
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
// 监听电话状态
(new MyListener(), );
outCallReceiver = new OutCallReceiver();
intentFilter = new IntentFilter();
//设置拨号广播过滤
("");
registerReceiver(outCallReceiver, intentFilter);
//注册拨号广播接收器
networkConnectChangedReceiver = new NetworkConnectChangedReceiver();
intentFilter2 = new IntentFilter();
//设置网络状态改变广播过滤
("");
("");
("");
//注册网络状态改变广播接收器
registerReceiver(networkConnectChangedReceiver, intentFilter2);
unUploadFile = getSharedPreferences("un_upload_file", 0);
().putString("description", "未上传的录音文件存放路径").commit();
dirPath = ().getAbsolutePath() + "/";
}
@Override
public void onDestroy() {
();
(getApplicationContext(), "进程被关闭,无法继续录音,请打开录音服务", ).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
(TAG1, "空闲状态" + incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
(TAG1, "空闲");
if (recorder != null && isRecording) {
();// 停止录音
();
recorder = null;
("电话", "通话结束,停止录音");
uploadFile(currentFile);
}
isRecording = false;
break;
case TelephonyManager.CALL_STATE_RINGING:
(TAG1, "来电响铃" + incomingNumber);
// 进行初始化
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
(TAG1, "摘机" + (!("") ? incomingNumber : currentCallNum));
initRecord(!("") ? incomingNumber : currentCallNum);
// 开始录音
if (recorder != null) {
();
isRecording = true;
}
default:
break;
}
(state, incomingNumber);
}
}
/**
* 当录音结束后,自动上传录音文件。
* ①网络可用:直接上传;
* ②网络不可用:保存文件路径,待网络可用的时候再进行上传;
* ③上传失败的文件,也保存文件路径,或者重新上传。
*/
public void uploadFile(String file) {
(dirPath + file, dirPath + file + ".zip");
if ((getApplicationContext())) {
//上传文件
// ()
} else {
saveUnUploadFIles(dirPath + file + ".zip");
}
}
/**
* 保存未上传的录音文件
*
* @param file 未上传的录音文件路径
*/
private void saveUnUploadFIles(String file) {
String files = ("unUploadFile", "");
if (("")) {
files = file;
} else {
StringBuilder sb = new StringBuilder(files);
files = (";").append(file).toString();
}
().putString("unUploadFile", files).commit();
}
/**
* 上传因为网络或者其他原因,暂未上传或者上传失败的文件,重新上传
*/
public void uploadUnUploadedFiles() {
//获取当前还未上传的文件,并把这些文件上传
String files = ("unUploadFile", "");
().putString("unUploadFile", "").commit();
if (("")) {
return;
}
String[] fileArry = (";");
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 || !()) {
//文件不存在
return;
}
if (!(getApplicationContext())) {
saveUnUploadFIles(file);
return;
}
Map<String, String> map = new HashMap<String, String>();
("type", "1");
final String url = "";
()//
.addFile("mFile", (), file1)//
.url(url)//
.params(map).build()//
.execute(new StringCallback() {
@Override
public void onResponse(String response, int id) {
(TAG, "成功 response=" + response);
}
@Override
public void onError(Call call, Exception e, int id) {
(TAG, "失败 response=" + ());
saveUnUploadFIles(file);
}
});
}
/**
* 初始化录音机,并给录音文件重命名
*
* @param incomingNumber 通话号码
*/
private void initRecord(String incomingNumber) {
previousStats = TelephonyManager.CALL_STATE_RINGING;
recorder = new MediaRecorder();
();// Microphone
recorder.setOutputFormat();// 设置输出3gp格式
File out = new File(dirPath);
if (!()) {
();
}
(dirPath
+ getFileName((previousStats == TelephonyManager.CALL_STATE_RINGING ? incomingNumber : currentCallNum))
);
();// 设置音频编码格式
try {
();// 做好准备
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 获取录音文件的名称
*
* @param incomingNumber 通话号码
* @return 获取录音文件的名称
*/
private String getFileName(String incomingNumber) {
Date date = new Date(());
currentFile = incomingNumber + " " + (date) + ".mp3";
return currentFile;
}
/**
* 拨号广播接收器,并获取拨号号码
*/
public class OutCallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
(TAG1, "当前手机拨打了电话:" + currentCallNum);
if (().equals()) {
currentCallNum = ();
(TAG1, "当前手机拨打了电话:" + currentCallNum);
} else {
(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 manager = (ConnectivityManager) context
.getSystemService();
(TAG, "CONNECTIVITY_ACTION");
NetworkInfo activeNetwork = ();
if (activeNetwork != null) { // connected to the internet
if (()) {
//当前网络可用
if (() == ) {
// connected to wifi
(TAG, "当前WiFi连接可用 ");
} else if (() == ) {
// connected to the mobile provider's data plan
(TAG, "当前移动网络连接可用 ");
}
uploadUnUploadedFiles();
} else {
(TAG, "当前没有网络连接,请确保你已经打开网络 ");
}
} else { // not connected to the internet
(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();
NetworkInfo mNetworkInfo = ();
if (mNetworkInfo != null) {
return ();
}
}
return false;
}
/**
* 判断WIFI网络是否可用
* @param context
* @return
*/
public static boolean isWifiConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context
.getSystemService();
NetworkInfo mWiFiNetworkInfo = mConnectivityManager
.getNetworkInfo();
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();
NetworkInfo mMobileNetworkInfo = mConnectivityManager
.getNetworkInfo();
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();
NetworkInfo mNetworkInfo = ();
if (mNetworkInfo != null && ()) {
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();
NetworkInfo networkInfo = ();
if (networkInfo == null) {
return netType;
}
int nType = ();
if (nType == ) {
netType = 1;// wifi
} else if (nType == ) {
int nSubType = ();
TelephonyManager mTelephony = (TelephonyManager) context
.getSystemService();
if (nSubType == TelephonyManager.NETWORK_TYPE_UMTS
&& !()) {
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 + ();
if (()) {
zipFilePath += ;
for (File f : ()) {
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);
while ((buffer) != -1) {
(buffer);
}
();
return true;
} catch (FileNotFoundException ex) {
();
} catch (IOException ex) {
();
}
return false;
}
}
/**
* @param srcPath 待压缩的文件或目录
* @param dstPath 压缩后的zip文件
* @return 只要待压缩的文件有一个压缩失败就停止压缩并返回(等价于windows上直接进行压缩)
*/
public static boolean zipFile(String srcPath, String dstPath) {
File srcFile = new File(srcPath);
if (!()) {
return false;
}
byte[] buffer = new byte[BUFF_SIZE];
try {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dstPath));
boolean result = zipFile(zos, "", srcFile, buffer);
();
return result;
} catch (FileNotFoundException ex) {
();
} catch (IOException ex) {
();
}
return false;
}
/**
* @param srcPath 待解压的zip文件
* @param dstPath zip解压后待存放的目录
* @return 只要解压过程中发生错误,就立即停止并返回(等价于windows上直接进行解压)
*/
public static boolean unzipFile(String srcPath, String dstPath) {
if ((srcPath) || (dstPath)) {
return false;
}
File srcFile = new File(srcPath);
if (!() || !().toLowerCase(()).endsWith("zip")) {
return false;
}
File dstFile = new File(dstPath);
if (!() || !()) {
();
}
try {
ZipInputStream zis = new ZipInputStream(new FileInputStream(srcFile));
BufferedInputStream bis = new BufferedInputStream(zis);
ZipEntry zipEntry = null;
byte[] buffer = new byte[BUFF_SIZE];
if (!()) {
dstPath += ;
}
while ((zipEntry = ()) != null) {
String fileName = dstPath + ();
File file = new File(fileName);
File parentDir = ();
if (!()) {
();
}
FileOutputStream fos = new FileOutputStream(file);
while ((buffer) != -1) {
(buffer);
}
();
}
();
();
return true;
} catch (FileNotFoundException ex) {
();
} catch (IOException ex) {
();
}
return false;
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助。