package com.steps.geosms.objects; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.support.v4.util.LongSparseArray; import android.text.TextUtils; import android.util.Log; import com.steps.geosms.utils.Constants; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.StringTokenizer; /** * Class Conversation. * * Created by Ioane on 2/21/2015. */ public class Conversation implements Serializable{ private static final String TAG = Conversation.class.getSimpleName(); private static final String RECIPIENT_IDS = Constants.RECIPIENT_IDS; private static final String THREAD_ID = Constants.ID; private static final String LAST_MESSAGE = Constants.CONVERSATION_LAST_MESSAGE; private static final String DATE = Constants.CONVERSATION_DATE; private static final String IS_READ = Constants.CONVERSATION_READ; /** * Contacts associated with conversation. */ private ArrayList<Contact> contacts; /** * Recipient Ids. * * One conversation may have many contacts within it. */ private String recipientIds; /** * This int represents conversation, (same as thread) * id */ private long id; /** * Last Message */ private String lastMessage; /** * Conversation date */ private Date date; private boolean messageRead; /** * This Constructor creates new Conversation object * from Cursor. * * Queries database for resolving Contact , if resolveContact is true. * * @param context context to resolve data. Null if resolveContacts is false. * @param cursor cursor to get data from. * @param resolveContact true if we need to resolve contacts */ public Conversation(Context context,Cursor cursor,boolean resolveContact){ this.recipientIds = cursor.getString(cursor.getColumnIndex(RECIPIENT_IDS)); this.lastMessage = cursor.getString(cursor.getColumnIndex(LAST_MESSAGE)); this.date = new Date(cursor.getLong(cursor.getColumnIndex(DATE))); this.messageRead = cursor.getInt(cursor.getColumnIndex(IS_READ)) == 1; this.id = cursor.getInt(cursor.getColumnIndex(THREAD_ID)); if(resolveContact){ this.contacts = resolveContacts(context,recipientIds); } } /** * This constructor is for working with cache. * * If contact was in cache we won't resolve contact * but will get directly from cache. * * @param context Context in order to use resolve some contact parts. (Like contact) * @param cursor cursor to get data from. * @param contactCache so save or retrieve contact without querying... */ public Conversation(Context context,Cursor cursor,LongSparseArray<ArrayList<Contact>> contactCache){ this.recipientIds = cursor.getString(cursor.getColumnIndex(RECIPIENT_IDS)); this.lastMessage = cursor.getString(cursor.getColumnIndex(LAST_MESSAGE)); this.date = new Date(cursor.getLong(cursor.getColumnIndex(DATE))); this.messageRead = cursor.getInt(cursor.getColumnIndex(IS_READ)) == 1; this.id = cursor.getLong(cursor.getColumnIndex(THREAD_ID)); ArrayList<Contact> cached= contactCache.get(id,null); if(cached == null){ this.contacts = resolveContacts(context,recipientIds); // save in cache contactCache.append(id,contacts); }else{ this.contacts = cached; } } /** * This method resolves contacts from canonical-address * by recipient IDs. * @param recipientIds id to get contacts * @return arrayList */ private ArrayList<Contact> resolveContacts(Context context , String recipientIds){ ArrayList<Contact> resolved = new ArrayList<>(1); // for most case we don't have group chat. ContentResolver cr = context.getContentResolver(); // iterate through the recipients... // we may have many Group chat. if(TextUtils.isEmpty(recipientIds)) return null; StringTokenizer tokenizer = new StringTokenizer(recipientIds," "); while(tokenizer.hasMoreTokens()){ try{ int rec_id = Integer.parseInt(tokenizer.nextToken()); Cursor c = cr.query(ContentUris.withAppendedId(Uri.parse("content://mms-sms/canonical-address"), rec_id), null, null, null, null); if(c.moveToFirst()){ String address = c.getString(0); if(address != null){ Contact contact = new Contact(context,address); resolved.add(contact); } } c.close(); }catch (NumberFormatException e){ e.printStackTrace(); } } return resolved; } public long getId() { return id; } public void setId(int id) { this.id = id; } public ArrayList<Contact> getContacts() { return contacts; } public void setContacts(ArrayList<Contact> contacts) { this.contacts = contacts; } public Contact getContact() { return contacts == null || contacts.isEmpty() ? null : contacts.get(0); } public void setContact(Contact contact) { if(contacts == null) contacts = new ArrayList<>(); contacts.add(contact); } public boolean isMessageRead() { return messageRead; } public void setMessageRead(boolean messageRead) { this.messageRead = messageRead; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getLastMessage() { return lastMessage; } public void setLastMessage(String lastMessage) { this.lastMessage = lastMessage; } public String getRecipientIds() { return recipientIds; } public void setRecipientIds(String recipientIds) { this.recipientIds = recipientIds; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Conversation that = (Conversation) o; return TextUtils.equals(recipientIds,that.recipientIds); } @Override public int hashCode() { return (int)id; } private static final Uri THREAD_ID_CONTENT_URI = Uri.parse( "content://mms-sms/threadID"); /** * {@inheritDoc getOrCreateThreadId} * @param context * @param address * @return */ public static long getOrCreateThreadId(Context context, String address){ ArrayList<String> list = new ArrayList<>(1); list.add(address); return getOrCreateThreadId(context, list); } /** * Searches thread_id for these recipients. * If thread didn't exist. It creates new and returns. * * @param context to lookup db. * @param recipients recipients in conversation. * @return Created or found thread_id */ public static long getOrCreateThreadId( Context context, Collection<String> recipients) { Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon(); for (String recipient : recipients) { uriBuilder.appendQueryParameter("recipient", recipient); } Uri uri = uriBuilder.build(); Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { return cursor.getLong(0); } else { Log.e(TAG, "getOrCreateThreadId returned no rows!"); } } finally { cursor.close(); } } Log.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString()); throw new IllegalArgumentException("Unable to find or allocate a thread ID."); } }