package org.xbmc.android.util;
import java.io.ByteArrayInputStream;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.telephony.PhoneNumberUtils;
import android.telephony.gsm.SmsMessage;
import android.text.format.DateUtils;
/**
* TODO Once we move to 1.6+, waste the deprecated code.
*/
@SuppressWarnings("deprecation")
public class SmsMmsMessage {
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_NAME = PREFIX + "EXTRAS_CONTACT_NAME";
private static final String EXTRAS_CONTACT_PHOTO = PREFIX + "EXTRAS_CONTACT_PHOTO";
private static final String EXTRAS_MESSAGE_TYPE = PREFIX + "EXTRAS_MESSAGE_TYPE";
private static final String EXTRAS_MESSAGE_ID = PREFIX + "EXTRAS_MESSAGE_ID";
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";
public static final int MESSAGE_TYPE_SMS = 0;
public static final int MESSAGE_TYPE_MMS = 1;
private Context mContext;
final private String mFromAddress;
final private String mMessageBody;
final private long mTimestamp;
final private int mUnreadCount;
final private long mThreadId;
private String mContactId;
private String mContactName;
final private byte[] mContactPhoto;
final private int mMessageType;
private boolean mNotify = true;
private int mReminderCount = 0;
private long mMessageId = 0;
/**
* Construct SmsMmsMessage with minimal information - this is useful for
* when a raw SMS comes in which just contains address, body and timestamp.
* We must then look in the database for the rest of the information
*/
public SmsMmsMessage(Context _context, String _fromAddress, String _messageBody, long _timestamp, int _messageType) {
mContext = _context;
mFromAddress = _fromAddress;
mMessageBody = _messageBody == null ? "" : _messageBody;
mTimestamp = _timestamp;
mMessageType = _messageType;
mContactId = SmsPopupUtils.getPersonIdFromPhoneNumber(mContext, mFromAddress);
mContactName = SmsPopupUtils.getPersonName(mContext, mContactId, mFromAddress);
mContactPhoto = SmsPopupUtils.getPersonPhoto(mContext, mContactId);
mUnreadCount = SmsPopupUtils.getUnreadMessagesCount(mContext, mTimestamp);
mThreadId = SmsPopupUtils.getThreadIdFromAddress(mContext, mFromAddress);
setMessageId();
if (mContactName == null) {
mContactName = mContext.getString(android.R.string.unknownName);
}
}
/**
* Construct SmsMmsMessage for getMmsDetails() - info fetched from the MMS
* database table
*/
public SmsMmsMessage(Context _context, String _fromAddress,
String _messageBody, long _timestamp, long _threadId,
int _unreadCount, int _messageType) {
mContext = _context;
mFromAddress = _fromAddress;
mMessageBody = _messageBody == null ? "" : _messageBody;;
mTimestamp = _timestamp;
mMessageType = _messageType;
// TODO: I think contactId can come the MMS table, this would save
// this database lookup
mContactId = SmsPopupUtils.getPersonIdFromPhoneNumber(mContext, mFromAddress);
mContactName = SmsPopupUtils.getPersonName(mContext, mContactId, mFromAddress);
mContactPhoto = SmsPopupUtils.getPersonPhoto(mContext, mContactId);
mUnreadCount = _unreadCount;
mThreadId = _threadId;
setMessageId();
if (mContactName == null) {
mContactName = mContext.getString(android.R.string.unknownName);
}
}
/**
* Construct SmsMmsMessage for getSmsDetails() - info fetched from the SMS
* database table
*/
public SmsMmsMessage(Context _context, String _fromAddress,
String _contactId, String _messageBody, long _timestamp,
long _threadId, int _unreadCount, long _messageId, int _messageType) {
mContext = _context;
mFromAddress = _fromAddress;
mMessageBody = _messageBody == null ? "" : _messageBody;;
mTimestamp = _timestamp;
mMessageType = _messageType;
mContactId = _contactId;
if ("0".equals(mContactId))
mContactId = null;
mContactName = SmsPopupUtils.getPersonName(mContext, mContactId, mFromAddress);
mContactPhoto = SmsPopupUtils.getPersonPhoto(mContext, mContactId);
mUnreadCount = _unreadCount;
mThreadId = _threadId;
mMessageId = _messageId;
if (mContactName == null) {
mContactName = mContext.getString(android.R.string.unknownName);
}
}
/**
* Construct SmsMmsMessage from an extras bundle
*/
public SmsMmsMessage(Context _context, Bundle b) {
mContext = _context;
mFromAddress = b.getString(EXTRAS_FROM_ADDRESS);
mMessageBody = b.getString(EXTRAS_MESSAGE_BODY) == null ? "" : b.getString(EXTRAS_MESSAGE_BODY);
mTimestamp = b.getLong(EXTRAS_TIMESTAMP);
mContactId = b.getString(EXTRAS_CONTACT_ID);
mContactName = b.getString(EXTRAS_CONTACT_NAME);
mContactPhoto = b.getByteArray(EXTRAS_CONTACT_PHOTO);
mUnreadCount = b.getInt(EXTRAS_UNREAD_COUNT, 1);
mThreadId = b.getLong(EXTRAS_THREAD_ID, 0);
mMessageType = b.getInt(EXTRAS_MESSAGE_TYPE, MESSAGE_TYPE_SMS);
mNotify = b.getBoolean(EXTRAS_NOTIFY, false);
mReminderCount = b.getInt(EXTRAS_REMINDER_COUNT, 0);
mMessageId = b.getLong(EXTRAS_MESSAGE_ID, 0);
}
/**
* 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 _contactName, byte[] _contactPhoto, int _unreadCount,
long _threadId, int _messageType) {
mContext = _context;
mFromAddress = _fromAddress;
mMessageBody = _messageBody == null ? "" : _messageBody;
mTimestamp = _timestamp;
mContactId = _contactId;
mContactName = _contactName;
mContactPhoto = _contactPhoto;
mUnreadCount = _unreadCount;
mThreadId = _threadId;
mMessageType = _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, mFromAddress);
b.putString(EXTRAS_MESSAGE_BODY, mMessageBody);
b.putLong(EXTRAS_TIMESTAMP, mTimestamp);
b.putString(EXTRAS_CONTACT_ID, mContactId);
b.putString(EXTRAS_CONTACT_NAME, mContactName);
b.putByteArray(EXTRAS_CONTACT_PHOTO, mContactPhoto);
b.putInt(EXTRAS_UNREAD_COUNT, mUnreadCount);
b.putLong(EXTRAS_THREAD_ID, mThreadId);
b.putInt(EXTRAS_MESSAGE_TYPE, mMessageType);
b.putBoolean(EXTRAS_NOTIFY, mNotify);
b.putInt(EXTRAS_REMINDER_COUNT, mReminderCount);
b.putLong(EXTRAS_MESSAGE_ID, mMessageId);
return b;
}
public static SmsMmsMessage getSmsfromPDUs(Context context, Object[] pdus) {
SmsMessage[] msgs = new SmsMessage[pdus.length];
String from;
StringBuilder body = new StringBuilder();
long timestamp;
int msgtype = MESSAGE_TYPE_SMS;
// public SmsMmsMessage(Context _context, String _fromAddress, String
// _messageBody,
// long _timestamp, int _messageType)
for (int i = 0; i < msgs.length; i++) {
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
SmsMessage firstMessage = msgs[0];
for (SmsMessage currentMessage : msgs) {
if (currentMessage.getDisplayOriginatingAddress().equals(firstMessage.getDisplayOriginatingAddress())) {
body.append(currentMessage.getDisplayMessageBody());
}
}
timestamp = firstMessage.getTimestampMillis();
from = firstMessage.getDisplayOriginatingAddress();
return new SmsMmsMessage(context, from, body.toString(), timestamp, msgtype);
}
public Bitmap getContactPhoto() {
if (mContactPhoto == null)
return null;
return BitmapFactory.decodeStream(new ByteArrayInputStream(mContactPhoto));
}
public int getUnreadCount() {
return mUnreadCount;
}
public long getTimestamp() {
return mTimestamp;
}
public CharSequence getFormattedTimestamp() {
/*
* No need for my own format function now, the 1.5 SDK has this built in
* (this will detect the system settings and return the correct format)
*/
// return SMSPopupUtils.formatTimestamp(context, timestamp);
return DateUtils.formatDateTime(mContext, mTimestamp, DateUtils.FORMAT_SHOW_TIME);
}
public String getContactName() {
if (mContactName == null) {
mContactName = mContext.getString(android.R.string.unknownName);
}
return mContactName;
}
public String getMessageBody() {
return mMessageBody;
}
public long getThreadId() {
return mThreadId;
}
public int getMessageType() {
return mMessageType;
}
public boolean getNotify() {
return mNotify;
}
public int getReminderCount() {
return mReminderCount;
}
public void updateReminderCount(int count) {
mReminderCount = count;
}
public void incrementReminderCount() {
mReminderCount++;
}
public void setMessageId() {
mMessageId = SmsPopupUtils.findMessageId(mContext, mThreadId, mTimestamp, mMessageType);
}
public long getMessageId() {
if (mMessageId == 0) {
setMessageId();
}
return mMessageId;
}
public String getContactId() {
return mContactId;
}
// public boolean equals(SmsMmsMessage compareMessage) {
// boolean equals = false;
// if (PhoneNumberUtils.compare(this.fromAddress,
// compareMessage.fromAddress) &&
// this.compareTimeStamp(compareMessage.timestamp) &&
// this.messageType == compareMessage.messageType) {
// equals = true;
// }
// return equals;
// }
/**
* Check if this message is sufficiently the same as the provided parameters
*/
public boolean equals(String fromAddress, long timestamp,
long timestamp_provider, String body) {
boolean equals = false;
if (PhoneNumberUtils.compare(this.mFromAddress, fromAddress)
&& this.compareTimeStamp(timestamp, timestamp_provider)
&& this.compareBody(body)) {
equals = true;
}
return equals;
}
// private boolean compareTimeStamp(long compareTimestamp) {
// return compareTimeStamp(compareTimestamp, 0);
// }
/*
* Compares the timestamps of a message, this is super hacky because the way
* which builds of Android store SMS timestamps changed in the cupcake
* branch - pre-cupcake it stored the timestamp provided by the telecom
* provider; post-cupcake it stored the system timestamp. Unfortunately this
* means we need to use 2 different ways to determine if the received
* message timestamp is sufficiently equal to the database timestamp :(
*/
private boolean compareTimeStamp(long compareTimestamp, long providerTimestamp) {
final int MESSAGE_COMPARE_TIME_BUFFER = 2000;
// final int buildVersion = Integer.valueOf(Build.VERSION.INCREMENTAL);
/*
* On March 28th, 2009 - these are the latest builds that I could find:
* 128600 TMI-RC9 (Tmobile EU) 126986 PLAT-RC33 (Tmobile US) 129975
* Emulator (from Android 1.1 SDK r1) Hopefully anything later will have
* the updated SMS code that uses the system timestamp rather than the
* SMS timestamp
*/
// final int LATEST_BUILD = 129975;
// boolean PRE_CUPCAKE = false;
// if (buildVersion <= LATEST_BUILD) {
// PRE_CUPCAKE = true;
// }
//
// Log.v("DB timestamp = " + timestamp);
// Log.v("Provider timestamp = " + providerTimestamp);
// Log.v("System timestamp = " + compareTimestamp);
/*
* If pre-cupcake we can just do a direct comparison as the Mms app
* stores the timestamp from the telecom provider (in the sms pdu)
*/
// if (PRE_CUPCAKE) {
// Log.v("Build is pre-cupcake ("+buildVersion+"), doing direct SMS timestamp comparison");
// Log.v("DB timestamp = " + timestamp);
// Log.v("Intent timestamp = " + providerTimestamp);
if (mTimestamp == providerTimestamp) {
// Log.v("SMS Compare: compareTimestamp() - intent timestamp = provider timestamp");
return true;
} // else {
// return false;
// }
// }
/*
* If post-cupcake, the system app stores a system timestamp - the only
* problem is we have no way of knowing the exact time the system app
* used. So what we'll do is compare against our own system timestamp
* and add a buffer in. This is an awful way of doing this, but I don't
* see any other way around it :(
*/
// Log.v("Build is post-cupcake ("+buildVersion+"), doing approx. SMS timestamp comparison");
// Log.v("DB timestamp = " + timestamp);
// Log.v("Intent timestamp = " + compareTimestamp);
if (mTimestamp < (compareTimestamp + MESSAGE_COMPARE_TIME_BUFFER)
&& mTimestamp > (compareTimestamp - MESSAGE_COMPARE_TIME_BUFFER)) {
// Log.v("SMS Compare: compareTimestamp() - timestamp is approx. the same");
return true;
}
// Log.v("SMS Compare: compareTimestamp() - return false");
return false;
}
/*
* Compare message body
*/
private boolean compareBody(String compareBody) {
if (compareBody != null) {
if (mMessageBody.length() != compareBody.length()) {
// Log.v("SMS Compare: compareBody() - length is different");
return false;
}
if (mMessageBody.equals(compareBody)) {
// Log.v("SMS Compare: compareBody() - messageBody is the same");
return true;
}
}
// Log.v("SMS Compare: compareBody() - return false");
return false;
}
public boolean replyToMessage(String quickreply) {
// SmsMessageSender sender =
// new SmsMessageSender(context, new String[] {fromAddress}, quickreply,
// threadId);
// return sender.sendMessage();
return false;
}
}