短信设置界面,主要给用户提供以下功能
1)存储设置,这里主要是关于一个会话存储短信的数量限制
2)短信设置,主要是是否开启短信的发送报告、短信模版管理、sim卡短信管理、短信中心号码显示
3)短信签名设置,短信签名是否开启,以及设置短信签名的内容
4)彩信设置,是否开启发送报告、已读报告、是否自动接收附件
5)通知设置,主要是是否开启通知、铃声选择震动模式等等
6)小区广播,主要是开启,设置频道
下面会分开来讲这些功能具体是做什么的,实现原理,怎么体现出来。
2、存储设置
<PreferenceCategory android:title="@string/pref_sms_storage_title" <CheckBoxPreference android:defaultValue="true" android:key="pref_key_auto_delete" android:summary="@string/pref_summary_auto_delete" android:title="@string/pref_title_auto_delete" /> <Preference android:key="pref_key_sms_delete_limit" android:dependency="pref_key_auto_delete" android:persistent="false" android:summary="@string/pref_summary_delete_limit" android:title="@string/pref_title_sms_delete" /> <Preference android:key="pref_key_mms_delete_limit" android:dependency="pref_key_auto_delete" android:persistent="false" android:summary="@string/pref_summary_delete_limit" android:title="@string/pref_title_mms_delete" /> </PreferenceCategory>
2.1 设置信息的保存
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { if (preference == mSmsLimitPref) { new NumberPickerDialog(this, mSmsLimitListener, mSmsRecycler.getMessageLimit(this), mSmsRecycler.getMessageMinLimit(), mSmsRecycler.getMessageMaxLimit(), R.string.pref_title_sms_delete).show(); } else if (preference == mMmsLimitPref) { new NumberPickerDialog(this, mMmsLimitListener, mMmsRecycler.getMessageLimit(this), mMmsRecycler.getMessageMinLimit(), mMmsRecycler.getMessageMaxLimit(), R.string.pref_title_mms_delete).show(); }
<boolean name="pref_key_auto_delete" value="true" /> <int name="MaxMmsMessagesPerThread" value="50" /> <int name="MaxSmsMessagesPerThread" value="500" />
2.2 功能体现
private Uri storeMessage(Context context, SmsMessage[] msgs, int error) { ---------省略----
// Now make sure we're not over the limit in stored messages Recycler.getSmsRecycler().deleteOldMessagesByThreadId(getApplicationContext(), threadId); return insertedUri; }
这时SmeReciverService当短信收到后会存储短信到数据库这里为了保证数据能正确存入;
public void deleteOldMessagesByThreadId(Context context, long threadId) { if (LOCAL_DEBUG) { Log.v(TAG, "Recycler.deleteOldMessagesByThreadId this: " + this + " threadId: " + threadId); } if (!isAutoDeleteEnabled(context)) { return; } deleteMessagesForThread(context, threadId, getMessageLimit(context)); }
这里大致就是首先看这个删除旧信息是否打开如果没有打开直接略过,不做任何操作如果打开,就根据设置的值来做判断,具体的操作如下代码所示:
public void deleteOldMessagesByThreadId(Context context, long threadId) { if (LOCAL_DEBUG) { Log.v(TAG, "Recycler.deleteOldMessagesByThreadId this: " + this + " threadId: " + threadId); } if (!isAutoDeleteEnabled(context)) { return; } deleteMessagesForThread(context, threadId, getMessageLimit(context)); }
这里大致就是首先看这个删除旧信息是否打开如果没有打开直接略过,不做任何操作如果打开,就根据设置的值来做判断,具体的操作如下代码所示:
protected void deleteMessagesForThread(Context context, long threadId, int keep) { ContentResolver resolver = context.getContentResolver(); Cursor cursor = null; try { 1、首先查询出该会话的包含多少条信息 cursor = SqliteWrapper.query(context, resolver, ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId), SMS_MESSAGE_PROJECTION, "locked=0", null, "date DESC"); // get in newest to oldest order if (cursor == null) { Log.e(TAG, "SMS: deleteMessagesForThread got back null cursor"); return; }2、总的信息数 int count = cursor.getCount(); int numberToDelete = count - keep;3、需要删除的信息数 if (numberToDelete <= 0) { return; } // Move to the keep limit and then delete everything older than that one. cursor.move(keep);删除超出限制的信息数 long latestDate = cursor.getLong(COLUMN_SMS_DATE); long cntDeleted = SqliteWrapper.delete(context, resolver, ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId), "locked=0 AND date<" + latestDate, null); if (LOCAL_DEBUG) { Log.v(TAG, "SMS: deleteMessagesForThread cntDeleted: " + cntDeleted); } } }
2)彩信接收检查
3)发送时的检查
private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) { String[] dests = TextUtils.split(semiSepRecipients, ";"); if (LogTag.VERBOSE || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { LogTag.debug("sendSmsWorker sending message: recipients=" + semiSepRecipients + ", threadId=" + threadId); } MessageSender sender; int subscription = (mCurrentConvSub == -1) ? SmsManager.getDefault().getPreferredSmsSubscription() : mCurrentConvSub; sender = new SmsMessageSender(mActivity, dests, msgText, threadId, subscription); try { sender.sendMessage(threadId); //检查是否超出范围,如果超出则删除数据库中旧的信息 // Make sure this thread isn't over the limits in message count Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId); Recycler.getMmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId); } catch (Exception e) { Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e); } mStatusListener.onMessageSent(); }
二、彩信
3、短信设置
3.1 发送报告
<CheckBoxPreference android:defaultValue="false" android:key="pref_key_sms_delivery_reports" android:summary="@string/pref_summary_sms_delivery_reports" android:title="@string/pref_title_sms_delivery_reports" />
然后点击该控件就会将com.android.mms_preferences.xml文件中对应的属性值进行更改,短信的发送流程--短信入库阶段,一条短信的属性中恰有一个属性就是发送报告,在SmsMessageSender的queueMessage方法中入库短信:
boolean requestDeliveryReport = prefs.getBoolean( MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE, DEFAULT_DELIVERY_REPORT_MODE); 获取设置中设置的值 for (int i = 0; i < mNumberOfDests; i++) { try { log("updating Database with sub = " + mSubscription); Sms.addMessageToUri(mContext.getContentResolver(), Uri.parse("content://sms/queued"), mDests[i], mMessageText, null, mTimestamp, true /* read */, requestDeliveryReport, mThreadId, mSubscription); } catch (SQLiteException e) { SqliteWrapper.checkSQLiteException(mContext, e); } }
在将这个值保存到数据库的时候,google工程师给我们设计了一个陷阱,那我们先来看看我们是怎么使用这个属性的?短信真正发送是调用SmsReceiverService的sendFirstQueeMessage方法里,前面入库是将短信的状态设置为待发送,后面真正的发送时,需要调用中间层SmsManger。
public synchronized void sendFirstQueuedMessage(int subscription) { if (c != null) { try { if (c.moveToFirst()) { String msgText = c.getString(SEND_COLUMN_BODY); String address = c.getString(SEND_COLUMN_ADDRESS); int threadId = c.getInt(SEND_COLUMN_THREAD_ID); int status = c.getInt(SEND_COLUMN_STATUS);发送报告的值 int msgId = c.getInt(SEND_COLUMN_ID); Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId); SmsMessageSender sender; sender = new SmsSingleRecipientSender(this,实例化SmsSingleRecipientSender对象 address, msgText, threadId, status == Sms.STATUS_PENDING,为什么?? msgUri, subscription); 这里并没有真正发送,发送短信的真正实现是在SmsSingleRecipientSender类的sendMessage方法里实现的 sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);; }
下面来看SmsSingleRecipientSender类
public SmsSingleRecipientSender(Context context, String dest, String msgText, long threadId, boolean requestDeliveryReport, Uri uri, int subscription) { super(context, null, msgText, threadId, subscription); mRequestDeliveryReport = requestDeliveryReport; mDest = dest; mUri = uri; }
然后看看sendMessage方法的相关内容
public boolean sendMessage(long token) throws MmsException { SmsManager smsManager = SmsManager.getDefault(); ArrayList<String> messages = null; int messageCount = messages.size(); boolean moved = Sms.moveMessageToFolder(mContext, mUri, Sms.MESSAGE_TYPE_OUTBOX, 0); ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount); ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount); for (int i = 0; i < messageCount; i++) { if (mRequestDeliveryReport) { 该值就代表的是否有发送报告 deliveryIntents.add(PendingIntent.getBroadcast( mContext, 0, new Intent( MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION, mUri, mContext, MessageStatusReceiver.class), 0));这里就是当短信发送完成后回调的一个intent,来处理发送报告 } Intent intent = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION, mUri, mContext, SmsReceiver.class); int requestCode = 0; if (i == messageCount -1) { requestCode = 1; intent.putExtra(SmsReceiverService.EXTRA_MESSAGE_SENT_SEND_NEXT, true); intent.putExtra(SUBSCRIPTION, mSubscription); } sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0)); } smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents, mSubscription); return false; }
上面可以看出发送报告是怎么实现的,解析到这,你可以很清楚的发现应用通过设置pendingIntent来设置发送报告,关于发送报告在短彩信的发送以及接收中有提到,其中间层是怎么返回回来,这里不做详细描述不懂的同学可以复习一下。这里还有一个小问题就在于我们从数据库取到的值是一个整数,但是我们存取的是一个布尔型,这是神马原因了??想知道这个问题,那我们来看短信存取的时候这个属性的值是怎么设置的,下面是短信发送报告存数据库的转换代码,Telephone类中:
public static Uri addMessageToUri(ContentResolver resolver, Uri uri, String address, String body, String subject, Long date, boolean read, boolean deliveryReport, long threadId, int subId) { ContentValues values = new ContentValues(8); values.put(SUB_ID, subId); values.put(ADDRESS, address); if (date != null) { values.put(DATE, date); } values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0)); values.put(SUBJECT, subject); values.put(BODY, body); if (deliveryReport) { values.put(STATUS, STATUS_PENDING); } if (threadId != -1L) { values.put(THREAD_ID, threadId); } return resolver.insert(uri, values); }
看到了吗?,存数据库的时候对这个值进行了转变,这也是为什么上面在去得之后要和STATUS_PENDING进行比较。
3.2 短信模版管理
短信模版保存在xml文件中,但大家第一次开机就有一些模版,这些预制的模版怎么做到的,实际上预制的这些模版是一些厂商定制的,其基本实现原理:将预制的模版定义在arrays.xml中,系统初始化MessageTemplateProvider就将模版保存到/data/data/com.android.mms/files/message_template.xml文件中,这里存储的介质是xml文件,与我们经常使用的数据库不同,总之使用MessageTemplateProvider来管理模版,message_template.xml来存储模版。具体的实现大家可以参考MessageTemplateProvider代码。
3.3 sim卡短信管理
3.4 短信中心号码
public void getSmscAddress(Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SMSC_ADDRESS, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); }
从这里可以看出获取短信中心号码实际上是通过modem来做的,android原生没有将这个接口开放出来,但是我们可以将其开放,放在相关的服务中例如PhoneInterfaceManager。这里向上开放该接口的方式不唯一也就导致我们获取短信中心号码的方式不唯一;总之都需要调用RIL.java中的接口,怎么暴露接口这里就不详细介绍了。
4、短信签名设置
<boolean name="pref_key_enable_signature" value="true" /> <string name="pref_key_edit_signature">test</string>
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mActivity); if (sp.getBoolean("pref_key_enable_signature", false)) { String signature = sp.getString("pref_key_edit_signature", ""); if (signature.trim().length() > 0) { text += " "+signature.trim(); }
5、彩信设置
6、通知设置
<string name="pref_key_ringtone">content://media/internal/audio/media/30</string> <boolean name="pref_key_enable_notifications" value="true" /> <string name="pref_key_vibrateWhen">always</string>
对于这些值怎么点击怎么保存,前面提到了很多很多关于preference的事件监听、以及属性值的保存,这里就不详解了,我们来看看在那会使用到这些配置文件,以接收短信为例:短信接收会调用MesageNotifiacation的updtaeNotification方法来实现。
private static void updateNotification( Context context, Intent clickIntent, String description, int iconRes, boolean isNew, CharSequence ticker, int subId, long timeMillis, String title, int messageCount, int uniqueThreadCount) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); if (!sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_ENABLED, true)) {获取是否开启短信通知 return;如果没有开启直接返回 } Notification notification = new Notification(iconRes, ticker, timeMillis); if (uniqueThreadCount > 1) { title = context.getString(R.string.notification_multiple_title); clickIntent = new Intent(Intent.ACTION_MAIN); clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); clickIntent.setType("vnd.android-dir/mms-sms"); } if (messageCount > 1) { description = context.getString(R.string.notification_multiple, Integer.toString(messageCount)); } PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT); notification.setLatestEventInfo(context, title, description, pendingIntent); if (isNew) { String vibrateWhen;获取震动的相关信息从配置文件中 if (sp.contains(MessagingPreferenceActivity.NOTIFICATION_VIBRATE_WHEN)) { vibrateWhen = sp.getString(MessagingPreferenceActivity.NOTIFICATION_VIBRATE_WHEN, null); } else if (sp.contains(MessagingPreferenceActivity.NOTIFICATION_VIBRATE)) { vibrateWhen = sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_VIBRATE, false) ? context.getString(R.string.prefDefault_vibrate_true) : context.getString(R.string.prefDefault_vibrate_false); } else { vibrateWhen = context.getString(R.string.prefDefault_vibrateWhen); } boolean vibrateAlways = vibrateWhen.equals("always");获取震动是神马模式下震动 boolean vibrateSilent = vibrateWhen.equals("silent"); AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); boolean nowSilent = audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; String ringtoneStr = sp.getString(MessagingPreferenceActivity.NOTIFICATION_RINGTONE, null);获取铃声 boolean messageSilent = TextUtils.isEmpty(ringtoneStr); if (vibrateAlways || vibrateSilent && (nowSilent || messageSilent)) { notification.defaults |= Notification.DEFAULT_VIBRATE; } notification.sound = messageSilent ? null : Uri.parse(ringtoneStr); } notification.flags |= Notification.FLAG_SHOW_LIGHTS; notification.defaults |= Notification.DEFAULT_LIGHTS; // set up delete intent notification.deleteIntent = PendingIntent.getBroadcast(context, 0, sNotificationOnDeleteIntent, 0); 通知 NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); nm.notify(NOTIFICATION_ID, notification); }
从上面你可以看到的是短信提示设置怎么生效的。