/*
* Copyright (C) 2008 Esmertec AG.
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mms.transaction;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF;
import com.android.mms.R;
import com.android.mms.LogTag;
import com.android.mms.data.Contact;
import com.android.mms.data.Conversation;
import com.android.mms.ui.ComposeMessageActivity;
import com.android.mms.ui.ConversationList;
import com.android.mms.ui.MessagingPreferenceActivity;
import com.android.mms.util.AddressUtils;
import com.android.mms.util.DownloadManager;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduPersister;
import android.database.sqlite.SqliteWrapper;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Telephony.Mms;
import android.provider.Telephony.Sms;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
import android.widget.Toast;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* 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.
*/
public class MessagingNotification {
private static final String TAG = LogTag.APP;
private static final int NOTIFICATION_ID = 123;
public static final int MESSAGE_FAILED_NOTIFICATION_ID = 789;
public static final int DOWNLOAD_FAILED_NOTIFICATION_ID = 531;
// This must be consistent with the column constants below.
private static final String[] MMS_STATUS_PROJECTION = new String[] {
Mms.THREAD_ID, Mms.DATE, Mms._ID, Mms.SUBJECT, Mms.SUBJECT_CHARSET };
// This must be consistent with the column constants below.
private static final String[] SMS_STATUS_PROJECTION = new String[] {
Sms.THREAD_ID, Sms.DATE, Sms.ADDRESS, Sms.SUBJECT, Sms.BODY };
// These must be consistent with MMS_STATUS_PROJECTION and
// SMS_STATUS_PROJECTION.
private static final int COLUMN_THREAD_ID = 0;
private static final int COLUMN_DATE = 1;
private static final int COLUMN_MMS_ID = 2;
private static final int COLUMN_SMS_ADDRESS = 2;
private static final int COLUMN_SUBJECT = 3;
private static final int COLUMN_SUBJECT_CS = 4;
private static final int COLUMN_SMS_BODY = 4;
private static final String NEW_INCOMING_SM_CONSTRAINT =
"(" + Sms.TYPE + " = " + Sms.MESSAGE_TYPE_INBOX
+ " AND " + Sms.SEEN + " = 0)";
private static final String NEW_DELIVERY_SM_CONSTRAINT =
"(" + Sms.TYPE + " = " + Sms.MESSAGE_TYPE_SENT
+ " AND " + Sms.STATUS + " = "+ Sms.STATUS_COMPLETE +")";
private static final String NEW_INCOMING_MM_CONSTRAINT =
"(" + Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_INBOX
+ " AND " + Mms.SEEN + "=0"
+ " AND (" + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_NOTIFICATION_IND
+ " OR " + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_RETRIEVE_CONF + "))";
private static final MmsSmsNotificationInfoComparator INFO_COMPARATOR =
new MmsSmsNotificationInfoComparator();
private static final Uri UNDELIVERED_URI = Uri.parse("content://mms-sms/undelivered");
private final static String NOTIFICATION_DELETED_ACTION =
"com.android.mms.NOTIFICATION_DELETED_ACTION";
public static class OnDeletedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.d(TAG, "[MessagingNotification] clear notification: mark all msgs seen");
}
Conversation.markAllConversationsAsSeen(context);
}
};
private static OnDeletedReceiver sNotificationDeletedReceiver = new OnDeletedReceiver();
private static Intent sNotificationOnDeleteIntent;
private static Handler mToastHandler = new Handler();
private MessagingNotification() {
}
public static void init(Context context) {
// set up the intent filter for notification deleted action
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(NOTIFICATION_DELETED_ACTION);
context.registerReceiver(sNotificationDeletedReceiver, intentFilter);
// initialize the notification deleted action
sNotificationOnDeleteIntent = new Intent(NOTIFICATION_DELETED_ACTION);
}
/**
* Checks to see if there are any "unseen" messages or delivery
* reports. Shows the most recent notification if there is one.
* Does its work and query in a worker thread.
*
* @param context the context to use
*/
public static void nonBlockingUpdateNewMessageIndicator(final Context context,
final boolean isNew,
final boolean isStatusMessage) {
new Thread(new Runnable() {
public void run() {
blockingUpdateNewMessageIndicator(context, isNew, isStatusMessage);
}
}).start();
}
/**
* Checks to see if there are any "unseen" 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.
*/
public static void blockingUpdateNewMessageIndicator(Context context, boolean isNew,
boolean isStatusMessage) {
SortedSet<MmsSmsNotificationInfo> accumulator =
new TreeSet<MmsSmsNotificationInfo>(INFO_COMPARATOR);
MmsSmsDeliveryInfo delivery = null;
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()) {
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.d(TAG, "blockingUpdateNewMessageIndicator: count=" + count +
", isNew=" + isNew);
}
accumulator.first().deliver(context, isNew, count, threads.size());
}
// And deals with delivery reports (which use Toasts). It's safe to call in a worker
// thread because the toast will eventually get posted to a handler.
delivery = getSmsNewDeliveryInfo(context);
if (delivery != null) {
delivery.deliver(context, isStatusMessage);
}
}
/**
* Updates all pending notifications, clearing or updating them as
* necessary.
*/
public static void blockingUpdateAllNotifications(final Context context) {
nonBlockingUpdateNewMessageIndicator(context, false, false);
updateSendFailedNotification(context);
updateDownloadFailedNotification(context);
}
private static final int accumulateNotificationInfo(
SortedSet set, MmsSmsNotificationInfo info) {
if (info != null) {
set.add(info);
return info.mCount;
}
return 0;
}
private static final class MmsSmsDeliveryInfo {
public CharSequence mTicker;
public long mTimeMillis;
public MmsSmsDeliveryInfo(CharSequence ticker, long timeMillis) {
mTicker = ticker;
mTimeMillis = timeMillis;
}
public void deliver(Context context, boolean isStatusMessage) {
updateDeliveryNotification(
context, isStatusMessage, mTicker, mTimeMillis);
}
}
private static final class MmsSmsNotificationInfo {
public Intent mClickIntent;
public String mDescription;
public int mIconResourceId;
public CharSequence mTicker;
public long mTimeMillis;
public String mTitle;
public int mCount;
public MmsSmsNotificationInfo(
Intent clickIntent, String description, int iconResourceId,
CharSequence ticker, long timeMillis, String title, int count) {
mClickIntent = clickIntent;
mDescription = description;
mIconResourceId = iconResourceId;
mTicker = ticker;
mTimeMillis = timeMillis;
mTitle = title;
mCount = count;
}
public void deliver(Context context, boolean isNew, int count, int uniqueThreads) {
updateNotification(
context, mClickIntent, mDescription, mIconResourceId, isNew,
(isNew? mTicker : null), // only display the ticker if the message is new
mTimeMillis, mTitle, count, uniqueThreads);
}
public long getTime() {
return mTimeMillis;
}
}
private static final class MmsSmsNotificationInfoComparator
implements Comparator<MmsSmsNotificationInfo> {
public int compare(
MmsSmsNotificationInfo info1, MmsSmsNotificationInfo info2) {
return Long.signum(info2.getTime() - info1.getTime());
}
}
private static final MmsSmsNotificationInfo getMmsNewMessageNotificationInfo(
Context context, Set<Long> threads) {
ContentResolver resolver = context.getContentResolver();
// This query looks like this when logged:
// I/Database( 147): elapsedTime4Sql|/data/data/com.android.providers.telephony/databases/
// mmssms.db|0.362 ms|SELECT thread_id, date, _id, sub, sub_cs FROM pdu WHERE ((msg_box=1
// AND seen=0 AND (m_type=130 OR m_type=132))) ORDER BY date desc
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);
Contact contact = Contact.get(address, false);
if (contact.getSendToVoicemail()) {
// don't notify
return null;
}
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;
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.d(TAG, "getMmsNewMessageNotificationInfo: count=" + cursor.getCount() +
", first addr = " + address + ", thread_id=" + threadId);
}
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 final MmsSmsDeliveryInfo getSmsNewDeliveryInfo(Context context) {
ContentResolver resolver = context.getContentResolver();
Cursor cursor = SqliteWrapper.query(context, resolver, Sms.CONTENT_URI,
SMS_STATUS_PROJECTION, NEW_DELIVERY_SM_CONSTRAINT,
null, Sms.DATE);
if (cursor == null)
return null;
try {
if (!cursor.moveToLast())
return null;
String address = cursor.getString(COLUMN_SMS_ADDRESS);
long timeMillis = 3000;
return new MmsSmsDeliveryInfo(String.format(
context.getString(R.string.delivery_toast_body), address),
timeMillis);
} finally {
cursor.close();
}
}
private static final MmsSmsNotificationInfo getSmsNewMessageNotificationInfo(
Context context, Set<Long> 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);
Contact contact = Contact.get(address, false);
if (contact.getSendToVoicemail()) {
// don't notify
return null;
}
String body = cursor.getString(COLUMN_SMS_BODY);
long threadId = cursor.getLong(COLUMN_THREAD_ID);
long timeMillis = cursor.getLong(COLUMN_DATE);
if (Log.isLoggable(LogTag.APP, Log.VERBOSE))
{
Log.d(TAG, "getSmsNewMessageNotificationInfo: count=" + cursor.getCount() +
", first addr=" + address + ", thread_id=" + threadId);
}
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 final MmsSmsNotificationInfo getNewMessageNotificationInfo(
String address,
String body,
Context context,
int iconResourceId,
String subject,
long threadId,
long timeMillis,
int count) {
Intent clickIntent = ComposeMessageActivity.createIntent(context, threadId);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
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 void cancelNotification(Context context, int notificationId) {
NotificationManager nm = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
nm.cancel(notificationId);
}
private static void updateDeliveryNotification(final Context context,
boolean isStatusMessage,
final CharSequence message,
final long timeMillis) {
if (!isStatusMessage) {
return;
}
if (!MessagingPreferenceActivity.getNotificationEnabled(context)) {
return;
}
mToastHandler.post(new Runnable() {
public void run() {
Toast.makeText(context, message, (int)timeMillis).show();
}
});
}
private static void updateNotification(
Context context,
Intent clickIntent,
String description,
int iconRes,
boolean isNew,
CharSequence ticker,
long timeMillis,
String title,
int messageCount,
int uniqueThreadCount) {
if (!MessagingPreferenceActivity.getNotificationEnabled(context)) {
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 = 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 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 "unseen" 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) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
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;
if (vibrateAlways || vibrateSilent && nowSilent) {
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.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);
}
protected static CharSequence buildTickerMessage(
Context context, String address, String subject, String body) {
String displayAddress = Contact.get(address, true).getName();
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;
}
private static String getMmsSubject(String sub, int charset) {
return TextUtils.isEmpty(sub) ? ""
: new EncodedStringValue(charset, PduPersister.getBytes(sub)).getString();
}
public static void notifyDownloadFailed(Context context, long threadId) {
notifyFailed(context, true, threadId, false);
}
public static void notifySendFailed(Context context) {
notifyFailed(context, false, 0, false);
}
public static void notifySendFailed(Context context, boolean noisy) {
notifyFailed(context, false, 0, noisy);
}
private static void notifyFailed(Context context, boolean isDownload, long threadId,
boolean noisy) {
// TODO factor out common code for creating notifications
boolean enabled = MessagingPreferenceActivity.getNotificationEnabled(context);
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, 1}; // Dummy initial values, just to initialize the memory
int totalFailedCount = getUndeliveredMessageCount(context, msgThreadId);
if (totalFailedCount == 0 && !isDownload) {
return;
}
// The getUndeliveredMessageCount method puts a non-zero value in msgThreadId[1] if all
// failures are from the same thread.
// If isDownload is true, we're dealing with 1 specific failure; therefore "all failed" are
// indeed in the same thread since there's only 1.
boolean allFailedInSameThread = (msgThreadId[1] != 0) || isDownload;
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);
} 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);
}
if (allFailedInSameThread) {
failedIntent = new Intent(context, ComposeMessageActivity.class);
if (isDownload) {
// When isDownload is true, the valid threadId is passed into this function.
failedIntent.putExtra("failed_download_flag", true);
} else {
threadId = msgThreadId[0];
failedIntent.putExtra("undelivered_flag", true);
}
failedIntent.putExtra("thread_id", threadId);
} else {
failedIntent = new Intent(context, ConversationList.class);
}
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) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
boolean vibrate = sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_VIBRATE,
false /* don't vibrate by default */);
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);
}
}
/**
* Query the DB and return the number of undelivered messages (total for both SMS and MMS)
* @param context The context
* @param threadIdResult A container to put the result in, according to the following rules:
* threadIdResult[0] contains the thread id of the first message.
* threadIdResult[1] is nonzero if the thread ids of all the messages are the same.
* You can pass in null for threadIdResult.
* You can pass in a threadIdResult of size 1 to avoid the comparison of each thread id.
*/
private static int getUndeliveredMessageCount(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 void updateSendFailedNotification(Context context) {
if (getUndeliveredMessageCount(context, null) < 1) {
cancelNotification(context, MESSAGE_FAILED_NOTIFICATION_ID);
} else {
notifySendFailed(context); // rebuild and adjust the message count if necessary.
}
}
/**
* If all the undelivered messages belong to "threadId", cancel the notification.
*/
public static void updateSendFailedNotificationForThread(Context context, long threadId) {
long[] msgThreadId = {0, 0};
if (getUndeliveredMessageCount(context, msgThreadId) > 0
&& msgThreadId[0] == threadId
&& msgThreadId[1] != 0) {
cancelNotification(context, MESSAGE_FAILED_NOTIFICATION_ID);
}
}
private static int getDownloadFailedMessageCount(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 void updateDownloadFailedNotification(Context context) {
if (getDownloadFailedMessageCount(context) < 1) {
cancelNotification(context, DOWNLOAD_FAILED_NOTIFICATION_ID);
}
}
public static boolean isFailedToDeliver(Intent intent) {
return (intent != null) && intent.getBooleanExtra("undelivered_flag", false);
}
public static boolean isFailedToDownload(Intent intent) {
return (intent != null) && intent.getBooleanExtra("failed_download_flag", false);
}
}