现在的位置: 首页 > 综合 > 正文

短信ui分析–设置界面

2013年04月13日 ⁄ 综合 ⁄ 共 14488字 ⁄ 字号 评论关闭
短信ui分析--设置界面

1、概括

      短信设置界面,主要给用户提供以下功能

     1)存储设置,这里主要是关于一个会话存储短信的数量限制

      2)短信设置,主要是是否开启短信的发送报告、短信模版管理、sim卡短信管理、短信中心号码显示

      3)短信签名设置,短信签名是否开启,以及设置短信签名的内容

      4)彩信设置,是否开启发送报告、已读报告、是否自动接收附件

      5)通知设置,主要是是否开启通知、铃声选择震动模式等等

      6)小区广播,主要是开启,设置频道

     下面会分开来讲这些功能具体是做什么的,实现原理,怎么体现出来。

2、存储设置

     首先来看看短信存储的界面如下图所示,该项实际上就是三个功能:1、当会话中相应信息达到设置的值,删除掉old的信息;2、设置一个会话中短信的数量;3、设置会话中彩信的数量。其中删除old的信息又决定了后面两个功能是否可用,因为如果这项功能不启用,对于每个会话设置短信、彩信的数量也就没有太多意义。
       
图1
从上图可以看出三个选项,删除旧的信息前提是当信息量达到设置的限制时,但大家可以尝试当删除旧的信息没有被选中的时候下面两个选项也不能进行编辑,也即是下面两个选项依赖上面两个选项,这里可以从其配置文件来看出其依据。
  <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 设置信息的保存

删除旧的短信息,那它要删除旧的信息前提是达到各种设置的信息上限,这些上限值从何而来,其值就是下面两个选项用户设置的值,这些值会保存到
com.android.mms_preferences.xml文件里,这里因为用的是sharedpreference来保存这些变量值:
    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();
        }

     以上代码就是侦听用户设置的值,并保存到xml中,格式如下所示:
<boolean name="pref_key_auto_delete" value="true" />
<int name="MaxMmsMessagesPerThread" value="50" />
<int name="MaxSmsMessagesPerThread" value="500" />

2.2 功能体现

1)短信信息检查
    首先我们想到在神马情况下检查短信的信息数,然后如果超过限制就删除掉旧的短信,答案是肯定的:当用户收到短息时,因为这时需要存储短信到数据库中,这时肯定会检查。下面请看短信收到后的检查实现:
    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));
    }

这里大致就是首先看这个删除旧信息是否打开如果没有打开直接略过,不做任何操作如果打开,就根据设置的值来做判断,具体的操作如下代码所示:


这时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));
    }

这里大致就是首先看这个删除旧信息是否打开如果没有打开直接略过,不做任何操作如果打开,就根据设置的值来做判断,具体的操作如下代码所示:

        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)彩信接收检查

彩信的删除,其实现和短信的删除基本上没有神马区别:1、查询出所有改会话的彩信 ;2、根据设置的限制计算需要删除的彩信数量;3、删除数据库中超出限制的彩信
3)发送时的检查
一、短信
      发送时,会在短信保存进入数据库后进行检查,可以从WorkingMessage类的sendSmsWorker()方法中找到答案,至于sendSmsWorker方法,如果不清楚的同胞可以复习一下短信的发送流程http://blog.csdn.net/ceko_wu/article/details/7798659;这里简单看看该函数的内容
 
    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();
    }

二、彩信

       彩信发送时,也会像短信一样检查是否超出,如果超出则删除掉旧的彩信其相关代码在sendMmsWorker()里面。其原理和短信一样这里就不重复累述。

3、短信设置

 短信设置功能 :发送报告、短信模版管理、sim卡短信管理、短信中心号码显示
  

3.1 发送报告

    短信的发送报告该功能怎么使用实际上在前面的文章(短信的收发流程,http://blog.csdn.net/ceko_wu/article/details/7732970)中已经有提及了,这里就来看看这个选项是怎么控制的。
    该项在布局文件中的说明:
    <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 短信模版管理

短信模版,该功能并不是特别常用,如果你没有用过那很正常,android 2.3 该功能怎么使用?
     
                  图1  添加按钮                                             图2   短信模版                               图3 短信发送内容                                                             
     模版的使用其基本步骤就是这样,点击图1 中的红色button就会让我们选择一个具体的模版,效果如图3所示。
那图2这些模版在设置中怎么来管理?
      在设置中对于短信模版提供三个功能:添加新的模版、编辑已经存在的模版、删除模版。模版管理主界面MessageTemplate,该类来完成对模版的管理,下面是模版管理的主界面
图  4 短信模版管理主界面

      短信模版保存在xml文件中,但大家第一次开机就有一些模版,这些预制的模版怎么做到的,实际上预制的这些模版是一些厂商定制的,其基本实现原理:将预制的模版定义在arrays.xml中,系统初始化MessageTemplateProvider就将模版保存到/data/data/com.android.mms/files/message_template.xml文件中,这里存储的介质是xml文件,与我们经常使用的数据库不同,总之使用MessageTemplateProvider来管理模版,message_template.xml来存储模版。具体的实现大家可以参考MessageTemplateProvider代码。

3.3 sim卡短信管理

sim 卡短信管理,在智能机中,对于这个功能已经不是非常看重,一方面主要是sim卡的容量有限,二方面是存储短信到sim卡意义不大;当然在功能机时代该功能是做的非常强大的,收件箱、发件箱等等。智能机对于sim卡短信的管理主要是:查看、删除、将短信导入到phone中、将短信从phong存储到sim卡、接收到的短信自动保存到sim卡。其中接收到的短信存储到sim卡该功能后面提到的短信存储位置设置这一项有关联。这里不会对sim卡短信仔细分析,笔者计划用单独的文章来解读sim卡短信的结构。

3.4 短信中心号码

     如何获取短信中心号码?这个答案不是唯一的,但唯一的是我们都需要使用到RIL.java中的getSmscAddress方法
        
    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、短信签名设置

     短信签名设置界面如图4所示
    
图5 短信 签名设置界面
当我们开启短信签名并编辑一个短信签名,相关内容保存在com.android.mms_preferences.xml文件中,例如我开启短信签名,并将签名设置成“test”,其对应的xml文档你可以看到对应的设置属性。
<boolean name="pref_key_enable_signature" value="true" />
<string name="pref_key_edit_signature">test</string>
当我们发送短信的时候会根据短信配置文件中的值来判断是否有短信签名,这是在WorkingMessage的send方法中,如果大家对于短信的发送流程不是很清楚的大家可以复习一下。
        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、通知设置

    通知设置总体而言比较简单,开启通知,此时当我们发送接收到短信,此时会按照我们设置的方式来提醒我们,这里主要是一个铃声的选择,震动的选择,代码以接收为例;
     
图6 短信通知设置界面
这里用户进行对应的设置会保存在com.android.mms_preferences.xml文件中,其格式如下所示:
<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);
    }

从上面你可以看到的是短信提示设置怎么生效的。

7、小区广播

       由于本篇幅较长,下文会继续分析。


抱歉!评论已关闭.