寫在前面:
剛學(xué)習(xí)Android開發(fā)就很想做這么一個(gè)工具了檀训。最近終于用eclipse把代碼敲出來了,寫博客記錄之埂软。
首先說下整體思路如下:
- 后臺(tái)開啟一個(gè)Servic通過ContentObserver監(jiān)聽通話記錄表的變化锈遥。
- 如果有變化則通過代碼判斷是否是剛產(chǎn)生的未接來電。
- 需要發(fā)生短信勘畔,則調(diào)用發(fā)送短信的代碼迷殿。
我遇到的問題:
- 我監(jiān)聽的位置是“CallLog.Calls.CONTENT_URI”,然而我卻發(fā)現(xiàn)ContentObserver的onChange方法被頻繁觸發(fā)咖杂。(即使沒有產(chǎn)生通話電記錄)
- 發(fā)生短信為了防止發(fā)送失敗,注冊(cè)短信發(fā)生狀態(tài)的廣播接收蚊夫。通過intent傳遞電話號(hào)碼和短信發(fā)生內(nèi)容诉字。然而測試中卻發(fā)生intent中獲取到的值都是第一次添加的值。(并不會(huì)更新)
問題的不優(yōu)雅解決:(希望得到前輩們指點(diǎn),優(yōu)雅地解決這兩個(gè)問題)
- 既然ContentObserver的onChange方法被頻繁觸發(fā)壤圃,那么多一些判斷陵霉,判斷是否是剛發(fā)生的未接來電記錄是則往下運(yùn)行,不是則忽略伍绳。
- 既然intent中獲取的數(shù)據(jù)不準(zhǔn)確踊挠,那么就換個(gè)地方獲取數(shù)據(jù)。我選擇了MyApplication做數(shù)據(jù)中轉(zhuǎn)站冲杀。
關(guān)鍵代碼片段:
package com.zji.service;
import java.util.Date;
import com.zji.activity.MyApplication;
import com.zji.broadcase.SendMessageReceiver;
import com.zji.db.MyDatabaseHelper;
import com.zji.utils.SendMessage;
import com.zji.utils.Timer;
import com.zji.utils.WriteAndRead;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Handler;
import android.os.IBinder;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.widget.Toast;
/**
* 后臺(tái)運(yùn)行的服務(wù)效床,負(fù)責(zé)開啟監(jiān)聽通話記錄表的變化
* @author phlofy
* @date 2016年3月3日 下午2:13:29
*/
public class MainService extends Service{
MyContentObserver mMyContentObserver;
SendMessageReceiver mSendMessageReceiver;
public static boolean isWorking = false; // 方便MainFragment知道是否開啟后臺(tái)監(jiān)聽服務(wù)
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 注冊(cè)通話記錄表監(jiān)聽
mMyContentObserver = new MyContentObserver(this, new Handler());
this.getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, false, mMyContentObserver);
// 注冊(cè)短信發(fā)送狀態(tài)監(jiān)聽
IntentFilter intentFilter = new IntentFilter("SENT_SMS_ACTION");
mSendMessageReceiver = new SendMessageReceiver();
this.registerReceiver(mSendMessageReceiver, intentFilter);
isWorking = true;
try{
Toast.makeText(this, "服務(wù)開始運(yùn)行", Toast.LENGTH_LONG).show();
}catch(Exception e){}
}
@Override
public void onDestroy() {
super.onDestroy();
// 注銷兩個(gè)監(jiān)聽
this.getContentResolver().unregisterContentObserver(mMyContentObserver);
this.unregisterReceiver(mSendMessageReceiver);
isWorking = false;
try{
Toast.makeText(this, "服務(wù)停止運(yùn)行", Toast.LENGTH_LONG).show();
}catch(Exception e){}
}
}
/**
* 通話記錄表變化的監(jiān)聽者
* @author Administrator
*
*/
class MyContentObserver extends ContentObserver{
Context context;
MyDatabaseHelper db;
SharedPreferences preferences;
SharedPreferences.Editor editor;
public MyContentObserver(Context context, Handler handler) {
super(handler);
this.context = context;
db = new MyDatabaseHelper(context);
preferences = context.getSharedPreferences("autosend", Context.MODE_WORLD_READABLE);
editor = preferences.edit();
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
/****************獲取到通話記錄表的最新一條消息******************/
Cursor cursor = context.getContentResolver().query(CallLog.Calls.CONTENT_URI, new String[]{Calls.NUMBER,Calls.CACHED_NAME,Calls.DATE,Calls.TYPE}, Calls.TYPE+" = ?", new String[]{Calls.MISSED_TYPE+""}, Calls.DEFAULT_SORT_ORDER);
cursor.moveToFirst();
String name = cursor.getString(cursor.getColumnIndex(Calls.CACHED_NAME));
String number = cursor.getString(cursor.getColumnIndex(Calls.NUMBER));
long date = cursor.getLong(cursor.getColumnIndex(Calls.DATE));
int type = cursor.getInt(cursor.getColumnIndex(Calls.TYPE));
if(cursor != null){
cursor.close();
}
/**
* 判斷該未接來電是否是該軟件安裝后發(fā)生。
* 防止沒有未接來電权谁,但onChange還是被執(zhí)行的情況剩檀。
* 解決軟件第一次安裝后onChange被觸發(fā)自動(dòng)發(fā)送一條短信問題
*/
long lifeStart = preferences.getLong("life_start", 0); //試圖獲取軟件安裝時(shí)間
if(lifeStart == 0){
// 為0說明軟件第一次執(zhí)行,記錄此時(shí)時(shí)間為軟件安裝時(shí)間
editor.putLong("life_start", new Date().getTime());
editor.commit();
}
if(lifeStart == 0 || date < lifeStart){
// 忽略掉軟件安裝前的未接來電
return;
}
/*******************查找短信發(fā)送表中近“經(jīng)濟(jì)時(shí)間”內(nèi)是否有該號(hào)碼********************/
long whereTime = date - preferences.getInt("time", 30)*60000; // 記錄的時(shí)間 - “經(jīng)濟(jì)時(shí)間”
// 該號(hào)碼在短信發(fā)送表中的近“經(jīng)濟(jì)時(shí)間”內(nèi)的記錄
Cursor cursorDb = db.getReadableDatabase().rawQuery("select * from "+db.SEND_NOTES+" where "+Calls.NUMBER+" = ? and time > ? ", new String[]{number,whereTime+""});
/*********************短信操作***********************/
if(cursorDb.moveToNext()){
// 有記錄旺芽,不發(fā)送短信
}
else{
// 沒有記錄沪猴,發(fā)送短信
MyApplication instance = MyApplication.getInstance();
if(instance.getNumber() != null) {
// 已經(jīng)規(guī)定MyApplication中的name、number采章、content為“現(xiàn)在”變量运嗜,
// 因此過一定時(shí)間(一般為短信開始發(fā)送到發(fā)送成功的時(shí)間)后將為被置空
// 如果不為空,說明發(fā)生了onChange短時(shí)間被多次觸發(fā)
return;
}
instance.setName(name);
instance.setNumber(number);
instance.setContent(preferences.getString("content", "抱歉悯舟,未能及時(shí)接聽您的來電担租。\n【來電管家自動(dòng)回復(fù)】"));
SendMessage.sendTextMessage(context, name, number, instance.getContent());
}
if(cursorDb != null){
cursorDb.close();
}
if(db != null){
db.close();
}
}
}
package com.zji.utils;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsManager;
import android.widget.Toast;
/**
* 發(fā)送短信類
* @author phlofy
* @date 2016年3月3日 下午9:58:45
*/
public class SendMessage {
synchronized public static void sendTextMessage(Context context, String name, String number, String content){
Intent in = new Intent("SENT_SMS_ACTION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, in, 0);
SmsManager.getDefault().sendTextMessage(number, null, content, pi, null);
}
}
package com.zji.broadcase;
import com.zji.activity.MyApplication;
import com.zji.db.MyDatabaseHelper;
import com.zji.utils.SendMessage;
import com.zji.utils.Timer;
import com.zji.utils.WriteAndRead;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.preference.Preference;
import android.telephony.SmsManager;
import android.widget.Toast;
public class SendMessageReceiver extends BroadcastReceiver{
MyDatabaseHelper db;
static int errCount = 0; // 記錄一條短信發(fā)送失敗次數(shù)
@Override
public void onReceive(Context context, Intent intent) {
if("SENT_SMS_ACTION".equals(intent.getAction())){
try{
MyApplication instance = MyApplication.getInstance();
switch (getResultCode()) {
// 短信發(fā)送成功
case Activity.RESULT_OK:
db = new MyDatabaseHelper(context);
db.getReadableDatabase().execSQL("insert into "+db.SEND_NOTES+" values(null , ? , ? , ?)",new String[]{instance.getName(),instance.getNumber(),Timer.getNowDate()+""});
if(db != null){
db.close();
}
errCount = 0;
// 短時(shí)間變量,用完后將其置空
instance.setName(null);
instance.setNumber(null);
instance.setContent(null);
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
case SmsManager.RESULT_ERROR_NO_SERVICE:
case SmsManager.RESULT_ERROR_NULL_PDU:
case SmsManager.RESULT_ERROR_RADIO_OFF:
if(errCount < 2){
// 最多可以嘗試發(fā)送三遍
SendMessage.sendTextMessage(context, instance.getName(), instance.getNumber(), instance.getContent());
errCount++;
}
else {
// 嘗試發(fā)送三遍仍然發(fā)送不出去图谷,放棄發(fā)送
errCount = 0;
// 短時(shí)間變量翩活,用完后將其置空
instance.setName(null);
instance.setNumber(null);
instance.setContent(null);
}
break;
default:
break;
}
}catch(Exception e){
e.printStackTrace();
}
finally{
}
}
}
}
package com.zji.activity;
import android.app.Application;
/**
* 用于存放中間變量
* @author phlofy
* @date 2016年3月4日 下午9:55:33
*/
public class MyApplication extends Application{
private static MyApplication myApplication = null;
/**
* “現(xiàn)在”短信要發(fā)送的目標(biāo)
* 1.為了防止MyContentObserver.onChange方法短時(shí)間內(nèi)被多次觸發(fā),
* 造成還未來得及插入短信發(fā)送成功的記錄便贵,短信重復(fù)發(fā)送出去
* 2.解決傳遞給SendMessageReceiver的Intent數(shù)據(jù)為上一次(第一次)
* 的數(shù)據(jù)菠镇。替代通過Intent得到number和name
*/
String number;
String name;
String content;
@Override
public void onCreate() {
super.onCreate();
//由于Application類本身已經(jīng)單例,所以直接按以下處理即可承璃。
myApplication = this;
}
/**
* 獲取Application實(shí)例
* @return
*/
public static MyApplication getInstance(){
return myApplication;
}
public void setNumber(String number) {
this.number = number;
}
/**
* 獲取現(xiàn)在短信的目標(biāo)號(hào)碼
* @return
*/
public String getNumber() {
return number;
}
public void setName(String name) {
this.name = name;
}
/**
* 獲取現(xiàn)在短信的目標(biāo)者名稱
* @return
*/
public String getName() {
return name;
}
public void setContent(String content) {
this.content = content;
}
/**
* 獲取短信內(nèi)容
* @return
*/
public String getContent() {
return content;
}
}