mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-11 16:33:39 +00:00
parent
945636ac5c
commit
4314a4b42b
@ -22,12 +22,14 @@ import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.thoughtcrime.redphone.util.Conversions;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -39,6 +41,8 @@ import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
@ -46,6 +50,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* A cursor adapter for a conversation thread. Ultimately
|
||||
@ -69,12 +74,13 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
|
||||
private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>());
|
||||
|
||||
private final ItemClickListener clickListener;
|
||||
private final MasterSecret masterSecret;
|
||||
private final Locale locale;
|
||||
private final Recipients recipients;
|
||||
private final MmsSmsDatabase db;
|
||||
private final LayoutInflater inflater;
|
||||
private final @Nullable ItemClickListener clickListener;
|
||||
private final @NonNull MasterSecret masterSecret;
|
||||
private final @NonNull Locale locale;
|
||||
private final @NonNull Recipients recipients;
|
||||
private final @NonNull MmsSmsDatabase db;
|
||||
private final @NonNull LayoutInflater inflater;
|
||||
private final @NonNull MessageDigest digest;
|
||||
|
||||
protected static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public <V extends View & BindableConversationItem> ViewHolder(final @NonNull V itemView) {
|
||||
@ -92,6 +98,23 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
void onItemLongClick(ConversationItem item);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@VisibleForTesting
|
||||
ConversationAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor);
|
||||
try {
|
||||
this.masterSecret = null;
|
||||
this.locale = null;
|
||||
this.clickListener = null;
|
||||
this.recipients = null;
|
||||
this.inflater = null;
|
||||
this.db = null;
|
||||
this.digest = MessageDigest.getInstance("SHA1");
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new AssertionError("SHA1 isn't supported!");
|
||||
}
|
||||
}
|
||||
|
||||
public ConversationAdapter(@NonNull Context context,
|
||||
@NonNull MasterSecret masterSecret,
|
||||
@NonNull Locale locale,
|
||||
@ -100,12 +123,19 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
@NonNull Recipients recipients)
|
||||
{
|
||||
super(context, cursor);
|
||||
this.masterSecret = masterSecret;
|
||||
this.locale = locale;
|
||||
this.clickListener = clickListener;
|
||||
this.recipients = recipients;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.db = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
try {
|
||||
this.masterSecret = masterSecret;
|
||||
this.locale = locale;
|
||||
this.clickListener = clickListener;
|
||||
this.recipients = recipients;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.db = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
this.digest = MessageDigest.getInstance("SHA1");
|
||||
|
||||
setHasStableIds(true);
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new AssertionError("SHA1 isn't supported!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,7 +144,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
super.changeCursor(cursor);
|
||||
}
|
||||
|
||||
@Override public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
@Override
|
||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
|
||||
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
|
||||
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
|
||||
@ -122,7 +153,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, recipients);
|
||||
}
|
||||
|
||||
@Override public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
@Override
|
||||
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
final V itemView = ViewUtil.inflate(inflater, parent, getLayoutForViewType(viewType));
|
||||
if (viewType == MESSAGE_TYPE_INCOMING || viewType == MESSAGE_TYPE_OUTGOING) {
|
||||
itemView.setOnClickListener(new OnClickListener() {
|
||||
@ -143,7 +175,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
return new ViewHolder(itemView);
|
||||
}
|
||||
|
||||
@Override public void onItemViewRecycled(ViewHolder holder) {
|
||||
@Override
|
||||
public void onItemViewRecycled(ViewHolder holder) {
|
||||
holder.getView().unbind();
|
||||
}
|
||||
|
||||
@ -171,6 +204,13 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(@NonNull Cursor cursor) {
|
||||
final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID));
|
||||
final byte[] bytes = digest.digest(unique.getBytes());
|
||||
return Conversions.byteArrayToLong(bytes);
|
||||
}
|
||||
|
||||
private MessageRecord getMessageRecord(long messageId, Cursor cursor, String type) {
|
||||
final SoftReference<MessageRecord> reference = messageRecordCache.get(type + messageId);
|
||||
if (reference != null) {
|
||||
|
@ -92,6 +92,7 @@ public class ConversationListAdapter extends CursorRecyclerViewAdapter<Conversat
|
||||
this.locale = locale;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.clickListener = clickListener;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,7 +128,8 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||
|
||||
public void onItemViewRecycled(VH holder) {}
|
||||
|
||||
@Override public final ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
@Override
|
||||
public final ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
case HEADER_TYPE: return new HeaderFooterViewHolder(header);
|
||||
case FOOTER_TYPE: return new HeaderFooterViewHolder(footer);
|
||||
@ -149,7 +150,8 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||
|
||||
public abstract void onBindItemViewHolder(VH viewHolder, @NonNull Cursor cursor);
|
||||
|
||||
@Override public int getItemViewType(int position) {
|
||||
@Override
|
||||
public final int getItemViewType(int position) {
|
||||
if (isHeaderPosition(position)) return HEADER_TYPE;
|
||||
if (isFooterPosition(position)) return FOOTER_TYPE;
|
||||
moveToPositionOrThrow(getCursorPosition(position));
|
||||
@ -160,6 +162,16 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long getItemId(int position) {
|
||||
moveToPositionOrThrow(getCursorPosition(position));
|
||||
return getItemId(cursor);
|
||||
}
|
||||
|
||||
public long getItemId(@NonNull Cursor cursor) {
|
||||
return cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
|
||||
}
|
||||
|
||||
private void assertActiveCursor() {
|
||||
if (!isActiveCursor()) {
|
||||
throw new IllegalStateException("this should only be called when the cursor is valid");
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
@SuppressWarnings("UnnecessaryInterfaceModifier")
|
||||
public interface MmsSmsColumns {
|
||||
|
||||
public static final String ID = "_id";
|
||||
@ -12,6 +13,7 @@ public interface MmsSmsColumns {
|
||||
public static final String ADDRESS_DEVICE_ID = "address_device_id";
|
||||
public static final String RECEIPT_COUNT = "delivery_receipt_count";
|
||||
public static final String MISMATCHED_IDENTITIES = "mismatched_identities";
|
||||
public static final String UNIQUE_ROW_ID = "unique_row_id";
|
||||
|
||||
public static class Types {
|
||||
protected static final long TOTAL_MASK = 0xFFFFFFFF;
|
||||
|
@ -16,13 +16,11 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
@ -42,7 +40,8 @@ public class MmsSmsDatabase extends Database {
|
||||
public static final String MMS_TRANSPORT = "mms";
|
||||
public static final String SMS_TRANSPORT = "sms";
|
||||
|
||||
private static final String[] PROJECTION = {MmsSmsColumns.ID, SmsDatabase.BODY, SmsDatabase.TYPE,
|
||||
private static final String[] PROJECTION = {MmsSmsColumns.ID, MmsSmsColumns.UNIQUE_ROW_ID,
|
||||
SmsDatabase.BODY, SmsDatabase.TYPE,
|
||||
MmsSmsColumns.THREAD_ID,
|
||||
SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT,
|
||||
MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||
@ -123,6 +122,9 @@ public class MmsSmsDatabase extends Database {
|
||||
String[] mmsProjection = {MmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||
MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
||||
MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " AS " + MmsSmsColumns.ID,
|
||||
"'MMS::' || " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID
|
||||
+ " || '::' || " + MmsDatabase.DATE_SENT
|
||||
+ " AS " + MmsSmsColumns.UNIQUE_ROW_ID,
|
||||
AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS,
|
||||
SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID,
|
||||
SmsDatabase.TYPE, SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,
|
||||
@ -143,7 +145,11 @@ public class MmsSmsDatabase extends Database {
|
||||
|
||||
String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||
SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
||||
MmsSmsColumns.ID, "NULL AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS,
|
||||
MmsSmsColumns.ID,
|
||||
"'SMS::' || " + MmsSmsColumns.ID
|
||||
+ " || '::' || " + SmsDatabase.DATE_SENT
|
||||
+ " AS " + MmsSmsColumns.UNIQUE_ROW_ID,
|
||||
"NULL AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS,
|
||||
SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID,
|
||||
SmsDatabase.TYPE, SmsDatabase.ADDRESS, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,
|
||||
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
|
||||
@ -222,8 +228,10 @@ public class MmsSmsDatabase extends Database {
|
||||
smsColumnsPresent.add(SmsDatabase.DATE_RECEIVED);
|
||||
smsColumnsPresent.add(SmsDatabase.STATUS);
|
||||
|
||||
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 3, MMS_TRANSPORT, selection, null, null, null);
|
||||
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery(TRANSPORT, smsProjection, smsColumnsPresent, 3, SMS_TRANSPORT, selection, null, null, null);
|
||||
@SuppressWarnings("deprecation")
|
||||
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 4, MMS_TRANSPORT, selection, null, null, null);
|
||||
@SuppressWarnings("deprecation")
|
||||
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery(TRANSPORT, smsProjection, smsColumnsPresent, 4, SMS_TRANSPORT, selection, null, null, null);
|
||||
|
||||
SQLiteQueryBuilder unionQueryBuilder = new SQLiteQueryBuilder();
|
||||
String unionQuery = unionQueryBuilder.buildUnionQuery(new String[] {smsSubQuery, mmsSubQuery}, order, limit);
|
||||
@ -231,6 +239,7 @@ public class MmsSmsDatabase extends Database {
|
||||
SQLiteQueryBuilder outerQueryBuilder = new SQLiteQueryBuilder();
|
||||
outerQueryBuilder.setTables("(" + unionQuery + ")");
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
String query = outerQueryBuilder.buildQuery(projection, null, null, null, null, null, null);
|
||||
|
||||
Log.w("MmsSmsDatabase", "Executing query: " + query);
|
||||
|
@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.powermock.api.mockito.PowerMockito.mock;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
public class ConversationAdapterTest extends BaseUnitTest {
|
||||
private Cursor cursor = mock(Cursor.class);
|
||||
private ConversationAdapter adapter;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
adapter = new ConversationAdapter(context, cursor);
|
||||
when(cursor.getColumnIndexOrThrow(anyString())).thenReturn(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetItemIdEquals() throws Exception {
|
||||
when(cursor.getString(anyInt())).thenReturn("SMS::1::1");
|
||||
long firstId = adapter.getItemId(cursor);
|
||||
when(cursor.getString(anyInt())).thenReturn("MMS::1::1");
|
||||
long secondId = adapter.getItemId(cursor);
|
||||
assertNotEquals(firstId, secondId);
|
||||
when(cursor.getString(anyInt())).thenReturn("MMS::2::1");
|
||||
long thirdId = adapter.getItemId(cursor);
|
||||
assertNotEquals(secondId, thirdId);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user