短信相关权限
<!-- 发送消息-->
<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" />
系统的短信库存在data/data/com.android.providers.telephony/databases/mmssms.db 但真机不可读
URI:content://mms-sms/conversations 相关重要列
thread_id :这个字段很重要,同一个会话中他们的thread_id是一样的,也就是说通过thread_id就可以知道A与B在聊天 还是 A与C在聊天
date :这条消息发送或接收的时间
read: 0 表示未读 1表示已读
type : 1表示接收 2 表示发出
body 表示 消息的内容
/////////////////////////////////////////////////////////////////////
/**发送与接收的广播**/
String SENT_SMS_ACTION = "SENT_SMS_ACTION";
String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION";
//注册两个用于监控短信情况的广播
public void registerSmsReceiver(){
// 注册广播 发送消息
registerReceiver(sendMessage, new IntentFilter(SENT_SMS_ACTION));
registerReceiver(receiver, new IntentFilter(DELIVERED_SMS_ACTION));
}
public void unRegisterSmsReceiver(){
// 注销广播 发送消息
unregisterReceiver(sendMessage);
unregisterReceiver(receiver);
}
private BroadcastReceiver sendMessage = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//判断短信是否发送成功
switch (getResultCode()) {
case Activity.RESULT_OK:
Toast.makeText(context, "短信发送成功", Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(mContext, "发送失败", Toast.LENGTH_LONG).show();
break;
}
}
};
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//表示对方成功收到短信
Toast.makeText(mContext, "对方接收成功",Toast.LENGTH_LONG).show();
}
};
/**
* 参数说明
* destinationAddress:收信人的手机号码
* scAddress:发信人的手机号码
* text:发送信息的内容
* sentIntent:发送是否成功的回执,用于监听短信是否发送成功。
* DeliveryIntent:接收是否成功的回执,用于监听短信对方是否接收成功。
*/
private void sendSMS(String phoneNumber, String message) {
// ---sends an SMS message to another device---
// 获得短信管理器
SmsManager sms = SmsManager.getDefault();
// create the sentIntent parameter
// 监控短信是否发送成功的Action和PendingIntent
Intent sentIntent = new Intent(SENT_SMS_ACTION);
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sentIntent,
0);
// create the deilverIntent parameter
// 监控对方是否收到短信的Action和PendingIntent
Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION);
PendingIntent deliverPI = PendingIntent.getBroadcast(this, 0,
deliverIntent, 0);
//如果短信内容超过70个字符 将这条短信拆成多条短信发送出去
if (message.length() > 70) {
ArrayList<String> msgs = sms.divideMessage(message);
for (String msg : msgs) {
sms.sendTextMessage(phoneNumber, null, msg, sentPI, deliverPI);
}
} else {
sms.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI);
}
}
//发送指定的短信,并保存
public void saveSms(String saveNumber, String saveText){
/** 拿到输入的手机号码 **/
String number = saveNumber;
/** 拿到输入的短信内容 **/
String text = saveText;
/** 手机号码 与输入内容 必需不为空 **/
if (!TextUtils.isEmpty(number) && !TextUtils.isEmpty(text)) {
//发送短信
sendSMS(number, text);
/**将发送的短信插入数据库**/
ContentValues values = new ContentValues();
//发送时间
values.put("date", System.currentTimeMillis());
//阅读状态
values.put("read", 0);
//1为收 2为发
values.put("type", 2);
//送达号码
values.put("address", number);
//送达内容
values.put("body", text);
//插入短信库
getContentResolver().insert(Uri.parse("content://sms"),values);
}
}
/////////////////////////////////////////////////////////////////////
//判断短信是否发送成功,完整的状态
case Activity.RESULT_OK:
message = "Message sent!";
error = false;
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
message = "Error.";
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
message = "Error: No service.";
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
message = "Error: Null PDU.";
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
message = "Error: Radio off.";
break;
//另一个将短信写入发件箱的方法
ContentValues cv = new ContentValues();
cv.put("address", destinationAddress);
cv.put("person", "");
cv.put("protocol", "0");
cv.put("read", "1");
cv.put("status", "-1");
cv.put("body", text);
this.getContentResolver().insert(Uri.parse("content://sms/sent"), cv);
Log.e("sms", "短信已经保存");
SMS不能直接访问数据库,只能通过协议来访问数据,相关的协议:
content://sms/inbox 收件箱
content://sms/sent 已发送
content://sms/draft 草稿
content://sms/outbox 发件箱
content://sms/failed 发送失败
content://sms/queued 待发送列表
在模拟器上Outbox可能没有数据。
数据库中sms相关的字段如下:
_id 一个自增字段,从1开始
thread_id 序号,同一发信人的id相同
address 发件人手机号码
person 联系人列表里的序号,陌生人为null(不一定有联系人就有值)
date 发件日期
protocol 协议,分为: 0 SMS_RPOTO, 1 MMS_PROTO
read 是否阅读 0未读, 1已读
status 状态 -1接收,0 complete, 64 pending, 128 failed
type
ALL = 0;
INBOX = 1;
SENT = 2;
DRAFT = 3;
OUTBOX = 4;
FAILED = 5;
QUEUED = 6;
body 短信内容
service_center 短信服务中心号码编号
subject 短信的主题
reply_path_present TP-Reply-Path
locked 是否加锁
/////////////////////////////////////////////////////////////////////
android的源代码,sms支持的协议有:
sURLMatcher.addURI("sms", null, SMS_ALL);
sURLMatcher.addURI("sms", "#", SMS_ALL_ID);
sURLMatcher.addURI("sms", "inbox", SMS_INBOX);
sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID);
sURLMatcher.addURI("sms", "sent", SMS_SENT);
sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID);
sURLMatcher.addURI("sms", "draft", SMS_DRAFT);
sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID);
sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX);
sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID);
sURLMatcher.addURI("sms", "undelivered", SMS_UNDELIVERED);
sURLMatcher.addURI("sms", "failed", SMS_FAILED);
sURLMatcher.addURI("sms", "failed/#", SMS_FAILED_ID);
sURLMatcher.addURI("sms", "queued", SMS_QUEUED);
sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS);
sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID);
sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE);
sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT);
sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID);
sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID);
sURLMatcher.addURI("sms", "threadID/*", SMS_QUERY_THREAD_ID);
sURLMatcher.addURI("sms", "status/#", SMS_STATUS_ID);
sURLMatcher.addURI("sms", "sr_pending", SMS_STATUS_PENDING);
sURLMatcher.addURI("sms", "sim", SMS_ALL_SIM);
sURLMatcher.addURI("sms", "sim/#", SMS_SIM);
/////////////////////////////////////////////////////////////////////
其中,delete方法中支持的协议为:
SMS_ALL 根据参数中的条件删除sms表数据
SMS_ALL_ID 根据_id删除sms表数据
SMS_CONVERSATIONS_ID 根据thread_id删除sms表数据,可以带其它条件
SMS_RAW_MESSAGE 根据参数中的条件删除 raw表
SMS_STATUS_PENDING 根据参数中的条件删除 sr_pending表
SMS_SIM 从Sim卡上删除数据
//删除thread_id="3", _id="5"的数据
//SMS_CONVERSATIONS_ID:"content://sms/conversations/3"
this.getContentResolver().delete(Uri.parse("content://sms/conversations/3"), "_id=?", new String[]{"5"});
在数据库中每个发送者的thread_id虽然一样,但不是固定的,如果把一个发送者的全部数据删除掉,
然后换一个新号码发送短信时,thread_id是以数据库中最大的id+1赋值的。
/////////////////////////////////////////////////////////////////////
update支持的协议有很多:
SMS_RAW_MESSAGE
SMS_STATUS_PENDING
SMS_ALL
SMS_FAILED
SMS_QUEUED
SMS_INBOX
SMS_SENT
SMS_DRAFT
SMS_OUTBOX
SMS_CONVERSATIONS
SMS_ALL_ID
SMS_INBOX_ID
SMS_FAILED_ID
SMS_SENT_ID
SMS_DRAFT_ID
SMS_OUTBOX_ID
SMS_CONVERSATIONS_ID
SMS_STATUS_ID
SMS_INBOX_ID测试:
ContentValues cv = new ContentValues();
cv.put("thread_id", "2");
cv.put("address", "00000");
cv.put("person", "11");
cv.put("date", "11111111");
this.getContentResolver().update(Uri.parse("content://sms/inbox/4"), cv, null, null);
可以更新thread_id(但有可能混乱)
/////////////////////////////////////////////////////////////////////
insert支持的协议:
SMS_ALL
SMS_INBOX
SMS_FAILED
SMS_QUEUED
SMS_SENT
SMS_DRAFT
SMS_OUTBOX
SMS_RAW_MESSAGE
SMS_STATUS_PENDING
SMS_ATTACHMENT
SMS_NEW_THREAD_ID
向sms表插入数据时,type是根据协议来自动设置,
如果传入的数据中没有设置date时,自动设置为当前系统时间;非SMS_INBOX协议时,read标志设置为1
SMS_INBOX协议时,系统会自动查询并设置PERSON
threadId为null或者0时,系统也会自动设置
一直为造不了"发送失败"的邮件而发愁,现在来做一个:
content://sms/failed
ContentValues cv = new ContentValues();
cv.put("_id", "99");
cv.put("thread_id", "0");
cv.put("address", "9999");
cv.put("person", "888");
cv.put("date", "9999");
cv.put("protocol", "0");
cv.put("read", "1");
cv.put("status", "-1");
//cv.put("type", "0");
cv.put("body", "短信内容");
this.getContentResolver().insert(Uri.parse("content://sms/failed"), cv);
//插入短信是否设置了检验?
type
ALL = 0;
INBOX = 1;
SENT = 2;
DRAFT = 3;
OUTBOX = 4;
FAILED = 5;
QUEUED = 6;
type是否被设置成了5?
///////////////////////////////////////////////////////////////////////////////////
希望读取彩信的同学,请参考这里
尊重别人的劳动:http://www.cnblogs.com/lycoris/archive/2011/04/27/2030714.html
代码主要实现一个读取彩信列表的功能,原本功能是点击某条彩信记录,显示相关包括文本、图片(该处并没有对音频附件处理)等,该处就把他们整合到一起了。
public class SmsPage extends ListActivity{
private final String TAG="SmsPage";
private final Uri CONTENT_URI = Uri.parse("content://mms/inbox"); //查询彩信收件箱
private final Uri CONTENT_URI_PART = Uri.parse("content://mms/part"); //彩信附件表
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.smslist);
Cursor cursor = getContentResolver().query(CONTENT_URI, null, null,null, null);
if(cursor.getCount()>0){
MyAdapter adapter = new MyAdapter(this,R.layout.smsitem,cursor,new String[]{},new int[]{});
setListAdapter(adapter);
}
}
public class MyAdapter extends SimpleCursorAdapter{
private String name="";
public MyAdapter(Context context, int layout, Cursor c, String[] from,
int[] to) {
super(context, layout, c, from, to);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView address = (TextView)view.findViewById(R.id.sms_address);
TextView body = (TextView)view.findViewById(R.id.sms_body);
TextView date = (TextView)view.findViewById(R.id.sms_date);
TextView sub = (TextView)view.findViewById(R.id.sms_sub);
ImageView image = (ImageView)view.findViewById(R.id.sms_image);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time=new Date(cursor.getLong(cursor.getColumnIndex("date"))*1000);//彩信时间
int id = cursor.getInt(cursor.getColumnIndex("_id"));//彩信Id
String subject = cursor.getString(cursor.getColumnIndex("sub"));//彩信主题
Cursor cAdd =null;
Cursor cPhones=null;
Cursor cPeople=null;
Cursor cPart=null;
try {
//根据彩信id从addr表中查出发送人电话号码,其中外键msg_id映射pdu表的id;
String selectionAdd = new String("msg_id=" + id + ");
Uri uriAddr = Uri.parse("content://mms/" + id + "/addr");
cAdd = getContentResolver().query(uriAddr, null, selectionAdd, null, null);
//根据addr表中的电话号码在phones表中查出PERSON_ID,外键PERSON_ID映射people表中的_id
if(cAdd.moveToFirst()){//该处会得到2条记录,第一条记录为发件人号码,第二条为本机号码
String number= cAdd.getString(cAdd.getColumnIndex("address"));
cPhones = getContentResolver().query(Contacts.Phones.CONTENT_URI, new String[]{Contacts.Phones.PERSON_ID},Contacts.Phones.NUMBER +"=?",new String[]{number}, null);
if(cPhones.getCount()>0){//根据phones表中的PERSON_ID查出 people 表中联系人的名字
while (cPhones.moveToNext()) {
String pId = cPhones.getString(cPhones.getColumnIndex(Contacts.Phones.PERSON_ID));
Uri uriPeo = Uri.parse(Contacts.People.CONTENT_URI+"/"+pId);
cPeople = getContentResolver().query(uriPeo, null,null,null, null);
if(cPeople.getCount()>0){
String str="";
while (cPeople.moveToNext()) {
if(str == ""){
str = cPeople.getString(cPeople.getColumnIndex(Contacts.People.DISPLAY_NAME));
}else{
str += ","+cPeople.getString(cPeople.getColumnIndex(Contacts.People.DISPLAY_NAME));
}
}
name=number+"/"+str;//如果通讯录中存在,则以 ‘电话号码/名字’ 形式显示
}else{
name=number;//如果是陌生人直接显示电话号码
}
}
}else{
name=number;//如果是陌生人直接显示电话号码
}
}
//根据彩信ID查询彩信的附件
String selectionPart = new String("mid="+id);//part表中的mid外键为pdu表中的_id
cPart = getContentResolver().query(CONTENT_URI_PART, null,selectionPart,null, null);
String bodyStr="";
String[] coloumns = null;
String[] values = null;
while(cPart.moveToNext()){
coloumns = cPart.getColumnNames();
if(values == null)
values = new String[coloumns.length];
for(int i=0; i< cPart.getColumnCount(); i++){
values[i] = cPart.getString(i);
}
if(values[3].equals("image/jpeg") || values[3].equals("image/bmp") || values[3].equals("image/gif") || values[3].equals("image/jpg") || values[3].equals("image/png")){ //判断附件类型
image.setImageBitmap(getMmsImage(values[0]);//该处只会显示一张图片,如果有需求的朋友可以根据自己的需求将ImageView换成Gallery,修改一下方法
image.setVisibility(View.VISIBLE);
}else if(values[3].equals("text/plain")){
/**该处详细描述一下
*发现一个版本问题,之前用的2.1版本的多台手机测试通过,结果用1.6的G2报异常
*经过调试发现,1.6版本part表中根本就没有"text"列,也就是values[13],所以就
*报错了,好像在1.6版本(我只测过G2,嘿嘿),就算是文本信息也是以一个附件形
*式存在_date里面也就是values[12]里面,与图片类似,但在2.1里面却不是这样存
*的,文本信息被存在了"text"这个字段中,且"_date"为null*/
if(values[12]!=null){//所以该处需判断一下,如果_date为null,可直接设置内容为"text"
bodyStr=getMmsText(values[0]);
}else{
bodyStr = values[13];
}
}
}
if(!"".equals(subject) && subject != null){
try {
sub.setText(new String(subject.getBytes("iso-8859-1"),"UTF-8"));//设置彩信主题的编码格式
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
if(!"".equals(bodyStr)){
body.setText(bodyStr);
}
address.setText(name);
date.setText(format.format(time));
} catch (RuntimeException e) {
Log.e(TAG, e.getMessage());
}finally{
if(cAdd != null){
cAdd.close();
}
if(cPart != null){
cPart.close();
}
if(cPeople != null){
cPeople.close();
}
if(cPhones != null){
cPhones.close();
}
}
super.bindView(view, context, cursor);
}
}
private String getMmsText(String _id){ //读取文本附件
Uri partURI = Uri.parse("content://mms/part/" + _id );
InputStream is = null;
StringBuilder sb = new StringBuilder();
try {
is = getContentResolver().openInputStream(partURI);
if(is!=null){
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String temp = reader.readLine();
while (temp != null) {
sb.append(temp);
temp=reader.readLine();//在网上看到很多把InputStream转成string的文章,没有这关键的一句,几乎千遍一律的复制粘贴,该处如果不加上这句的话是会内存溢出的
}
}
}catch (IOException e) {
e.printStackTrace();
Log.v(TAG, "读取附件异常"+e.getMessage());
}finally{
if (is != null){
try {
is.close();
}catch (IOException e){
Log.v(TAG, "读取附件异常"+e.getMessage());
}
}
}
return sb.toString();
}
private Bitmap getMmsImage(String _id){ //读取图片附件
Uri partURI = Uri.parse("content://mms/part/" + _id );
//ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
Bitmap bitmap=null;
try {
is = getContentResolver().openInputStream(partURI);
//byte[] buffer = new byte[256];
//int len = -1;
//while ((len = is.read(buffer)) != -1) {
// baos.write(buffer, 0, len);
//}
//bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toByteArray().length);
bitmap = BitmapFactory.decodeStream(is); }catch (IOException e) {
e.printStackTrace();
Log.v(TAG, "读取图片异常"+e.getMessage());
}finally{
if (is != null){
try {
is.close();
}catch (IOException e){
Log.v(TAG, "读取图片异常"+e.getMessage());
}
}
}
return bitmap;
}
}
///////////////////////////////////////////////////////////////////////////////////
//扩展参考
http://labs.ywlx.net/?p=899
http://www.cnblogs.com/not-code/archive/2011/12/01/2270903.html
http://www.cnblogs.com/not-code/archive/2011/11/27/2265287.html
///////////////////////////////////////////////////////////////////////////////////
android dev day8
最新推荐文章于 2025-07-27 14:56:01 发布