package cri.sanity;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.text.TextUtils;
import cri.sanity.util.*;
public class SmsReceiver extends BroadcastReceiver
{
private static final char SEP = Conf.SMS_SEP;
private static final String SEP_S = SEP + "";
private static final String UNSEP = Conf.SMS_UNSEP;
private static final String UNSLASH = Conf.SMS_UNSLASH;
private FileWriter logFile;
private String idTTS;
private static String anonym, unknown;
private static CallFilter cf;
// used by TTS and Blocker
public static final CallFilter callFilter() { return cf==null? new CallFilter() : cf; }
@Override
public void onReceive(Context ctx, Intent i)
{
if(i==null || !A.isEnabled()) return;
final Bundle extras = i.getExtras();
if(extras == null) return;
Object[] pdus = (Object[])extras.get("pdus");
if(pdus==null || pdus.length<=0) return;
final AudioManager am = A.audioMan();
final boolean tts = A.is(K.TTS_SMS) && !MainService.isRunning()
&& (!A.is(K.TTS_SKIP) || am.getRingerMode()==AudioManager.RINGER_MODE_NORMAL)
&& (!A.is(K.TTS_HEADSET) || am.isWiredHeadsetOn() || am.isBluetoothA2dpOn() || am.isBluetoothScoOn());
idTTS = null;
if(A.is(K.BLOCK_SMS) && A.is(K.BLOCK_FILTER)) {
pdus = block(pdus, extras, getSectTTS(tts));
if(cf != null) cf.close();
if(pdus == null) return;
}
else if(tts) filterTTS(pdus);
if(pdus!=null && pdus.length>0 && A.is(K.SMS_ALERT))
smsAlert(SmsMessage.createFromPdu((byte[])pdus[0]));
if(A.empty(idTTS)) return;
new TTS(new String(idTTS), false, false, true);
}
private Object[] block(Object[] pdus, Bundle extras, String sectTTS)
{
if(cf == null) cf = new CallFilter();
final int max = A.geti(K.BLOCK_SMS_MAX);
final int n = pdus.length;
final Map<String,Integer> freeNames = sectTTS==null? null : new HashMap<String,Integer>(n);
final String sectBlock = A.is(K.BLOCK_SMS_FILTER) ? "blocksms" : "block";
final boolean log = max != 0;
final boolean[] blocked = new boolean[n];
SmsMessage msg; String num = null;
int blockCount = 0;
for(int i=0; i<n; i++) {
try {
msg = SmsMessage.createFromPdu((byte[])pdus[i]);
num = msg.getDisplayOriginatingAddress();
if(!cf.includes(num, sectBlock, false)) throw new Exception();
if(log) log(num, cf.searchName(num), msg.getMessageBody(), msg.getTimestampMillis());
blocked[i] = true;
++blockCount;
} catch(Exception e) {
blocked[i] = false;
if(freeNames != null) filterTTS(cf, sectTTS, num, freeNames);
}
}
if(freeNames!=null && !freeNames.isEmpty())
idTTS = TextUtils.join(", ", freeNames.keySet());
if(blockCount <= 0)
return pdus;
if(log) {
logClose();
if(max > 0) logTrunc(max, blockCount + A.geti(K.SMS_COUNT));
}
if(A.is(K.BLOCK_SMS_NOTIFY)) Blocker.notification(true);
if(blockCount == n) {
abortBroadcast();
return null;
}
final Object[] free = new Object[n - blockCount];
for(int k=0, i=0; i<n; i++)
if(!blocked[i]) free[k++] = pdus[i];
extras.putSerializable("pdus", free);
setResultExtras(extras);
return free;
}
private void log(String num, String name, String body, long time) throws IOException
{
if(logFile == null) logFile = new FileWriter(smsFn(), true);
if(num == null) num = "";
if(name == null) name = "";
body = body.replace("\r","").replace("\\",UNSLASH).replace("\n","\\n").replace(SEP_S, UNSEP);
logFile.append(A.date(time) + SEP + name + SEP + num + SEP + body + '\n');
}
private void logClose()
{
if(logFile == null) return;
try {
logFile.flush();
logFile.close();
} catch(IOException e) {}
logFile = null;
}
private static void logTrunc(int max, int cnt)
{
if(cnt < max+max/2) {
A.putc(K.SMS_COUNT, cnt);
return;
}
boolean done = false;
BufferedReader in = null;
FileWriter fw = null;
String fn = null;
try { fn = smsFn(); } catch(Exception e) { return; }
final String tmp = fn + ".tmp";
try {
in = new BufferedReader(new FileReader(fn), 8192);
for(int n=cnt-max; --n>=0;)
in.readLine();
fw = new FileWriter(tmp, false);
for(int i=0; i<max; i++)
fw.append(in.readLine()+'\n');
A.putc(K.SMS_COUNT, max);
done = true;
} catch(Exception e) {}
try { in.close(); } catch(Exception e) {}
try { fw.flush(); fw.close(); } catch(Exception e) {}
if(!done) {
A.putc(K.SMS_COUNT, cnt);
try { new File(tmp).delete(); } catch(Exception e) {}
return;
}
try {
final File f = new File(fn);
if(f.delete()) new File(tmp).renameTo(f);
else throw new Exception("SmsReceiver");
} catch(Exception e) {
A.putc(K.SMS_COUNT, "SmsReceiver".equals(e.getMessage()) ? cnt : 0);
try { new File(tmp).delete(); } catch(Exception e2) {}
}
}
private void filterTTS(Object[] pdus) {
if(cf == null) cf = new CallFilter();
final int n = pdus.length;
final Map<String,Integer> names = new HashMap<String,Integer>(n);
final String sectTTS = getSectTTS(true);
for(int i=0; i<n; i++) {
try {
filterTTS(cf, sectTTS, SmsMessage.createFromPdu((byte[])pdus[i]).getDisplayOriginatingAddress(), names);
} catch(Exception e) {}
}
cf.close();
idTTS = names.isEmpty() ? null : TextUtils.join(", ", names.keySet());
}
private static void filterTTS(CallFilter cf, String sect, String num, Map<String,Integer> map) {
if(!cf.includes(num, sect, true)) return;
if(A.empty(num)) {
if(anonym == null) anonym = A.gets(K.TTS_ANONYM);
if(anonym.length() > 0) map.put(anonym, 1);
} else if(!isTelNum(num)) {
map.put(num, 1);
} else {
final String name = cf.searchName(num);
if(!A.empty(name))
map.put(name, 1);
else {
if(unknown == null) unknown = A.gets(K.TTS_UNKNOWN);
if(unknown.length() > 0) map.put(unknown, 1);
}
}
}
private static void smsAlert(SmsMessage sms) {
final String from = sms.getDisplayOriginatingAddress();
final String name = new CallFilter().searchName(from);
final String body = sms.getMessageBody();
BlankActivity.force = true;
BlankActivity.postSingleton(new Runnable(){ public void run(){
Alert.activity = BlankActivity.getInstance();
Alert.msg(
String.format(A.s(R.string.msg_sms_from), A.empty(name)? from : name),
body,
new Alert.Click(){ public void on(){
Intent i = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:"+from));
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
A.app().startActivity(i);
}},
null,
Alert.REPLY
).setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
BlankActivity.getInstance().postFinish();
Alert.activity = null;
}
});
}});
Intent i = new Intent(A.app(), BlankActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
A.app().startActivity(i);
}
private static boolean isTelNum(String num) {
for(char c : num.toCharArray())
if(c!='+' && c!='-' && c<'0' && c>'9') return false;
return true;
}
private static String smsFn() { return A.sdcardDir()+'/'+Conf.SMS_FN; }
private static String getSectTTS(boolean tts) { return tts? A.is(K.TTS_SMS_FILTER)? "ttsms" : "tts" : null; }
}