Androidで通話自動録音サービスを実現
この実例では、Androidが通話自動録音サービスを実現するための具体的なコードを共有します。参考にしてください。具体的な内容は以下の通りです。
必要:
①:通話の自動録音;
②:インターフェースがなく、ただ一つのサービスです。
③:録音自動圧縮アップロード;
④:ユーザーがバックグラウンドを整理する時、serviceが殺されてはいけないと要求します。
⑤:安定性:1、ネットワークがない場合;2、アップロードに失敗しました3、サービスのエラー。
ソリューション:
①:通話の自動録音
一つのサービスを起動して、ユーザーの携帯電話の通話状態を監督し、ユーザーが通話状態であることを検知したら、直ちに録音を開始し、通話終了後、録音を停止し、ファイルを保存します。
この機能の前提条件:
1、録音権限、読み書き記憶空間の権限、通話状態の読み取り権限。
2、Serviceは停止されてはいけません。録音できません。
3、起動して起動する(ユーザが起動するたびに自動的にサービスを開くようにしてはいけない)
②:インターフェースがなく、ただ一つのサービスです。
シナリオ①
普通のserviceで、起動放送を傍受し、ユーザーが起動すると、サービスを開始します。しかし、serviceは起動していないことが分かりました。serviceを起動するには、activityが必要です。これを開けなくてもactivityが必要です。
本当にプロジェクトをする時、PMは各種のあなたの理解できない需要を出します。例えば、このシステム、PMはこのアプリケーションが録音サービスだけであることを要求します。インタフェースがあってもいいです。携帯のデスクトップにアプリケーションアイコンが現れてもいいです。したがって、方案①は実行できません。
プラン②
Android携帯はセットの中に補助機能があります。これを利用して強力な機能を実現できます。前提はユーザーが私達の補助機能を開くことです。
③:録音自動圧縮アップロード
アップロードする前にファイルを圧縮処理してからアップロードすればいいです。
④:ユーザーがバックグラウンドを整理する時、serviceが殺されてはいけないと要求します。
殺されないサービスはシステムサービスだけかもしれません。もちろんQQやWeChatのように、彼らが作ったこのような家族用の桶もできます。大手企業はメーカーと協力できるので、彼らの応用は簡単に殺されないです。もちろんこのようにすることを提唱しないで、このようにごみのソフトウェア、Android開発のすばらしい環境を破壊しました。
実際には、サービスをシステムサービスに設定することができますが、ユーザーが補助機能ページでサービスをオフにしない限り、バックグラウンドはクリーンアップされません。私はミニ携帯でテストしましたが、システムレベルのサービスに設定した後、バックグラウンドを整理する時、サービスが殺されても、非常に速く再起動します。興味のある方は試してみてもいいです。
⑤:安定性:1、ネットワークがない場合;2、アップロードに失敗しました3、サービスのエラー
考え方:
ネットワークがない場合は、録音ファイルのアドレスを保存します。アップロード失敗も同じです。ネットワークが切断され、インターフェースが間違っているなど、ネットワークが復旧したときは、再アップロードしてもいいです。録音ファイルは失われません。
コードは簡単です。コメントは詳細です。
プロジェクトの構造:
必要:
①:通話の自動録音;
②:インターフェースがなく、ただ一つのサービスです。
③:録音自動圧縮アップロード;
④:ユーザーがバックグラウンドを整理する時、serviceが殺されてはいけないと要求します。
⑤:安定性:1、ネットワークがない場合;2、アップロードに失敗しました3、サービスのエラー。
ソリューション:
①:通話の自動録音
一つのサービスを起動して、ユーザーの携帯電話の通話状態を監督し、ユーザーが通話状態であることを検知したら、直ちに録音を開始し、通話終了後、録音を停止し、ファイルを保存します。
この機能の前提条件:
1、録音権限、読み書き記憶空間の権限、通話状態の読み取り権限。
2、Serviceは停止されてはいけません。録音できません。
3、起動して起動する(ユーザが起動するたびに自動的にサービスを開くようにしてはいけない)
②:インターフェースがなく、ただ一つのサービスです。
シナリオ①
普通のserviceで、起動放送を傍受し、ユーザーが起動すると、サービスを開始します。しかし、serviceは起動していないことが分かりました。serviceを起動するには、activityが必要です。これを開けなくてもactivityが必要です。
本当にプロジェクトをする時、PMは各種のあなたの理解できない需要を出します。例えば、このシステム、PMはこのアプリケーションが録音サービスだけであることを要求します。インタフェースがあってもいいです。携帯のデスクトップにアプリケーションアイコンが現れてもいいです。したがって、方案①は実行できません。
プラン②
Android携帯はセットの中に補助機能があります。これを利用して強力な機能を実現できます。前提はユーザーが私達の補助機能を開くことです。
③:録音自動圧縮アップロード
アップロードする前にファイルを圧縮処理してからアップロードすればいいです。
④:ユーザーがバックグラウンドを整理する時、serviceが殺されてはいけないと要求します。
殺されないサービスはシステムサービスだけかもしれません。もちろんQQやWeChatのように、彼らが作ったこのような家族用の桶もできます。大手企業はメーカーと協力できるので、彼らの応用は簡単に殺されないです。もちろんこのようにすることを提唱しないで、このようにごみのソフトウェア、Android開発のすばらしい環境を破壊しました。
実際には、サービスをシステムサービスに設定することができますが、ユーザーが補助機能ページでサービスをオフにしない限り、バックグラウンドはクリーンアップされません。私はミニ携帯でテストしましたが、システムレベルのサービスに設定した後、バックグラウンドを整理する時、サービスが殺されても、非常に速く再起動します。興味のある方は試してみてもいいです。
⑤:安定性:1、ネットワークがない場合;2、アップロードに失敗しました3、サービスのエラー
考え方:
ネットワークがない場合は、録音ファイルのアドレスを保存します。アップロード失敗も同じです。ネットワークが切断され、インターフェースが間違っているなど、ネットワークが復旧したときは、再アップロードしてもいいです。録音ファイルは失われません。
コードは簡単です。コメントは詳細です。
プロジェクトの構造:
<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;
}
}
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。