package kz.virtex.htc.tweaker.mods;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import java.io.File;
import java.util.Locale;
import kz.virtex.htc.tweaker.Const;
import kz.virtex.htc.tweaker.Misc;
import kz.virtex.htc.tweaker.R;
import kz.virtex.htc.tweaker.TweakerService;
import kz.virtex.htc.tweaker.XMain;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.XModuleResources;
import android.os.AsyncResult;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.Connection;
import com.htc.widget.HtcIconButton;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class Recorder
{
// AndroidAppHelper.currentApplication(). Then, use that context to create a
// context for your own app (see Context.createContext(...))
private static Boolean is_incoming;
private static Connection mConnection;
private static boolean CallRecording;
private static boolean AutoRecording;
private static boolean RecordIn;
private static boolean RecordOut;
private static int RecordCaller;
private static int AutoRecordingStorage;
private static int SlotToRecord;
public static void hookIsEnableAudioRecord(final LoadPackageParam paramLoadPackageParam)
{
Misc.x("hookIsEnableAudioRecord");
findAndHookMethod("com.htc.soundrecorder.PausableAudioRecorder", paramLoadPackageParam.classLoader, "isEnableAudioRecord", new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
// Misc.x("isEnableAudioRecord: " + param.getResult());
// XposedHelpers.setBooleanField(param.thisObject,
// "mIsInCallRecording", true);
// param.setResult(Boolean.valueOf(true));
}
});
findAndHookMethod("com.htc.soundrecorder.PausableAudioRecorder", paramLoadPackageParam.classLoader, "isInCallRecording", String.class, new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
// param.setResult(Boolean.valueOf(true));
}
});
findAndHookMethod("com.htc.soundrecorder.InCallRecorder", paramLoadPackageParam.classLoader, "start", new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
// Misc.x("InCallRecorder: invoke");
}
});
findAndHookMethod("com.htc.soundrecorder.PausableAudioRecorder", paramLoadPackageParam.classLoader, "initAudioRecorder", new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
// Object mRecorder =
// XposedHelpers.getObjectField(param.thisObject, "mRecorder");
// XposedHelpers.callMethod(mRecorder, "setAudioSource", 4);
}
});
}
public static void hookAudioRecord()
{
Misc.x("hookAudioRecord");
final Class<?> AudioRecord = XposedHelpers.findClass("android.media.AudioRecord", null);
XposedHelpers.findAndHookConstructor(AudioRecord, int.class, int.class, int.class, int.class, int.class, new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
// Misc.x("AudioRecord");
// param.args[0] = 4;
}
});
XposedHelpers.findAndHookMethod("android.media.MediaRecorder", null, "setAudioSource", int.class, new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
// Misc.x("MediaRecorder want's source" + param.args[0]);
// param.args[0] = 4;
}
});
XposedHelpers.findAndHookMethod(AudioRecord, "audioParamCheck", int.class, int.class, int.class, int.class, new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
// Misc.x("audioParamCheck");
// param.args[0] = 4;
}
});
}
public static void hookPausableAudioRecorderStart(final LoadPackageParam paramLoadPackageParam)
{
findAndHookMethod("com.htc.soundrecorder.PausableAudioRecorder", paramLoadPackageParam.classLoader, "start", "long", "java.lang.String", "java.lang.String", new XC_MethodHook()
{
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
int i = Integer.parseInt(preferences.getString("encodeOption", "1"));
param.args[1] = "audio/amr";
if (i == 1) {
param.args[1] = "audio/aac";
}
if (i == 2) {
param.args[1] = "audio/amr-wb";
}
}
});
}
private static boolean isToAutoRecord(Context paramContext, ClassLoader classLoader)
{
CallRecording = Misc.toBoolean(Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC, 0));
AutoRecording = Misc.toBoolean(Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC_AUTO, 0));
RecordIn = Misc.toBoolean(Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC_AUTO_FILTER_IN, 1));
RecordOut = Misc.toBoolean(Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC_AUTO_FILTER_OUT, 1));
RecordCaller = Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC_AUTO_CALLER, 0);
AutoRecordingStorage = Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC_AUTO_STORAGE, 1);
SlotToRecord = Settings.System.getInt(paramContext.getContentResolver(), Const.TWEAK_CALL_REC_AUTO_SLOT, 0);
if (!CallRecording)
return false;
if (!AutoRecording)
return false;
// Record by slot
if (SlotToRecord != 0) {
if (mConnection != null) {
int PhoneType = mConnection.getCall().getPhone().getPhoneType();
Class<?> PhoneUtils = XposedHelpers.findClass("com.android.phone.PhoneUtils", classLoader);
int slot = (Integer) XposedHelpers.callStaticMethod(PhoneUtils, "getSimSlotTypeByPhoneType", PhoneType);
if (slot != SlotToRecord) {
return false;
}
}
}
if (is_incoming != null) {
if (!RecordIn && is_incoming) // Если не записывать входящие и
// звонок
// входящий
return false;
if (!RecordOut && !is_incoming) // Если не записывать исходящие и
// звонок исходящий
return false;
}
String localName = null;
if (mConnection != null) {
Call localCall = mConnection.getCall();
Connection localConnection = localCall.getEarliestConnection();
if (localConnection != null) {
Object localObject = localConnection.getUserData();
if ((localObject instanceof CallerInfo)) {
CallerInfo localCallerInfo = (CallerInfo) localObject;
localName = localCallerInfo.name;
}
}
}
// Если только из записной книжки и имя не определено
if (RecordCaller == 1 && localName == null)
return false;
// Если только неизвестные номера и имя задано
if (RecordCaller == 2 && localName != null)
return false;
// Если записывать всех
if (RecordCaller == 0)
return true;
// Во всех остальных случая возвращаем true
return true;
}
// Метод для разрешения записи с телефонной линии
// Меняет статическую переменную в HtcFeatureList
public static void hookEnableCallRecording(final LoadPackageParam paramLoadPackageParam)
{
findAndHookMethod("com.android.phone.PhoneApp", paramLoadPackageParam.classLoader, "onCreate", new XC_MethodHook()
{
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
ContentResolver cr = (ContentResolver) XposedHelpers.callMethod(param.thisObject, "getContentResolver");
Class<?> Features = XposedHelpers.findClass("com.android.phone.HtcFeatureList", paramLoadPackageParam.classLoader);
boolean CallRecording = Misc.toBoolean(Settings.System.getInt(cr, Const.TWEAK_CALL_REC, 0));
if (CallRecording) {
XposedHelpers.setStaticBooleanField(Features, "FEATURE_SUPPORT_VOICE_RECORDING", true);
// XposedHelpers.setStaticBooleanField(Features,
// "FEATURE_DISABLE_GSM_VOICE_RECORDING", false);
}
}
});
// Record button
findAndHookMethod("com.android.phone.InCallScreen", paramLoadPackageParam.classLoader, "updateScreen", new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
Context context = (Context) XposedHelpers.callMethod(param.thisObject, "getApplicationContext");
boolean AutoRecording = Misc.toBoolean(Settings.System.getInt(context.getContentResolver(), Const.TWEAK_CALL_REC_AUTO, 0));
boolean Recording = Misc.toBoolean(Settings.System.getInt(context.getContentResolver(), Const.TWEAK_CALL_REC, 0));
if (Recording && !AutoRecording) {
LinearLayout screen = (LinearLayout) XposedHelpers.getObjectField(param.thisObject, "mControlPanel");
final Object mControlPanel = XposedHelpers.getObjectField(param.thisObject, "mControlPanel");
Object mInCallScreenMode = XposedHelpers.getObjectField(param.thisObject, "mInCallScreenMode");
boolean isSingleAlive = (Boolean) XposedHelpers.callMethod(mInCallScreenMode, "isSingleAlive");
boolean isConference = (Boolean) XposedHelpers.callMethod(mInCallScreenMode, "isConference");
boolean isMultiple = (Boolean) XposedHelpers.callMethod(mInCallScreenMode, "isMultiple");
final XModuleResources modRes = XModuleResources.createInstance(XMain.MODULE_PATH, null);
// Russsian language fix to fit 4 buttons
if (Locale.getDefault().getLanguage().equalsIgnoreCase("ru")) {
Object mMuteButton = XposedHelpers.getObjectField(mControlPanel, "mMuteButton");
XposedHelpers.callMethod(mMuteButton, "setText", "Микрофон");
}
Button recordButton;
recordButton = (Button) screen.findViewById(R.id.RecordingButton);
Class<?> VoiceRecorderHelper = XposedHelpers.findClass("com.android.phone.util.VoiceRecorderHelper", paramLoadPackageParam.classLoader);
final Object Recorder = XposedHelpers.callStaticMethod(VoiceRecorderHelper, "getInstance");
if (recordButton == null) {
recordButton = new HtcIconButton(context);
recordButton.setId(R.id.RecordingButton);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, LayoutParams.FILL_PARENT, 1.0f);
recordButton.setLayoutParams(layoutParams);
recordButton.setText(modRes.getString(R.string.menu_start_record));
XposedHelpers.callMethod(recordButton, "setIconDrawable", modRes.getDrawable(R.drawable.icon_btn_recorder_on_dark));
recordButton.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View paramView)
{
PowerManager pm = (PowerManager) paramView.getContext().getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();
if (isScreenOn) {
if ((Boolean) XposedHelpers.callMethod(Recorder, "isRecording")) {
XposedHelpers.callMethod(Recorder, "stop");
updateRecordButton(paramView, true, modRes);
} else {
XposedHelpers.callMethod(Recorder, "start");
updateRecordButton(paramView, false, modRes);
}
}
}
});
screen.addView(recordButton);
}
recordButton.setVisibility(View.GONE);
updateRecordButton(recordButton, true, modRes);
if (isSingleAlive || isConference || isMultiple) {
recordButton.setVisibility(View.VISIBLE);
updateRecordButton(recordButton, true, modRes);
} else {
recordButton.setVisibility(View.GONE);
updateRecordButton(recordButton, false, modRes);
}
XposedHelpers.callMethod(mControlPanel, "updateIconButtonTextSize");
}
}
});
}
private static void updateRecordButton(Object button, boolean startRecordState, XModuleResources modRes)
{
if (startRecordState) {
XposedHelpers.callMethod(button, "setPressed", false);
XposedHelpers.callMethod(button, "setColorOn", false);
XposedHelpers.callMethod(button, "setText", modRes.getString(R.string.menu_start_record));
XposedHelpers.callMethod(button, "setIconDrawable", modRes.getDrawable(R.drawable.icon_btn_recorder_on_dark));
} else {
XposedHelpers.callMethod(button, "setPressed", true);
XposedHelpers.callMethod(button, "setColorOn", true);
XposedHelpers.callMethod(button, "setText", modRes.getString(R.string.menu_stop_record));
XposedHelpers.callMethod(button, "setIconDrawable", modRes.getDrawable(R.drawable.icon_btn_recorder_stop_on_dark));
}
}
// Метод по реализации начала и остановки автозаписи
public static void hookAutomateCallRecording(final LoadPackageParam paramLoadPackageParam)
{
// Начать автозапись
findAndHookMethod("com.android.phone.CallNotifier", paramLoadPackageParam.classLoader, "onCallConnected", "android.os.AsyncResult", new XC_MethodHook()
{
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
AsyncResult asyncResult = (AsyncResult) param.args[0];
mConnection = (Connection) asyncResult.result;
Object application = XposedHelpers.getObjectField(param.thisObject, "mApplication");
Context context = (Context) XposedHelpers.callMethod(application, "getApplicationContext");
is_incoming = mConnection.isIncoming();
// Если условия автозаписи нас устраивают
if (isToAutoRecord(context, paramLoadPackageParam.classLoader)) {
Class<?> VoiceRecorderHelper = XposedHelpers.findClass("com.android.phone.util.VoiceRecorderHelper", paramLoadPackageParam.classLoader);
Object Recorder = XposedHelpers.callStaticMethod(VoiceRecorderHelper, "getInstance");
if (!(Boolean) XposedHelpers.callMethod(Recorder, "isRecording")) {
XposedHelpers.callMethod(Recorder, "start");
}
}
}
});
// заканчивать автозапись. Должен срабатывать автоматически, но иногда
// система не закрывает звонок
// поэтому делаем это еще раз, так как ошибок это не вызывает
findAndHookMethod("com.android.phone.CallNotifier", paramLoadPackageParam.classLoader, "onDisconnect", "android.os.AsyncResult", new XC_MethodHook()
{
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
Object application = XposedHelpers.getObjectField(param.thisObject, "mApplication");
Context context = (Context) XposedHelpers.callMethod(application, "getApplicationContext");
// Если условия автозаписи у нас сработали
if (isToAutoRecord(context, paramLoadPackageParam.classLoader)) {
Context tweakContext = context.createPackageContext(Const.PACKAGE_NAME, Context.CONTEXT_IGNORE_SECURITY);
Intent intent = new Intent(tweakContext, TweakerService.class);
intent.setAction(TweakerService.ACTION_CLEANUP_RECORDS);
tweakContext.startService(intent);
is_incoming = null;
mConnection = null;
Class<?> VoiceRecorderHelper = XposedHelpers.findClass("com.android.phone.util.VoiceRecorderHelper", paramLoadPackageParam.classLoader);
Object Recorder = XposedHelpers.callStaticMethod(VoiceRecorderHelper, "getInstance");
// Проверяем еще раз на то, что запись идет
if ((Boolean) XposedHelpers.callMethod(Recorder, "isRecording")) {
XposedHelpers.callMethod(Recorder, "stop");
}
}
}
});
}
// Переименовываем файл записи
public static void hookAutomateCallRecordingFilename(final LoadPackageParam paramLoadPackageParam)
{
findAndHookMethod("com.android.phone.util.VoiceRecorderHelper", paramLoadPackageParam.classLoader, "getIncallRecordingFileName", "com.android.internal.telephony.CallManager", new XC_MethodHook()
{
protected void afterHookedMethod(final MethodHookParam param) throws Throwable
{
// Context context = mContext;
/*
* Context context = (Context)
* XposedHelpers.getObjectField(param.thisObject, "mContext");
*
*
* boolean CallRecording =
* Misc.toBoolean(Settings.System.getInt(
* context.getContentResolver(), Const.TWEAK_CALL_REC, 0));
* boolean AutoRecording =
* Misc.toBoolean(Settings.System.getInt(
* context.getContentResolver(), Const.TWEAK_CALL_REC_AUTO, 0));
* int AutoRecordingStorage =
* Settings.System.getInt(context.getContentResolver(),
* Const.TWEAK_CALL_REC_AUTO_STORAGE, 1);
*/
if (CallRecording && AutoRecording && is_incoming != null) {
String recordFile = (String) param.getResult();
// Если звонок входящий
if (is_incoming) {
if (AutoRecordingStorage == 1) {
recordFile = Const.AUTO_REC_INCOMING + recordFile;
} else {
recordFile = Const.AUTO_REC_MAIN + recordFile + "-IN";
}
} else {
if (AutoRecordingStorage == 1) {
recordFile = Const.AUTO_REC_OUTGOING + recordFile;
} else {
recordFile = Const.AUTO_REC_MAIN + recordFile + "-OUT";
}
}
param.setResult(recordFile);
}
}
});
}
public static void getStorageRoot(final LoadPackageParam paramLoadPackageParam)
{
findAndHookMethod("com.htc.soundrecorder.util.FileNameGen", paramLoadPackageParam.classLoader, "getRootPathFile", new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
Context context = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext");
boolean CallRecording = Misc.toBoolean(Settings.System.getInt(context.getContentResolver(), Const.TWEAK_CALL_REC, 0));
boolean AutoRecording = Misc.toBoolean(Settings.System.getInt(context.getContentResolver(), Const.TWEAK_CALL_REC_AUTO, 0));
int AutoRecordingStorage = Settings.System.getInt(context.getContentResolver(), Const.TWEAK_CALL_REC_AUTO_STORAGE, 1);
if (CallRecording && AutoRecording) {
File RootPathFile = (File) param.getResult();
File recordPathIn;
File recordPathOut;
if (AutoRecordingStorage == 1) {
recordPathIn = new File(RootPathFile.getPath() + "/" + Const.AUTO_REC_INCOMING);
recordPathOut = new File(RootPathFile.getPath() + "/" + Const.AUTO_REC_OUTGOING);
} else {
recordPathIn = new File(RootPathFile.getPath() + "/" + Const.AUTO_REC_MAIN);
recordPathOut = new File(RootPathFile.getPath() + "/" + Const.AUTO_REC_MAIN);
}
if (!recordPathIn.exists()) {
if (!recordPathIn.mkdirs()) {
XposedBridge.log("Problem creating incoming folder");
}
}
if (!recordPathOut.exists()) {
if (!recordPathOut.mkdirs()) {
XposedBridge.log("Problem creating outgoing folder");
}
}
File nomedia = new File(RootPathFile.getPath() + "/" + Const.AUTO_REC_MAIN, ".nomedia");
if (!nomedia.exists()) {
if (!nomedia.createNewFile()) {
XposedBridge.log("Problem creating nomedia file");
}
}
}
}
});
}
}