FileDocCategorySizeDatePackage
MessagingNotification.javaAPI DocAndroid 1.5 API24071Wed May 06 22:42:46 BST 2009com.android.mms.transaction

MessagingNotification

public class MessagingNotification extends Object
This class is used to update the notification indicator. It will check whether there are unread messages. If yes, it would show the notification indicator, otherwise, hide the indicator.

Fields Summary
public static final String
NOTIFICATION_CLICK_RECEIVER
private static final String
TAG
private static final int
NOTIFICATION_ID
public static final int
MESSAGE_FAILED_NOTIFICATION_ID
public static final int
DOWNLOAD_FAILED_NOTIFICATION_ID
private static final String[]
MMS_STATUS_PROJECTION
private static final String[]
SMS_STATUS_PROJECTION
private static final int
COLUMN_THREAD_ID
private static final int
COLUMN_DATE
private static final int
COLUMN_MMS_ID
private static final int
COLUMN_SMS_ADDRESS
private static final int
COLUMN_SUBJECT
private static final int
COLUMN_SUBJECT_CS
private static final int
COLUMN_SMS_BODY
private static final String
NEW_INCOMING_SM_CONSTRAINT
private static final String
NEW_INCOMING_MM_CONSTRAINT
private static final MmsSmsNotificationInfoComparator
INFO_COMPARATOR
private static final android.net.Uri
UNDELIVERED_URI
Constructors Summary
private MessagingNotification()


      
    
Methods Summary
private static final intaccumulateNotificationInfo(java.util.SortedSet set, com.android.mms.transaction.MessagingNotification$MmsSmsNotificationInfo info)

        if (info != null) {
            set.add(info);

            return info.mCount;
        }

        return 0;
    
protected static java.lang.CharSequencebuildTickerMessage(android.content.Context context, java.lang.String address, java.lang.String subject, java.lang.String body)

        String displayAddress = ContactInfoCache.getInstance()
                .getContactName(context, address);
        
        StringBuilder buf = new StringBuilder(
                displayAddress == null
                ? ""
                : displayAddress.replace('\n", ' ").replace('\r", ' "));
        buf.append(':").append(' ");

        int offset = buf.length();
        if (!TextUtils.isEmpty(subject)) {
            subject = subject.replace('\n", ' ").replace('\r", ' ");
            buf.append(subject);
            buf.append(' ");
        }

        if (!TextUtils.isEmpty(body)) {
            body = body.replace('\n", ' ").replace('\r", ' ");
            buf.append(body);
        }

        SpannableString spanText = new SpannableString(buf.toString());
        spanText.setSpan(new StyleSpan(Typeface.BOLD), 0, offset,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        return spanText;
    
public static voidcancelNotification(android.content.Context context, int notificationId)

        NotificationManager nm = (NotificationManager) context.getSystemService(
                Context.NOTIFICATION_SERVICE);

        nm.cancel(notificationId);
    
private static android.content.IntentgetAppIntent()

        Intent appIntent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI);

        appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        return appIntent;
   
private static intgetDownloadFailedMessageCount(android.content.Context context)

        // Look for any messages in the MMS Inbox that are of the type
        // NOTIFICATION_IND (i.e. not already downloaded) and in the
        // permanent failure state.  If there are none, cancel any
        // failed download notification.
        Cursor c = SqliteWrapper.query(context, context.getContentResolver(),
                Mms.Inbox.CONTENT_URI, null,
                Mms.MESSAGE_TYPE + "=" +
                    String.valueOf(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) +
                " AND " + Mms.STATUS + "=" +
                    String.valueOf(DownloadManager.STATE_PERMANENT_FAILURE),
                null, null);
        if (c == null) {
            return 0;
        }
        int count = c.getCount();
        c.close();
        return count;
    
public static final com.android.mms.transaction.MessagingNotification$MmsSmsNotificationInfogetMmsNewMessageNotificationInfo(android.content.Context context, java.util.Set threads)

        ContentResolver resolver = context.getContentResolver();
        Cursor cursor = SqliteWrapper.query(context, resolver, Mms.CONTENT_URI,
                            MMS_STATUS_PROJECTION, NEW_INCOMING_MM_CONSTRAINT,
                            null, Mms.DATE + " desc");

        if (cursor == null) {
            return null;
        }
        
        try {
            if (!cursor.moveToFirst()) {
                return null;
            }
            long msgId = cursor.getLong(COLUMN_MMS_ID);
            Uri msgUri = Mms.CONTENT_URI.buildUpon().appendPath(
                    Long.toString(msgId)).build();
            String address = AddressUtils.getFrom(context, msgUri);
            String subject = getMmsSubject(
                    cursor.getString(COLUMN_SUBJECT), cursor.getInt(COLUMN_SUBJECT_CS));
            long threadId = cursor.getLong(COLUMN_THREAD_ID);
            long timeMillis = cursor.getLong(COLUMN_DATE) * 1000;

            MmsSmsNotificationInfo info = getNewMessageNotificationInfo(
                    address, subject, context,
                    R.drawable.stat_notify_mms, null, threadId,
                    timeMillis, cursor.getCount());

            threads.add(threadId);
            while (cursor.moveToNext()) {
                threads.add(cursor.getLong(COLUMN_THREAD_ID));
            }

            return info;
        } finally {
            cursor.close();
        }
    
private static java.lang.StringgetMmsSubject(java.lang.String sub, int charset)

        return TextUtils.isEmpty(sub) ? ""
                : new EncodedStringValue(charset, PduPersister.getBytes(sub)).getString();
    
private static final com.android.mms.transaction.MessagingNotification$MmsSmsNotificationInfogetNewMessageNotificationInfo(java.lang.String address, java.lang.String body, android.content.Context context, int iconResourceId, java.lang.String subject, long threadId, long timeMillis, int count)

        Intent clickIntent = getAppIntent();
        clickIntent.setData(
                Uri.withAppendedPath(
                        clickIntent.getData(), Long.toString(threadId)));
        clickIntent.setAction(Intent.ACTION_VIEW);

        String senderInfo = buildTickerMessage(
                context, address, null, null).toString();
        String senderInfoName = senderInfo.substring(
                0, senderInfo.length() - 2);
        CharSequence ticker = buildTickerMessage(
                context, address, subject, body);

        return new MmsSmsNotificationInfo(
                clickIntent, body, iconResourceId, ticker, timeMillis,
                senderInfoName, count);
    
public static final com.android.mms.transaction.MessagingNotification$MmsSmsNotificationInfogetSmsNewMessageNotificationInfo(android.content.Context context, java.util.Set threads)

        ContentResolver resolver = context.getContentResolver();
        Cursor cursor = SqliteWrapper.query(context, resolver, Sms.CONTENT_URI,
                            SMS_STATUS_PROJECTION, NEW_INCOMING_SM_CONSTRAINT,
                            null, Sms.DATE + " desc");

        if (cursor == null) {
            return null;
        }
        
        try {
            if (!cursor.moveToFirst()) {
                return null;
            }

            String address = cursor.getString(COLUMN_SMS_ADDRESS);
            String body = cursor.getString(COLUMN_SMS_BODY);
            long threadId = cursor.getLong(COLUMN_THREAD_ID);
            long timeMillis = cursor.getLong(COLUMN_DATE);

            MmsSmsNotificationInfo info = getNewMessageNotificationInfo(
                    address, body, context, R.drawable.stat_notify_sms,
                    null, threadId, timeMillis, cursor.getCount());

            threads.add(threadId);
            while (cursor.moveToNext()) {
                threads.add(cursor.getLong(COLUMN_THREAD_ID));
            }

            return info;
        } finally {
            cursor.close();
        }
    
private static intgetUndeliveredMessageCount(android.content.Context context, long[] threadIdResult)

        Cursor undeliveredCursor = SqliteWrapper.query(context, context.getContentResolver(),
                UNDELIVERED_URI, new String[] { Mms.THREAD_ID }, "read=0", null, null);
        if (undeliveredCursor == null) {
            return 0;
        }
        int count = undeliveredCursor.getCount();
        try {
            if (threadIdResult != null && undeliveredCursor.moveToFirst()) {
                threadIdResult[0] = undeliveredCursor.getLong(0);
                
                if (threadIdResult.length >= 2) {
                    // Test to see if all the undelivered messages belong to the same thread.
                    long firstId = threadIdResult[0];
                    while (undeliveredCursor.moveToNext()) {
                        if (undeliveredCursor.getLong(0) != firstId) {
                            firstId = 0;
                            break;
                        }
                    }
                    threadIdResult[1] = firstId;    // non-zero if all ids are the same
                }
            }
        } finally {
            undeliveredCursor.close();
        }
        return count;
    
public static voidnotifyDownloadFailed(android.content.Context context, long threadId)

        notifyFailed(context, true, threadId, false);
    
private static voidnotifyFailed(android.content.Context context, boolean isDownload, long threadId, boolean noisy)

        // TODO factor out common code for creating notifications
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

        boolean enabled = sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_ENABLED, true);
        if (!enabled) {
            return;
        }

        NotificationManager nm = (NotificationManager)
                context.getSystemService(Context.NOTIFICATION_SERVICE);
        
        // Strategy:
        // a. If there is a single failure notification, tapping on the notification goes
        //    to the compose view.
        // b. If there are two failure it stays in the thread view. Selecting one undelivered 
        //    thread will dismiss one undelivered notification but will still display the
        //    notification.If you select the 2nd undelivered one it will dismiss the notification.
        
        long[] msgThreadId = {0};
        int totalFailedCount = getUndeliveredMessageCount(context, msgThreadId);
        
        Intent failedIntent;
        Notification notification = new Notification();
        String title;
        String description;
        if (totalFailedCount > 1) {
            description = context.getString(R.string.notification_failed_multiple,
                    Integer.toString(totalFailedCount));
            title = context.getString(R.string.notification_failed_multiple_title);

            failedIntent = new Intent(context, ConversationList.class);
        } else {
            title = isDownload ?
                        context.getString(R.string.message_download_failed_title) :
                        context.getString(R.string.message_send_failed_title);
            
            description = context.getString(R.string.message_failed_body);
            threadId = (msgThreadId[0] != 0 ? msgThreadId[0] : 0);
            
            failedIntent = new Intent(context, ComposeMessageActivity.class);
            failedIntent.putExtra("thread_id", threadId);         
            failedIntent.putExtra("undelivered_flag", true);
        }

        failedIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                context, 0, failedIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        notification.icon = R.drawable.stat_notify_sms_failed;

        notification.tickerText = title;

        notification.setLatestEventInfo(context, title, description, pendingIntent);

        if (noisy) {
            boolean vibrate = sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_VIBRATE, true);
            if (vibrate) {
                notification.defaults |= Notification.DEFAULT_VIBRATE;
            }

            String ringtoneStr = sp.getString(MessagingPreferenceActivity.NOTIFICATION_RINGTONE, null);
            notification.sound = TextUtils.isEmpty(ringtoneStr) ? null : Uri.parse(ringtoneStr);
        }
        
        if (isDownload) {
            nm.notify(DOWNLOAD_FAILED_NOTIFICATION_ID, notification);
        } else {
            nm.notify(MESSAGE_FAILED_NOTIFICATION_ID, notification);
        }
    
public static voidnotifySendFailed(android.content.Context context)

        notifyFailed(context, false, 0, false);
    
public static voidnotifySendFailed(android.content.Context context, boolean noisy)

        notifyFailed(context, false, 0, noisy);
    
public static voidupdateAllNotifications(android.content.Context context)
Updates all pending notifications, clearing or updating them as necessary. This task is completed in the background on a worker thread.

        new Thread(new Runnable() {
            public void run() {
                updateNewMessageIndicator(context);
                updateSendFailedNotification(context);
                updateDownloadFailedNotification(context);
            }
        }).start();
    
public static voidupdateDownloadFailedNotification(android.content.Context context)

        if (getDownloadFailedMessageCount(context) < 1) {
            cancelNotification(context, DOWNLOAD_FAILED_NOTIFICATION_ID);
        }
    
public static voidupdateNewMessageIndicator(android.content.Context context)
Checks to see if there are any unread messages or delivery reports. Shows the most recent notification if there is one.

param
context the context to use

        updateNewMessageIndicator(context, false);
    
public static voidupdateNewMessageIndicator(android.content.Context context, boolean isNew)
Checks to see if there are any unread messages or delivery reports. Shows the most recent notification if there is one.

param
context the context to use
param
isNew if notify a new message comes, it should be true, otherwise, false.

        SortedSet<MmsSmsNotificationInfo> accumulator =
                new TreeSet<MmsSmsNotificationInfo>(INFO_COMPARATOR);
        Set<Long> threads = new HashSet<Long>(4);
        
        int count = 0;
        count += accumulateNotificationInfo(
                accumulator, getMmsNewMessageNotificationInfo(context, threads));
        count += accumulateNotificationInfo(
                accumulator, getSmsNewMessageNotificationInfo(context, threads));

        cancelNotification(context, NOTIFICATION_ID);
        if (!accumulator.isEmpty()) {
            accumulator.first().deliver(context, isNew, count, threads.size());
        }
    
public static voidupdateNewMessageIndicator(android.content.Context context, long threadId)
Deletes any delivery report notifications for the specified thread, then checks to see if there are any unread messages or delivery reports. Shows the most recent notification if there is one.

param
context the context to use
param
threadId the thread for which to clear delivery notifications

        updateNewMessageIndicator(context);
    
private static voidupdateNotification(android.content.Context context, android.content.Intent clickIntent, java.lang.String description, int iconRes, boolean isNew, java.lang.CharSequence ticker, long timeMillis, java.lang.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 we have more than one unique thread, change the title (which would
        // normally be the contact who sent the message) to a generic one that
        // makes sense for multiple senders, and change the Intent to take the
        // user to the conversation list instead of the specific thread.
        if (uniqueThreadCount > 1) {
            title = context.getString(R.string.notification_multiple_title);
            clickIntent = getAppIntent();
            clickIntent.setAction(Intent.ACTION_MAIN);
            clickIntent.setType("vnd.android-dir/mms-sms");
        }
        
        // If there is more than one message, change the description (which
        // would normally be a snippet of the individual message text) to
        // a string indicating how many unread messages there are.
        if (messageCount > 1) {
            description = context.getString(R.string.notification_multiple,
                    Integer.toString(messageCount));
        }

        // Make a startActivity() PendingIntent for the notification.
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        // Update the notification.
        notification.setLatestEventInfo(context, title, description, pendingIntent);

        if (isNew) {
            boolean vibrate = sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_VIBRATE, true);
            if (vibrate) {
                notification.defaults |= Notification.DEFAULT_VIBRATE;
            }

            String ringtoneStr = sp
                    .getString(MessagingPreferenceActivity.NOTIFICATION_RINGTONE, null);
            notification.sound = TextUtils.isEmpty(ringtoneStr) ? null : Uri.parse(ringtoneStr);
        }

        notification.flags |= Notification.FLAG_SHOW_LIGHTS;
        notification.ledARGB = 0xff00ff00;
        notification.ledOnMS = 500;
        notification.ledOffMS = 2000;

        NotificationManager nm = (NotificationManager)
            context.getSystemService(Context.NOTIFICATION_SERVICE);

        nm.notify(NOTIFICATION_ID, notification);
    
public static voidupdateSendFailedNotification(android.content.Context context)

        if (getUndeliveredMessageCount(context, null) < 1) {
            cancelNotification(context, MESSAGE_FAILED_NOTIFICATION_ID);
        } else {
            notifySendFailed(context);      // rebuild and adjust the message count if necessary.
        }
    
public static voidupdateSendFailedNotificationForThread(android.content.Context context, long threadId)
If all the undelivered messages belong to "threadId", cancel the notification.

        long[] msgThreadId = {0, 0};
        if (getUndeliveredMessageCount(context, msgThreadId) > 0 
                && msgThreadId[0] == threadId
                && msgThreadId[1] != 0) {
            cancelNotification(context, MESSAGE_FAILED_NOTIFICATION_ID);
        }