監聽未接來電,自動回覆簡訊
阿新 • • 發佈:2019-01-22
剛學習Android開發就很想做這麼一個工具了。最近終於用eclipse把程式碼敲出來了,寫部落格記錄之。
首先說下整體思路如下:
1. 後臺開啟一個Servic通過ContentObserver監聽通話記錄表的變化。
2. 如果有變化則通過程式碼判斷是否是剛產生的未接來電。
3. 需要發生簡訊,則呼叫傳送簡訊的程式碼。
我遇到的問題:
1. 我監聽的位置是“CallLog.Calls.CONTENT_URI”,然而我卻發現ContentObserver的onChange方法被頻繁觸發。(即使沒有產生通話電記錄)
2. 發生簡訊為了防止傳送失敗,註冊簡訊發生狀態的廣播接收。通過intent傳遞電話號碼和簡訊發生內容。然而測試中卻發生intent中獲取到的值都是第一次新增的值。(並不會更新)
問題的不優雅解決:(希望得到前輩們指點,優雅地解決這兩個問題)
1. 既然ContentObserver的onChange方法被頻繁觸發,那麼多一些判斷,判斷是否是剛發生的未接來電記錄是則往下執行,不是則忽略。
2. 既然intent中獲取的資料不準確,那麼就換個地方獲取資料。我選擇了MyApplication做資料中轉站。
關鍵程式碼片段:
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;
/**
* 後臺執行的服務,負責開啟監聽通話記錄表的變化
* @author phlofy
* @date 2016年3月3日 下午2:13:29
*/
public class MainService extends Service{
MyContentObserver mMyContentObserver;
SendMessageReceiver mSendMessageReceiver;
public static boolean isWorking = false; // 方便MainFragment知道是否開啟後臺監聽服務
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 註冊通話記錄表監聽
mMyContentObserver = new MyContentObserver(this, new Handler());
this.getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, false, mMyContentObserver);
// 註冊簡訊傳送狀態監聽
IntentFilter intentFilter = new IntentFilter("SENT_SMS_ACTION");
mSendMessageReceiver = new SendMessageReceiver();
this.registerReceiver(mSendMessageReceiver, intentFilter);
isWorking = true;
try{
Toast.makeText(this, "服務開始執行", Toast.LENGTH_LONG).show();
}catch(Exception e){}
}
@Override
public void onDestroy() {
super.onDestroy();
// 登出兩個監聽
this.getContentResolver().unregisterContentObserver(mMyContentObserver);
this.unregisterReceiver(mSendMessageReceiver);
isWorking = false;
try{
Toast.makeText(this, "服務停止執行", Toast.LENGTH_LONG).show();
}catch(Exception e){}
}
}
/**
* 通話記錄表變化的監聽者
* @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();
}
/**
* 判斷該未接來電是否是該軟體安裝後發生。
* 防止沒有未接來電,但onChange還是被執行的情況。
* 解決軟體第一次安裝後onChange被觸發自動傳送一條簡訊問題
*/
long lifeStart = preferences.getLong("life_start", 0); //試圖獲取軟體安裝時間
if(lifeStart == 0){
// 為0說明軟體第一次執行,記錄此時時間為軟體安裝時間
editor.putLong("life_start", new Date().getTime());
editor.commit();
}
if(lifeStart == 0 || date < lifeStart){
// 忽略掉軟體安裝前的未接來電
return;
}
/*******************查詢簡訊傳送表中近“經濟時間”內是否有該號碼********************/
long whereTime = date - preferences.getInt("time", 30)*60000; // 記錄的時間 - “經濟時間”
// 該號碼在簡訊傳送表中的近“經濟時間”內的記錄
Cursor cursorDb = db.getReadableDatabase().rawQuery("select * from "+db.SEND_NOTES+" where "+Calls.NUMBER+" = ? and time > ? ", new String[]{number,whereTime+""});
/*********************簡訊操作***********************/
if(cursorDb.moveToNext()){
// 有記錄,不傳送簡訊
}
else{
// 沒有記錄,傳送簡訊
MyApplication instance = MyApplication.getInstance();
if(instance.getNumber() != null) {
// 已經規定MyApplication中的name、number、content為“現在”變數,
// 因此過一定時間(一般為簡訊開始傳送到傳送成功的時間)後將為被置空
// 如果不為空,說明發生了onChange短時間被多次觸發
return;
}
instance.setName(name);
instance.setNumber(number);
instance.setContent(preferences.getString("content", "抱歉,未能及時接聽您的來電。\n【來電管家自動回覆】"));
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;
/**
* 傳送簡訊類
* @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; // 記錄一條簡訊傳送失敗次數
@Override
public void onReceive(Context context, Intent intent) {
if("SENT_SMS_ACTION".equals(intent.getAction())){
try{
MyApplication instance = MyApplication.getInstance();
switch (getResultCode()) {
// 簡訊傳送成功
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;
// 短時間變數,用完後將其置空
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){
// 最多可以嘗試傳送三遍
SendMessage.sendTextMessage(context, instance.getName(), instance.getNumber(), instance.getContent());
errCount++;
}
else {
// 嘗試傳送三遍仍然傳送不出去,放棄傳送
errCount = 0;
// 短時間變數,用完後將其置空
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;
/**
* “現在”簡訊要傳送的目標
* 1.為了防止MyContentObserver.onChange方法短時間內被多次觸發,
* 造成還未來得及插入簡訊傳送成功的記錄,簡訊重複傳送出去
* 2.解決傳遞給SendMessageReceiver的Intent資料為上一次(第一次)
* 的資料。替代通過Intent得到number和name
*/
String number;
String name;
String content;
@Override
public void onCreate() {
super.onCreate();
//由於Application類本身已經單例,所以直接按以下處理即可。
myApplication = this;
}
/**
* 獲取Application例項
* @return
*/
public static MyApplication getInstance(){
return myApplication;
}
public void setNumber(String number) {
this.number = number;
}
/**
* 獲取現在簡訊的目標號碼
* @return
*/
public String getNumber() {
return number;
}
public void setName(String name) {
this.name = name;
}
/**
* 獲取現在簡訊的目標者名稱
* @return
*/
public String getName() {
return name;
}
public void setContent(String content) {
this.content = content;
}
/**
* 獲取簡訊內容
* @return
*/
public String getContent() {
return content;
}
}