最近做的一个项目智能设备项目,应用与设备之间的通信是通过短信实现的。因此在项目中就涉及到短信的拦截、发送、删除以及状态更新等问题。
Android中使用短信功能要配置一些基本的权限:
`<!-- 发送消息 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<!-- 阅读消息 -->
<uses-permission android:name="android.permission.READ_SMS" />
<!-- 写入消息 -->
<uses-permission android:name="android.permission.WRITE_SMS" />
<!-- 接收消息 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />`
Android4.4上增加了不少安全措施,除了把SELinux设置为enforce外,在短信方向也加强了限制。
4.4之后,新增了一个default sms的机制,应用必须是默认的短信程序才可以收发短信。收发短信之前,先调用
if (!SmsWriteOpUtil.isWriteEnabled(mActivity)) {
SmsWriteOpUtil.setWriteEnabled(
mActivity, true);
}
发送代码:
device_no是目标手机号码,text是发送内容。
SmsManager.getDefault().sendTextMessage(device_no, null, text, null, null);
读取手机中的短信代码:
protocol 0代表短信,1代表彩信;
address 手机号码
body 短信内容
date 短信时间,long整型
_id 短信sqlite数据库中的id,自增
read 是否已读
其它字段百度
private Uri SMS_INBOX = Uri.parse("content://sms/inbox");
String query = WhereBuilder.b("address", "in", targets).and("protocol", "=", "0").toString();
ContentResolver cr = mActivity.getContentResolver();
String[] projection = new String[]{"_id", "body", "person", "address", "date"};//"_id", "address", "person",, "date", "type
Cursor cur = cr.query(SMS_INBOX, projection, query, null, "date desc");
if (null == cur)
return;
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex("_id"));
String number = cur.getString(cur.getColumnIndex("address"));//手机号
String name = cur.getString(cur.getColumnIndex("person"));//联系人姓名列表
String body = cur.getString(cur.getColumnIndex("body"));
long x = cur.getLong(cur.getColumnIndex("date"));
//String date = DateUtil.formatDate(new Date(x), DateUtil.yyyyMMDD);
}
cur.close();
短信删除代码:
result为数据库操作返回的行数。
String where = "_id=" + bean.get_id();
ContentResolver cr = getActivity().getContentResolver();
int result = cr.delete(Uri.parse("content://sms/"), where, null);
更新短信已读状态:
String query = WhereBuilder.b("address", "in", targets).and("protocol", "=", "0").toString();
ContentResolver cr = getActivity().getContentResolver();
ContentValues ct = new ContentValues();
ct.put("read",1);
cr.update(SMS_INBOX,ct,query,null);
短信监听,当手机接收到短信时,我们要进行一些过滤,是绑定的设备,程序才响应。可以用监听广播的方法来实现。但是这种方法只对新接收到的短信有效,对收件箱中的未读和已读短信无法响应。而且如果一些安全软件对短信进行拦截后,无法接收到广播。一句话,这种方法不推荐。我们可以使用ContentObserver观察者来观察短信数据库,但数据发生改变时,回调做后续处理。
首先继承一个ContentObserver:
targets为绑定的手机号码,因为我们只需要过滤处理这些,其它的短信不管。
class SmsObserver extends ContentObserver {
public SmsObserver(Context context, Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
String query = WhereBuilder.b("address", "in", targets).and("protocol", "=", "0").and("read","=","0").toString();
ContentResolver cr = getActivity().getContentResolver();
String[] projection = new String[]{"_id", "body", "person", "address", "date"};//"_id", "address", "person",, "date", "type"
Cursor cur = cr.query(SMS_INBOX, projection, query, null, "date desc");
if (null == cur)
return;
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex("_id"));
.....
}
cur.close();
smsHandler.sendEmptyMessage(0);
}
}
Handler实现UI操作:
我们在这里使用通知栏提醒,点击通知栏后重新打开应用,最重要的代码是setContentIntent(getDefalutIntent())方法,接着给出
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getActivity());
mBuilder.setContentTitle(getString(R.string.app_name))//设置通知栏标题
.setContentText(content) //设置通知栏显示内容
.setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图
// .setNumber(number) //设置通知集合的数量
.setTicker(ticker) //通知首次出现在通知栏,带上升动画效果的
.setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
.setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级
// .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消
.setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
.setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
//Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
.setSmallIcon(R.drawable.notify);//设置通知小ICON
Notification notification = mBuilder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
mNotificationManager.notify(888, notification);
public PendingIntent getDefalutIntent(int flags){
Intent intent = new Intent(getActivity(),com.xxxx.MainActivity.class);
Bundle bundle = new Bundle();
bundle.putInt(Const.KEY_NOTIFY_OPEN,1);
intent.putExtras(bundle);
Context mContext = getApplicationContext();
PendingIntent contextIntent = PendingIntent.getActivity(mContext, (int)(Math.random()*1000), intent, PendingIntent.FLAG_UPDATE_CURRENT);
return contextIntent;
}
MainActivity.class要使用singleTask启动模式,这样应用原来在哪一个页面,在点击通知栏后,都会只进入到MainActivity中。如果app已经被kill掉,MainActivity会进入onCreate方法,重建窗口。如果MainActivity只是onStop(),则会进入onNewIntent(Intent intent),可以从这里获取到intent中的值。
PendingIntent.getActivity是获取一个Activity打开,在一些要做初始化的应用中我们可以用getActivitys()的方法返回Intent[],例如:LoadingActivity->LoginActivity->MainActivity;ApiDemo中也是使用getActivitys来模拟从新打开应用的流程。