前言
最近做了一个Android小应用,可以接受短信或服务器发来的指令,根据指令自动拨打保存在app里面对应的电话号码,觉得挺有意思的。而且通过这个app,让我熟悉了很多Android的知识,在这里分享并记录一下。
功能
- 保存电话号码及对应的指令。
- 指令可以从短信获取,也可以从服务器获取。
- 根据指令,获取相应的电话号码,并轮流拨打。
- 可以设置号码保护时间,在这个时间以内,即使获取到相应的指令,也不会拨打此号码。
- 可以设置从服务器获取指令的间隔。
- 可以打开或关闭从服务器获取指令。
- 可以打开或关闭从短信息获取指令。
知识点
- 为ActionBar添加按钮
- 子Activity向上导航的实现
- ListView的使用
- SQLite数据库的使用
- SharedPreferences的使用
- 如何拦截短信息
- 如何在不操作的情况下自动拨打电话
- 如何监听电话的拨打状态
- 使用Service实现不间断的后台网络请求
- 使用volley实现网络请求
- AndroidStudio添加model
实现思路
1.添加\修改\删除一条号码及对应的指令
- 将number和sms封装成bean;
- 创建ItemsDBHelper类,继承SQLiteOpenHelper;
- 使用ItemsDBHelper创建表items,列名分别为:number,sms;
- ItemsDBHelper判断数据库中有没有这条数据:
public boolean hasItem(ItemBean itemBean) {
String selection = COLUMN_SMS + "=? AND " + COLUMN_NUMBER + "=?";
String[] selectionArgs = new String[] {itemBean.getSms(), itemBean.getNumber()};
Cursor cursor = this.db.query(TABLE_NAME, null, selection, selectionArgs, null, null,null);
if(cursor == null || cursor.getCount() == 0) {
return false;
} else {
return true;
}
}
- ItemsDBHelper封装添加\修改\删除方法:
public void saveItem(ItemBean itemBean) {
if(hasItem(itemBean)) {
return;
}
ContentValues values = new ContentValues();
values.put(COLUMN_SMS, itemBean.getSms());
values.put(COLUMN_NUMBER, itemBean.getNumber());
db.insert(TABLE_NAME, "", values);
}
public void deleteItem(ItemBean itemBean) {
String selection = COLUMN_SMS + "=? AND " + COLUMN_NUMBER + "=?";
String[] selectionArgs = {itemBean.getSms(), itemBean.getNumber()};
this.db.delete(TABLE_NAME, selection, selectionArgs);
}
public void updateItem(ItemBean oldItemBean, ItemBean newItemBean) {
deleteItem(oldItemBean);
saveItem(newItemBean);
}
2.在主界面上显示所有号码及对应的指令。
- 在ItemsDBHelper创建查询方法:
public ArrayList<ItemBean> loadItems() {
ArrayList<ItemBean> itemBeans = new ArrayList<ItemBean>();
Cursor cursor = this.db.query(TABLE_NAME, null, null, null, null, null,null);
if(cursor != null) {
while (cursor.moveToNext()) {
ItemBean itemBean = new ItemBean();
itemBean.setSms(cursor.getString(cursor.getColumnIndex(COLUMN_SMS)));
itemBean.setNumber(cursor.getString(cursor.getColumnIndex(COLUMN_NUMBER)));
itemBeans.add(itemBean);
}
}
Log.i(Config.TAG, "ItemsDBHelper->loadItems: " + itemBeans);
return itemBeans;
}
- 在Adapter中,将数据取出,并显示。
mItems = mItemsDBHelper.loadItems();
在getView()
中将值赋给每一行:
holder.tvSms.setText(mItems.get(position).getSms());
holder.tvNumber.setText(mItems.get(position).getNumber());
3.拦截短信息,获取到指令
4.不间断请求服务器,获取到指令
- 开启一个前台Service,每过一段时间请求一次服务器:
private void executeNetListen() {
new Handler().postDelayed(new Runnable() {
public void run() {
if(getIsReceiveServerMsg()) {
requestServer();
}
executeNetListen();
}
}, getIntervalTime());
}
5.根据指令获取到相应的号码
- 在ItemsDBHelper中,创建方法
getNumbers(String sms)
,返回一个放号码的ArrayList:
public ArrayList<String> getNumbers(String sms) {
ArrayList<String> sNumbers = new ArrayList<String>();
String selection = COLUMN_SMS + "=?";
String[] selectionArgs = new String[] {sms};
Cursor cursor = this.db.query(TABLE_NAME, null, selection, selectionArgs, null, null,null);
if(cursor != null) {
while (cursor.moveToNext()) {
String sNumber = cursor.getString(cursor.getColumnIndex(COLUMN_NUMBER));
sNumbers.add(sNumber);
}
}
return sNumbers;
}
6.创建表current_task
- 表current_task用来存放当前需要打的电话,相当于一个队列的概念;
- 创建CurrentTaskDBHelper类,继承SQLiteOpenHelper;
- 使用CurrentTaskDBHelper创建表current_task,列名分别为:phone_number,add_time;
7.将号码添加到表current_task
- 如果当前要插入的号码,在表中已经存在,那么不再插入;
8.从表current_task提取一条号码出来
- 根据add_time,提取最先添加的一条号码;
- 提取出来以后,说明这个号码要被执行,可以从表中删掉;
9.创建表done_task
- 表done_task用来存放拨打过的电话,相当于一个历史记录的概念;
- 创建CurrentTaskDBHelper类,继承SQLiteOpenHelper;
- 使用CurrentTaskDBHelper创建表current_task,列名分别为:phone_number,add_time;
10.将号码同表done_task的号码做比较
- 将current_task提取的号码拿到done_task中查询;
- 离上次拨打过去的时间s_time,s_time =(当前时间 - add_time);
- 如果号码在dome_task中没有找到,或者s_time>号码保护时间,执行第11步,并跟新add_time为当前时间;
- 如果s_time<号码保护时间,执行第8步;
11.执行打电话的动作
- 系统将自动拨打电话;
12.监听到挂断状态
- 当通话界面收起的时候(通常是对方挂断电话,语音提示结束以后),挂断状态被监听到;
- 继续执行第8步;
13.拨打保护
- 创建标志位
FLAG_IS_ON_CALL_TASK
,保存在SharedPreferences
中,用来表示是否正在执行拨打任务; - 当拨打任务开始时,
FLAG_IS_ON_CALL_TASK
为true;任务结束后FLAG_IS_ON_CALL_TASK
为false; - 如果任务执行过程中,接受到新的指令,则只把号码添加到current_task中(第7步),而不调用电话拨打(第八步);
- 这样可以防止通话被干扰;
14.拨打任务结束
- 表current_task中的号码,会在任务执行过程中越来越少;
- 表current_task一条号码也没有时,从方法中return,结束当前任务;
总结
- 短信的拦截;
- 后台不断监听服务器信息;
- SQLite的查询、添加、修改、删除;
- current_task是一个队列,按顺序拨打;
- done_task会记录号码上次拨打的时间;
- 拨打任务的执行中,添加标志位,防止通话被干扰;
- current_task中没有号码,结束通话任务;