package net.everythingandroid.smspopup.provider;
import net.everythingandroid.smspopup.BuildConfig;
import net.everythingandroid.smspopup.ui.SmsPopupActivity;
import net.everythingandroid.smspopup.util.Log;
import net.everythingandroid.smspopup.util.SmsMessageSender;
import net.everythingandroid.smspopup.util.SmsPopupUtils;
import net.everythingandroid.smspopup.util.SmsPopupUtils.ContactIdentification;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsMessage;
import android.telephony.SmsMessage.MessageClass;
import android.text.format.DateUtils;
public class SmsMmsMessage {
// Private EXTRAS strings
private static final String PREFIX = "net.everythingandroid.smspopup.";
private static final String EXTRAS_FROM_ADDRESS = PREFIX + "EXTRAS_FROM_ADDRESS";
private static final String EXTRAS_MESSAGE_BODY = PREFIX + "EXTRAS_MESSAGE_BODY";
private static final String EXTRAS_TIMESTAMP = PREFIX + "EXTRAS_TIMESTAMP";
private static final String EXTRAS_UNREAD_COUNT = PREFIX + "EXTRAS_UNREAD_COUNT";
private static final String EXTRAS_THREAD_ID = PREFIX + "EXTRAS_THREAD_ID";
private static final String EXTRAS_CONTACT_ID = PREFIX + "EXTRAS_CONTACT_ID";
private static final String EXTRAS_CONTACT_LOOKUP = PREFIX + "EXTRAS_CONTACT_LOOKUP";
private static final String EXTRAS_CONTACT_NAME = PREFIX + "EXTRAS_CONTACT_NAME";
private static final String EXTRAS_MESSAGE_TYPE = PREFIX + "EXTRAS_MESSAGE_TYPE";
private static final String EXTRAS_MESSAGE_ID = PREFIX + "EXTRAS_MESSAGE_ID";
private static final String EXTRAS_EMAIL_GATEWAY = PREFIX + "EXTRAS_EMAIL_GATEWAY";
// Public EXTRAS strings
public static final String EXTRAS_NOTIFY = PREFIX + "EXTRAS_NOTIFY";
public static final String EXTRAS_REMINDER_COUNT = PREFIX + "EXTRAS_REMINDER_COUNT";
public static final String EXTRAS_REPLYING = PREFIX + "EXTRAS_REPLYING";
public static final String EXTRAS_QUICKREPLY = PREFIX + "EXTRAS_QUICKREPLY";
// Message types
public static final int MESSAGE_TYPE_SMS = 0;
public static final int MESSAGE_TYPE_MMS = 1;
public static final int MESSAGE_TYPE_MESSAGE = 2;
// Timestamp compare buffer for incoming messages
public static final int MESSAGE_COMPARE_TIME_BUFFER = 5000; // 5 seconds
// Main message object private vars
private Context context;
private String fromAddress = null;
private String messageBody = null;
private long timestamp = 0;
private int unreadCount = 0;
private long threadId = 0;
private String contactId = null;
private String contactLookupKey = null;
private String contactName = null;
private int messageType = 0;
private boolean notify = true;
private int reminderCount = 0;
private long messageId = 0;
private boolean fromEmailGateway = false;
private MessageClass messageClass = null;
private String replyText = "";
// Sprint vars to check for special voicemail messages
private static final String SPRINT_BRAND = "sprint";
private static final String SPRINT_VOICEMAIL_PREFIX = "//ANDROID:";
/**
* Construct SmsMmsMessage given a raw message (created from pdu), used for
* when a message is initially received via the network.
*/
public SmsMmsMessage(Context _context, SmsMessage[] messages, long _timestamp) {
SmsMessage sms = messages[0];
context = _context;
timestamp = _timestamp;
messageType = MESSAGE_TYPE_SMS;
/*
* Fetch data from raw SMS
*/
fromAddress = sms.getDisplayOriginatingAddress();
fromEmailGateway = sms.isEmail();
messageClass = sms.getMessageClass();
String body = "";
try {
if (messages.length == 1 || sms.isReplace()) {
body = sms.getDisplayMessageBody();
} else {
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
bodyText.append(messages[i].getMessageBody());
}
body = bodyText.toString();
}
} catch (Exception e) {
if (BuildConfig.DEBUG) Log.v("SmsMmsMessage<init> exception: " + e.toString());
}
messageBody = body;
/*
* Lookup the rest of the info from the system db
*/
ContactIdentification contactIdentify = null;
// If this SMS is from an email gateway then lookup contactId by email
// address
if (fromEmailGateway) {
if (BuildConfig.DEBUG) Log.v("Sms came from email gateway");
contactIdentify = SmsPopupUtils.getPersonIdFromEmail(context, fromAddress);
contactName = fromAddress;
} else { // Else lookup contactId by phone number
if (BuildConfig.DEBUG) Log.v("Sms did NOT come from email gateway");
contactIdentify = SmsPopupUtils.getPersonIdFromPhoneNumber(context, fromAddress);
contactName = PhoneNumberUtils.formatNumber(fromAddress);
}
if (contactIdentify != null) {
contactId = contactIdentify.contactId;
contactLookupKey = contactIdentify.contactLookup;
contactName = contactIdentify.contactName;
}
unreadCount = SmsPopupUtils.getUnreadMessagesCount(context, timestamp, messageBody);
}
/**
* Construct SmsMmsMessage for getMmsDetails() - fetched from the MMS
* database table
*/
public SmsMmsMessage(Context _context, long _messageId, long _threadId, long _timestamp,
String _messageBody, int _unreadCount, int _messageType) {
context = _context;
messageId = _messageId;
threadId = _threadId;
timestamp = _timestamp;
messageBody = _messageBody;
unreadCount = _unreadCount;
messageType = _messageType;
fromAddress = SmsPopupUtils.getMmsAddress(context, messageId);
fromEmailGateway = false;
contactName = PhoneNumberUtils.formatNumber(fromAddress);
// Look up by phone number first
ContactIdentification contactIdentify =
SmsPopupUtils.getPersonIdFromPhoneNumber(context, fromAddress);
if (contactIdentify == null) {
// Lookup by email
contactIdentify = SmsPopupUtils.getPersonIdFromEmail(context, fromAddress);
if (contactIdentify != null) {
// If found then set "from email" flag
fromEmailGateway = true;
}
}
// If a contact was found then set fields
if (contactIdentify != null) {
contactId = contactIdentify.contactId;
contactLookupKey = contactIdentify.contactLookup;
contactName = contactIdentify.contactName;
}
}
/**
* Construct SmsMmsMessage for getSmsDetails() - info fetched from the SMS
* database table
*/
public SmsMmsMessage(Context _context, String _fromAddress, String _messageBody,
long _timestamp, long _threadId, int _unreadCount, long _messageId, int _messageType) {
context = _context;
fromAddress = _fromAddress;
messageBody = _messageBody;
timestamp = _timestamp;
messageType = _messageType;
ContactIdentification contactIdentify = null;
if (PhoneNumberUtils.isWellFormedSmsAddress(fromAddress)) {
contactIdentify = SmsPopupUtils.getPersonIdFromPhoneNumber(context, fromAddress);
contactName = PhoneNumberUtils.formatNumber(fromAddress);
fromEmailGateway = false;
} else {
contactIdentify = SmsPopupUtils.getPersonIdFromEmail(context, fromAddress);
contactName = fromAddress;
fromEmailGateway = true;
}
if (contactIdentify != null) {
contactId = contactIdentify.contactId;
contactLookupKey = contactIdentify.contactLookup;
contactName = contactIdentify.contactName;
}
unreadCount = _unreadCount;
threadId = _threadId;
messageId = _messageId;
}
/**
* Construct SmsMmsMessage from an extras bundle
*/
public SmsMmsMessage(Context _context, Bundle b) {
context = _context;
fromAddress = b.getString(EXTRAS_FROM_ADDRESS);
messageBody = b.getString(EXTRAS_MESSAGE_BODY);
timestamp = b.getLong(EXTRAS_TIMESTAMP);
contactId = b.getString(EXTRAS_CONTACT_ID);
contactLookupKey = b.getString(EXTRAS_CONTACT_LOOKUP);
contactName = b.getString(EXTRAS_CONTACT_NAME);
unreadCount = b.getInt(EXTRAS_UNREAD_COUNT, 1);
threadId = b.getLong(EXTRAS_THREAD_ID, 0);
messageType = b.getInt(EXTRAS_MESSAGE_TYPE, MESSAGE_TYPE_SMS);
notify = b.getBoolean(EXTRAS_NOTIFY, false);
reminderCount = b.getInt(EXTRAS_REMINDER_COUNT, 0);
messageId = b.getLong(EXTRAS_MESSAGE_ID, 0);
fromEmailGateway = b.getBoolean(EXTRAS_EMAIL_GATEWAY, false);
}
/**
* Construct SmsMmsMessage by specifying all data, only used for testing the
* notification from the preferences screen
*/
public SmsMmsMessage(Context _context, String _fromAddress, String _messageBody,
long _timestamp, String _contactId, String _contactLookup, String _contactName,
int _unreadCount, long _threadId, int _messageType) {
context = _context;
fromAddress = _fromAddress;
messageBody = _messageBody;
timestamp = _timestamp;
contactId = _contactId;
contactLookupKey = _contactLookup;
contactName = _contactName;
unreadCount = _unreadCount;
threadId = _threadId;
messageType = _messageType;
}
/**
* Convert all SmsMmsMessage data to an extras bundle to send via an intent
*/
public Bundle toBundle() {
Bundle b = new Bundle();
b.putString(EXTRAS_FROM_ADDRESS, fromAddress);
b.putString(EXTRAS_MESSAGE_BODY, messageBody);
b.putLong(EXTRAS_TIMESTAMP, timestamp);
b.putString(EXTRAS_CONTACT_ID, contactId);
b.putString(EXTRAS_CONTACT_LOOKUP, contactLookupKey);
b.putString(EXTRAS_CONTACT_NAME, contactName);
b.putInt(EXTRAS_UNREAD_COUNT, unreadCount);
b.putLong(EXTRAS_THREAD_ID, threadId);
b.putInt(EXTRAS_MESSAGE_TYPE, messageType);
b.putBoolean(EXTRAS_NOTIFY, notify);
b.putInt(EXTRAS_REMINDER_COUNT, reminderCount);
b.putLong(EXTRAS_MESSAGE_ID, messageId);
b.putBoolean(EXTRAS_EMAIL_GATEWAY, fromEmailGateway);
return b;
}
public Intent getPopupIntent() {
Intent popup = new Intent(context, SmsPopupActivity.class);
popup.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
popup.putExtras(toBundle());
return popup;
}
/**
* Fetch the "reply to" message intent
*
* @param replyToThread whether or not to reply using the message threadId
* or using the sender address
*
* @return the intent to pass to startActivity()
*/
public Intent getReplyIntent(boolean replyToThread) {
if (messageType == MESSAGE_TYPE_MMS) {
locateThreadId();
return SmsPopupUtils.getSmsToIntent(context, threadId);
} else if (messageType == MESSAGE_TYPE_SMS) {
locateThreadId();
/*
* There are two ways to reply to a message, by "viewing" the
* threadId or by sending a new message to the address. In most
* cases we should just execute the former, but in some cases its
* better to send a new message to an address (apps like Google
* Voice will intercept this intent as they don't seem to look at
* the threadId).
*/
if (replyToThread && threadId > 0) {
if (BuildConfig.DEBUG) Log.v("Replying by threadId: " + threadId);
return SmsPopupUtils.getSmsToIntent(context, threadId);
} else {
if (BuildConfig.DEBUG) Log.v("Replying by address: " + fromAddress);
return SmsPopupUtils.getSmsToIntent(context, fromAddress);
}
}
return null;
}
public Intent getReplyIntent() {
return getReplyIntent(true);
}
public void setThreadRead() {
locateThreadId();
SmsPopupUtils.setThreadRead(context, threadId);
}
public void setMessageRead() {
locateMessageId();
SmsPopupUtils.setMessageRead(context, messageId, messageType);
}
public void setUnreadCount(int _unreadCount) {
unreadCount = _unreadCount;
}
public int getUnreadCount() {
return unreadCount;
}
public long getTimestamp() {
return timestamp;
}
public MessageClass getMessageClass() {
return messageClass;
}
public CharSequence getFormattedTimestamp() {
CharSequence formattedTime;
try {
formattedTime = DateUtils.formatDateTime(context, timestamp, DateUtils.FORMAT_SHOW_TIME);
} catch (Exception e) {
if (BuildConfig.DEBUG) Log.v("Error formatting timestamp + " + timestamp);
formattedTime = "";
}
return formattedTime;
}
public String getContactName() {
if (contactName == null) {
contactName = context.getString(android.R.string.unknownName);
}
return contactName;
}
public String getMessageBody() {
if (messageBody == null) {
messageBody = "";
}
return messageBody;
}
public int getMessageType() {
return messageType;
}
public boolean isSms() {
return messageType == MESSAGE_TYPE_SMS;
}
public boolean isMms() {
return messageType == MESSAGE_TYPE_MMS;
}
public void setNotify(boolean mNotify) {
notify = mNotify;
}
public boolean shouldNotify() {
if (BuildConfig.DEBUG) Log.v("shouldNotify() - notify is " + notify);
return notify;
}
public int getReminderCount() {
return reminderCount;
}
public void updateReminderCount(int count) {
reminderCount = count;
}
public void incrementReminderCount() {
reminderCount++;
}
public void delete() {
SmsPopupUtils.deleteMessage(context, getMessageId(), threadId, messageType);
}
public void locateThreadId() {
if (threadId == 0) {
threadId = SmsPopupUtils.findThreadIdFromAddress(context, fromAddress);
}
}
public long getThreadId() {
locateThreadId();
return threadId;
}
public void locateMessageId() {
if (messageId == 0) {
if (threadId == 0) {
locateThreadId();
}
messageId = SmsPopupUtils.findMessageId(
context, threadId, timestamp, messageBody, messageType);
}
}
public long getMessageId() {
locateMessageId();
return messageId;
}
public String getContactId() {
return contactId;
}
public String getContactLookupKey() {
return contactLookupKey;
}
public Uri getContactLookupUri() {
if (contactId == null) {
return null;
}
return Uri.withAppendedPath(Contacts.CONTENT_URI, contactId);
// This seems to fail even though the docs say to use it.
// return Contacts.getLookupUri(Long.valueOf(contactId),
// contactLookupKey);
}
public String getAddress() {
return fromAddress;
}
public boolean isEmail() {
return fromEmailGateway;
}
public String getReplyText() {
return replyText;
}
public void setReplyText(String text) {
replyText = text;
}
/**
* Sned a reply to this message
*
* @param quickreply the message to send
* @return true of the message was sent, false otherwise
*/
public boolean replyToMessage(String quickReply) {
// Mark the message we're replying to as read
setMessageRead();
// Send new message
SmsMessageSender sender =
new SmsMessageSender(context, new String[] {fromAddress}, quickReply, getThreadId());
return sender.sendMessage();
}
// Checks if user is on carrier Sprint and message is a special system message
public boolean isSprintVisualVoicemail() {
if (!SPRINT_BRAND.equals(Build.BRAND)) {
return false;
}
if (messageBody != null) {
if (messageBody.trim().startsWith(SPRINT_VOICEMAIL_PREFIX)) {
return true;
}
}
return false;
}
}