刚学习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知道是否开启后台监听服务@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic 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){}}@Overridepublic 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();}@Overridepublic 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; // 记录一条短信发送失败次数@Overridepublic 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;@Overridepublic 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;}
}

点击下载源码

监听未接来电,自动回复短信相关推荐

  1. Android监听未接来电

    采用注册通话记录的内容观察者来监听数据库的变化,从而达到监听未接来电. private MissedCallContentObserver mMissedCallContentObserver; mM ...

  2. android 发送彩信监听,在Android中发送短信和彩信,监听短信并显示

    发送短信: String body="this is sms demo"; Intent mmsintent = new Intent(Intent.ACTION_SENDTO, ...

  3. android如何实现QQ信息通知,android NotificationListenerService监听通知栏(qq 微信 短信)...

    [实例简介] android NotificationListenerService 监听通知栏,android NotificationListenerService 监听通知栏 android N ...

  4. android之来电自动拒接并自动回复短信_上课模式app

    上课的时候老师说总是错过电话,对方打来没人接还一遍遍的打,觉得可以有个app在上课期间自动拒接电话,并自动回复短信过去. 当然了,需要权限的. 尝试做了个雏形出来. 界面如下: 主要代码如下: pac ...

  5. android开发之来电自动拒接并自动回复短信_上课模式app

    上课的时候老师说总是错过电话,对方打来没人接还一遍遍的打,觉得可以有个app在上课期间自动拒接电话,并自动回复短信过去. 当然了,需要权限的. 尝试做了个雏形出来. 界面如下: 主要代码如下: pac ...

  6. android 来电拒接_[置顶] android开发之来电自动拒接并自动回复短信_上课模式app...

    上课的时候老师说总是错过电话,对方打来没人接还一遍遍的打,觉得可以有个app在上课期间自动拒接电话,并自动回复短信过去. 当然了,需要权限的. 尝试做了个雏形出来. 界面如下: 主要代码如下: pac ...

  7. 在Emulator上模拟来电、短信等 效果

    在Android Studio中,当我们需要模拟收到短信.或模拟来电时,虽然AS里面有一个自带Android Device Monitor可以调试,但是当我们启动它的时候,往往需要将AS与模拟器断开, ...

  8. android 监听电话状态 来电 接听 挂断

    如果想要监听手机的来电状态  需要接收手机的电话广播 首先是静态注册 <receiver android:name=".PhoneReceiver"android:expor ...

  9. 社恐人必备逃跑神器-模拟来电+模拟短信+模拟钱包+模拟关机

    小伙伴们注意:公众号的推送机制不再按照时间前后推送了,微信公众号信息流乱序.君哥建议大家把科技毒瘤君公众号置顶(设为星标⭐),以便第一时间看到推送,非常感谢~,方法如下图: 如果你是一个具有社交恐惧症 ...

最新文章

  1. 事件冒泡和阻止事件冒泡
  2. php HASHTABLE 实现
  3. 《C++ Primer Plus 6th》读书笔记 - 第8章 函数探幽
  4. KVM虚拟机文件优化导出最小化体积的qcow2镜像文件
  5. movielens推荐系统_案例|推荐系统的评估指标
  6. 102.二叉树的层序遍历
  7. e课表项目第二次冲刺周期第四天
  8. MAC使用青花瓷(charles)抓包
  9. 这个方法可以实现自动抠图,快来get
  10. springboot系列(二十七):如何实现word携带图片导出?这你得会|超级详细,建议收藏
  11. ubuntu的初始密码
  12. thinkpad l470 一种键盘错乱解决方法
  13. win10桌面图标变成白色文件
  14. 可能是最强的Python可视化神器,建议一试
  15. 利用WordPress源代码轻松搭建个人博客站点
  16. 天嵌E9开发板tftp烧录eMMC教程(Android)
  17. win11专业版升级
  18. 0基础如何学习安卓开发
  19. JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialize
  20. anaconda中matplotlib安装

热门文章

  1. 飞行堡垒windows10装Linux,华硕飞行堡垒安装Ubuntu16.04
  2. 作文提升~老师整理的优美拟人句太实用
  3. 一文弄懂Python中的*args 和 **kwargs
  4. 2020适合计算机专业的笔记本电脑,2020年,如何选择一款适合自己的笔记本电脑,全面解析指南...
  5. Could not transfer artifact xxx from/to maven-default-http-blocker (http://0.0.0.0/)
  6. 十六、Lua 文件 I/O的学习
  7. 用计算机名共享不了,局域网内无法用计算机名访问共享的解决办法
  8. origin平台linux,EA Origin游戏服务平台任意代码执行漏洞
  9. 腾讯云Web直播组件助力“直播+”场景
  10. 探究JS常见的6种继承方式