group and contact list fixes

1) Updating a group without changing the avatar will keep that
   avatar

2) Prohibit adding non-push users to an existing push group

3) Add Android contacts to the same database. Takes a small amount
   more time and memory, but allows queries to not be a hack, and
   enables us to dedupe numbers in JB and higher devices.

// FREEBIE
This commit is contained in:
Jake McGinty
2014-04-01 16:40:16 -07:00
committed by Moxie Marlinspike
parent b715debefc
commit e2f7c1529a
11 changed files with 171 additions and 56 deletions

View File

@@ -82,8 +82,12 @@ public class ContactAccessor {
null, null, null, ContactsContract.Groups.TITLE + " ASC");
}
public Loader<Cursor> getCursorLoaderForContacts(Context context) {
return new ContactsCursorLoader(context);
public Loader<Cursor> getCursorLoaderForContacts(Context context, String filter) {
return new ContactsCursorLoader(context, filter, false);
}
public Loader<Cursor> getCursorLoaderForPushContacts(Context context, String filter) {
return new ContactsCursorLoader(context, filter, true);
}
public Cursor getCursorForContactsWithNumbers(Context context) {

View File

@@ -30,6 +30,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.FilterQueryProvider;
import android.widget.ImageView;
import android.widget.TextView;

View File

@@ -28,22 +28,25 @@ import android.support.v4.content.CursorLoader;
public class ContactsCursorLoader extends CursorLoader {
private final Context context;
private final String filter;
private final boolean pushOnly;
private ContactsDatabase db;
public ContactsCursorLoader(Context context) {
public ContactsCursorLoader(Context context, String filter, boolean pushOnly) {
super(context);
this.context = context;
this.context = context;
this.filter = filter;
this.pushOnly = pushOnly;
}
@Override
public Cursor loadInBackground() {
db = new ContactsDatabase(context);
return db.getAllContacts();
db = ContactsDatabase.getInstance(context);
return db.query(filter, pushOnly);
}
@Override
public void onReset() {
super.onReset();
db.close();
}
}

View File

@@ -20,6 +20,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -28,10 +29,15 @@ import android.provider.ContactsContract;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Database to supply all types of contacts that TextSecure needs to know about
@@ -50,50 +56,102 @@ public class ContactsDatabase {
public static final String NUMBER_COLUMN = ContactsContract.CommonDataKinds.Phone.NUMBER;
public static final String TYPE_COLUMN = "type";
private static final String FILTER_SELECTION = NAME_COLUMN + " LIKE ? OR " + NUMBER_COLUMN + " LIKE ?";
private static final String CONTACT_LIST_SORT = NAME_COLUMN + " ASC";
private static final String[] ANDROID_PROJECTION = new String[]{ID_COLUMN,
NAME_COLUMN,
NUMBER_TYPE_COLUMN,
NUMBER_COLUMN};
private static final String[] CONTACTS_PROJECTION = new String[]{ID_COLUMN,
NAME_COLUMN,
NUMBER_TYPE_COLUMN,
NUMBER_COLUMN,
TYPE_COLUMN};
public static final int NORMAL_TYPE = 0;
public static final int PUSH_TYPE = 1;
public static final int GROUP_TYPE = 2;
public ContactsDatabase(Context context) {
private static ContactsDatabase instance = null;
public synchronized static ContactsDatabase getInstance(Context context) {
if (instance == null) instance = new ContactsDatabase(context);
return instance;
}
public synchronized static void destroyInstance() {
if (instance != null) instance.close();
instance = null;
}
private ContactsDatabase(Context context) {
this.dbHelper = new DatabaseOpenHelper(context);
this.context = context;
this.context = context;
}
public void close() {
dbHelper.close();
}
public Cursor getAllContacts() {
return query(null, null, null);
}
public Cursor query(String filter, boolean pushOnly) {
final boolean includeAndroidContacts = !pushOnly && TextSecurePreferences.isSmsNonDataOutEnabled(context);
final Cursor localCursor = queryLocalDb(filter);
final Cursor androidCursor;
final MatrixCursor newNumberCursor;
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
final Cursor localCursor = queryLocalDb(selection, selectionArgs, columns);
final Cursor androidCursor;
if (TextSecurePreferences.isSmsNonDataOutEnabled(context)) {
androidCursor = queryAndroidDb();
} else{
return localCursor;
if (includeAndroidContacts) {
androidCursor = queryAndroidDb(filter);
} else {
androidCursor = null;
}
if (localCursor != null && androidCursor != null) return new MergeCursor(new Cursor[]{localCursor,androidCursor});
else if (localCursor != null) return localCursor;
else if (androidCursor != null) return androidCursor;
else return null;
if (includeAndroidContacts && !Util.isEmpty(filter) && NumberUtil.isValidSmsOrEmail(filter)) {
newNumberCursor = new MatrixCursor(CONTACTS_PROJECTION, 1);
newNumberCursor.addRow(new Object[]{-1L, context.getString(R.string.contact_selection_list__unknown_contact),
0, filter, NORMAL_TYPE});
} else {
newNumberCursor = null;
}
List<Cursor> cursors = new ArrayList<Cursor>();
if (localCursor != null) cursors.add(localCursor);
if (androidCursor != null) cursors.add(androidCursor);
if (newNumberCursor != null) cursors.add(newNumberCursor);
switch (cursors.size()) {
case 0: return null;
case 1: return cursors.get(0);
default: return new MergeCursor(cursors.toArray(new Cursor[]{}));
}
}
private Cursor queryAndroidDb() {
Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, ANDROID_PROJECTION, null, null, CONTACT_LIST_SORT);
private Cursor queryAndroidDb(String filter) {
final Uri baseUri;
if (filter != null) {
baseUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
Uri.encode(filter));
} else {
baseUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
}
Cursor cursor = context.getContentResolver().query(baseUri, ANDROID_PROJECTION, null, null, CONTACT_LIST_SORT);
return new TypedCursorWrapper(cursor);
}
private Cursor queryLocalDb(String filter) {
final String selection;
final String[] selectionArgs;
final String fuzzyFilter = "%" + filter + "%";
if (!Util.isEmpty(filter)) {
selection = FILTER_SELECTION;
selectionArgs = new String[]{fuzzyFilter, fuzzyFilter};
} else {
selection = null;
selectionArgs = null;
}
return queryLocalDb(selection, selectionArgs, null);
}
private Cursor queryLocalDb(String selection, String[] selectionArgs, String[] columns) {
SQLiteDatabase localDb = dbHelper.getReadableDatabase();
final Cursor localCursor;