Привет,
Я работаю над решением для Android, которое будет записывать вызовы (как исходящие, так и входящие) и в дальнейшем обрабатывать записанные данные (в конечной точке моего приложения нет данных аудиофайлов).будет удерживаться в памяти телефона).Я реализовал BroadcastReceiver с PhoneStateListener.LISTEN_CALL_STATE, который запускает службу записи, если состояние CALL_STATE_OFFHOOK.Затем в сервисе я запускаю новый поток, который пытается записать вызов, и другой BroadcastReceiver с PhoneStateListener.LISTEN_CALL_STATE, который вызывает метод для остановки записи, если состояние телефона изменяется на TelephonyManager.CALL_STATE_IDLE.
Создан аудиофайлпустой.Исключение выдается при вызове метода Recorder.stop () в моем сервисе.Где ошибка?Что я могу сделать лучше?
Первый (автономный) BroadcastReceiver:
package com.piotr.callerrecogniser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
public class CallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
MyPhoneStateListener phoneListener = new MyPhoneStateListener(context);
TelephonyManager telephony = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
class MyPhoneStateListener extends PhoneStateListener {
public static final String tag = "CallerRecogniser - CallReceiver";
private Context context;
MyPhoneStateListener(Context c) {
super();
context = c;
}
public static final int NOTIFICATION_ID_RECEIVED = 0x1221;
@Override
public void onCallStateChanged(int state, String incomingNumber) {
SharedPreferences preferences = context.getSharedPreferences("CallReceiver", Context.MODE_PRIVATE);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
break;
//If call is answered, run recording service. Also pass "phone_number" variable with incomingNumber to shared prefs, so service will be able to access that via shared prefs.
case TelephonyManager.CALL_STATE_OFFHOOK: // The call is answered
String phone_number = preferences.getString("phone_number",null);
Intent serv = new Intent(context,
CallRecordingService.class);
serv.putExtra("number", phone_number);
context.startService(serv);
break;
//If phone is ringing, save phone_number. This is done because incomingNumber is not saved on CALL_STATE_OFFHOOK
case TelephonyManager.CALL_STATE_RINGING:
SharedPreferences.Editor editor = preferences.edit();
editor.putString("phone_number", incomingNumber);
editor.commit();
break;
}
}
}
}
Служба:
package com.piotr.callerrecogniser;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
public class CallRecordingService extends Service implements Runnable {
CallerRecogniserDB database = new CallerRecogniserDB(this);
String phoneNumber;
MediaRecorder recorder;
private final Handler mHandler = new Handler();
private final BroadcastReceiver myCallStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
MyPhoneStateListener phoneListener = new MyPhoneStateListener(
context);
TelephonyManager telephony = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(phoneListener,
PhoneStateListener.LISTEN_CALL_STATE);
}
class MyPhoneStateListener extends PhoneStateListener {
private Context context;
MyPhoneStateListener(Context c) {
super();
context = c;
}
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
if (recording) {
NotificationManager mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification not;
not = new Notification(R.drawable.ic_launcher,
"Phone call being processed",
System.currentTimeMillis());
Intent notIntent = new Intent();
PendingIntent contentIntent = PendingIntent
.getActivity(context, 0, notIntent, 0);
not.setLatestEventInfo(context, "CallRecordingService",
"Notification from idle",
contentIntent);
mNotificationManager.notify(NOTIFICATION_ID_RECEIVED,
not);
stopRecording();
}
break;
}
}
}
};
private static boolean recording = false;
private String INCOMING_CALL_ACTION = "android.intent.action.PHONE_STATE";
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
IntentFilter intentToReceiveFilter = new IntentFilter();
intentToReceiveFilter.addAction(INCOMING_CALL_ACTION);
this.registerReceiver(myCallStateReceiver, intentToReceiveFilter, null,
mHandler);
Thread aThread = new Thread(this);
aThread.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
phoneNumber = intent.getExtras().getString("number");
return START_STICKY;
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
public static final int NOTIFICATION_ID_RECEIVED = 0x1221;
@Override
public void run() {
Looper.myLooper();
Looper.prepare();
// TODO Auto-generated method stub
database.open();
String[] numbers = database.getData(CallerRecogniserDB.KEY_NUMBER);
int index = -1;
outside_for: for (int i = 0; i < numbers.length; i++) {
String[] splitted = numbers[i].split(",");
for (String nu : splitted) {
if (nu.equals(phoneNumber)) {
index = i;
break outside_for;
}
}
}
database.close();
if (index >= 0) { // Phone number is in a database and it's state==0 =>
// it has no data recorded
// Notification that call is recorded
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/" + phoneNumber + ".3gp");
try {
recorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
recording = true;
recorder.start();
}
}
void stopRecording() {
// Then clean up with when it hangs up:
recorder.stop();
recorder.release();
recording=false;
}
}
Разрешения:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Выход исключения:
11-28 14:28:44.385: E/MediaRecorder(22438): stop called in an invalid state: 8
11-28 14:28:44.400: D/AndroidRuntime(22438): Shutting down VM
11-28 14:28:44.425: W/dalvikvm(22438): threadid=1: thread exiting with uncaught exception (group=0x4001e578)
11-28 14:28:44.435: E/AndroidRuntime(22438): FATAL EXCEPTION: main
11-28 14:28:44.435: E/AndroidRuntime(22438): java.lang.IllegalStateException
11-28 14:28:44.435: E/AndroidRuntime(22438): at android.media.MediaRecorder.stop(Native Method)
11-28 14:28:44.435: E/AndroidRuntime(22438): at com.piotr.callerrecogniser.CallRecordingService.stopRecording(CallRecordingService.java:159)
11-28 14:28:44.435: E/AndroidRuntime(22438): at com.piotr.callerrecogniser.CallRecordingService$1$MyPhoneStateListener.onCallStateChanged(CallRecordingService.java:65)
11-28 14:28:44.435: E/AndroidRuntime(22438): at android.telephony.PhoneStateListener$2.handleMessage(PhoneStateListener.java:369)
11-28 14:28:44.435: E/AndroidRuntime(22438): at android.os.Handler.dispatchMessage(Handler.java:99)
11-28 14:28:44.435: E/AndroidRuntime(22438): at android.os.Looper.loop(Looper.java:123)
11-28 14:28:44.435: E/AndroidRuntime(22438): at android.app.ActivityThread.main(ActivityThread.java:3691)
11-28 14:28:44.435: E/AndroidRuntime(22438): at java.lang.reflect.Method.invokeNative(Native Method)
11-28 14:28:44.435: E/AndroidRuntime(22438): at java.lang.reflect.Method.invoke(Method.java:507)
11-28 14:28:44.435: E/AndroidRuntime(22438): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847)
11-28 14:28:44.435: E/AndroidRuntime(22438): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
11-28 14:28:44.435: E/AndroidRuntime(22438): at dalvik.system.NativeStart.main(Native Method)