1. 程式人生 > >監聽未接來電,自動回覆簡訊

監聽未接來電,自動回覆簡訊

剛學習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;
    }
}