mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-17 12:58:25 +00:00
Clean up contact queries.
// FREEBIE
This commit is contained in:
parent
d1940fe0f9
commit
704f2b91e2
@ -32,9 +32,10 @@ import android.widget.AdapterView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -140,11 +141,10 @@ public class PushContactSelectionListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
if (getActivity().getIntent().getBooleanExtra(PushContactSelectionActivity.PUSH_ONLY_EXTRA, false)) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForPushContacts(getActivity(), cursorFilter);
|
||||
} else {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContacts(getActivity(), cursorFilter);
|
||||
}
|
||||
boolean pushOnly = getActivity().getIntent().getBooleanExtra(PushContactSelectionActivity.PUSH_ONLY_EXTRA, false);
|
||||
boolean supportsSms = TextSecurePreferences.isSmsEnabled(getActivity());
|
||||
|
||||
return new ContactsCursorLoader(getActivity(), !pushOnly && supportsSms, cursorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,13 +23,9 @@ import android.database.MergeCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -65,35 +61,6 @@ public class ContactAccessor {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public CursorLoader getCursorLoaderForContactsWithNumbers(Context context) {
|
||||
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
||||
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
||||
|
||||
return new CursorLoader(context, uri, null, selection, null,
|
||||
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||
}
|
||||
|
||||
public CursorLoader getCursorLoaderForContactGroups(Context context) {
|
||||
return new CursorLoader(context, ContactsContract.Groups.CONTENT_URI,
|
||||
null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
||||
}
|
||||
|
||||
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) {
|
||||
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
||||
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
||||
|
||||
return context.getContentResolver().query(uri, null, selection, null,
|
||||
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||
}
|
||||
|
||||
public Collection<ContactData> getContactsWithPush(Context context) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
||||
@ -136,34 +103,6 @@ public class ContactAccessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getNameForNumber(Context context, String number) {
|
||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
|
||||
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME));
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GroupData getGroupData(Context context, Cursor cursor) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Groups._ID));
|
||||
String title = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Groups.TITLE));
|
||||
|
||||
return new GroupData(id, title);
|
||||
}
|
||||
|
||||
public ContactData getContactData(Context context, Cursor cursor) {
|
||||
return getContactData(context,
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME)),
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(Contacts._ID)));
|
||||
}
|
||||
|
||||
public ContactData getContactData(Context context, Uri uri) {
|
||||
return getContactData(context, getNameFromContact(context, uri), Long.parseLong(uri.getLastPathSegment()));
|
||||
}
|
||||
@ -193,32 +132,6 @@ public class ContactAccessor {
|
||||
return contactData;
|
||||
}
|
||||
|
||||
public List<ContactData> getGroupMembership(Context context, long groupId) {
|
||||
LinkedList<ContactData> contacts = new LinkedList<ContactData>();
|
||||
Cursor groupMembership = null;
|
||||
|
||||
try {
|
||||
String selection = ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID + " = ? AND " +
|
||||
ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE + " = ?";
|
||||
String[] args = new String[] {groupId+"",
|
||||
ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE};
|
||||
|
||||
groupMembership = context.getContentResolver().query(Data.CONTENT_URI, null, selection, args, null);
|
||||
|
||||
while (groupMembership != null && groupMembership.moveToNext()) {
|
||||
String displayName = groupMembership.getString(groupMembership.getColumnIndexOrThrow(Data.DISPLAY_NAME));
|
||||
long contactId = groupMembership.getLong(groupMembership.getColumnIndexOrThrow(Data.CONTACT_ID));
|
||||
|
||||
contacts.add(getContactData(context, displayName, contactId));
|
||||
}
|
||||
} finally {
|
||||
if (groupMembership != null)
|
||||
groupMembership.close();
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public List<String> getNumbersForThreadSearchFilter(Context context, String constraint) {
|
||||
LinkedList<String> numberList = new LinkedList<>();
|
||||
Cursor cursor = null;
|
||||
@ -258,26 +171,6 @@ public class ContactAccessor {
|
||||
return Phone.getTypeLabel(mContext.getResources(), type, label);
|
||||
}
|
||||
|
||||
private long getContactIdFromLookupUri(Context context, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri,
|
||||
new String[] {ContactsContract.Contacts._ID},
|
||||
null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getLong(0);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NumberData implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<NumberData> CREATOR = new Parcelable.Creator<NumberData>() {
|
||||
@ -313,16 +206,6 @@ public class ContactAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
public static class GroupData {
|
||||
public final long id;
|
||||
public final String name;
|
||||
|
||||
public GroupData(long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContactData implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<ContactData> CREATOR = new Parcelable.Creator<ContactData>() {
|
||||
@ -345,13 +228,6 @@ public class ContactAccessor {
|
||||
this.numbers = new LinkedList<NumberData>();
|
||||
}
|
||||
|
||||
public ContactData(long id, String name, List<NumberData> numbers) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.numbers = numbers;
|
||||
}
|
||||
|
||||
|
||||
public ContactData(Parcel in) {
|
||||
id = in.readLong();
|
||||
name = in.readString();
|
||||
|
@ -70,21 +70,21 @@ public class ContactSelectionListAdapter extends CursorAdapter
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN));
|
||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN));
|
||||
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
|
||||
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN));
|
||||
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN));
|
||||
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(),
|
||||
numberType, label).toString();
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN));
|
||||
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
|
||||
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
|
||||
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN));
|
||||
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN));
|
||||
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(),
|
||||
numberType, label).toString();
|
||||
|
||||
int color = (type == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
|
||||
drawables.getColor(1, 0xff000000);
|
||||
int color = (contactType == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
|
||||
drawables.getColor(1, 0xff000000);
|
||||
|
||||
|
||||
((ContactSelectionListItem)view).unbind();
|
||||
((ContactSelectionListItem)view).set(id, type, name, number, labelText, color, multiSelect);
|
||||
((ContactSelectionListItem)view).set(id, contactType, name, number, labelText, color, multiSelect);
|
||||
((ContactSelectionListItem)view).setChecked(selectedContacts.containsKey(id));
|
||||
}
|
||||
|
||||
@ -105,10 +105,10 @@ public class ContactSelectionListAdapter extends CursorAdapter
|
||||
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN));
|
||||
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
|
||||
|
||||
if (type == ContactsDatabase.PUSH_TYPE) holder.text.setText(R.string.contact_selection_list__header_textsecure_users);
|
||||
else holder.text.setText(R.string.contact_selection_list__header_other);
|
||||
if (contactType == ContactsDatabase.PUSH_TYPE) holder.text.setText(R.string.contact_selection_list__header_textsecure_users);
|
||||
else holder.text.setText(R.string.contact_selection_list__header_other);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
@ -118,7 +118,7 @@ public class ContactSelectionListAdapter extends CursorAdapter
|
||||
Cursor cursor = getCursor();
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN));
|
||||
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
|
||||
}
|
||||
|
||||
public Map<Long, String> getSelectedContacts() {
|
||||
|
@ -18,12 +18,15 @@ package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* CursorLoader that initializes a ContactsDatabase instance
|
||||
@ -31,47 +34,34 @@ import java.util.concurrent.Semaphore;
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class ContactsCursorLoader extends CursorLoader {
|
||||
private static final String TAG = ContactsCursorLoader.class.getSimpleName();
|
||||
private static final int DB_PERMITS = 100;
|
||||
|
||||
private final Context context;
|
||||
private final String filter;
|
||||
private final boolean pushOnly;
|
||||
private final Semaphore dbSemaphore = new Semaphore(DB_PERMITS);
|
||||
private ContactsDatabase db;
|
||||
private static final String TAG = ContactsCursorLoader.class.getSimpleName();
|
||||
|
||||
public ContactsCursorLoader(Context context, String filter, boolean pushOnly) {
|
||||
private final String filter;
|
||||
private boolean includeSmsContacts;
|
||||
|
||||
public ContactsCursorLoader(Context context, boolean includeSmsContacts, String filter) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
|
||||
this.filter = filter;
|
||||
this.pushOnly = pushOnly;
|
||||
this.db = new ContactsDatabase(context);
|
||||
this.includeSmsContacts = includeSmsContacts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
try {
|
||||
dbSemaphore.acquire();
|
||||
return db.query(filter, pushOnly);
|
||||
} catch (InterruptedException ie) {
|
||||
throw new AssertionError(ie);
|
||||
} finally {
|
||||
dbSemaphore.release();
|
||||
}
|
||||
}
|
||||
ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(getContext());
|
||||
ArrayList<Cursor> cursorList = new ArrayList<>(3);
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
Log.w(TAG, "onReset()");
|
||||
try {
|
||||
dbSemaphore.acquire(DB_PERMITS);
|
||||
db.close();
|
||||
db = new ContactsDatabase(context);
|
||||
} catch (InterruptedException ie) {
|
||||
throw new AssertionError(ie);
|
||||
} finally {
|
||||
dbSemaphore.release(DB_PERMITS);
|
||||
cursorList.add(contactsDatabase.queryTextSecureContacts(filter));
|
||||
|
||||
if (includeSmsContacts) {
|
||||
cursorList.add(contactsDatabase.querySystemContacts(filter));
|
||||
}
|
||||
super.onReset();
|
||||
|
||||
if (!TextUtils.isEmpty(filter) && NumberUtil.isValidSmsOrEmail(filter)) {
|
||||
cursorList.add(contactsDatabase.getNewNumberCursor(filter));
|
||||
}
|
||||
|
||||
return new MergeCursor(cursorList.toArray(new Cursor[0]));
|
||||
}
|
||||
}
|
||||
|
@ -18,33 +18,26 @@ package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
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;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
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 java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -55,46 +48,25 @@ import java.util.Set;
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class ContactsDatabase {
|
||||
|
||||
private static final String TAG = ContactsDatabase.class.getSimpleName();
|
||||
private final DatabaseOpenHelper dbHelper;
|
||||
private final Context context;
|
||||
|
||||
public static final String TABLE_NAME = "CONTACTS";
|
||||
public static final String ID_COLUMN = ContactsContract.CommonDataKinds.Phone._ID;
|
||||
public static final String NAME_COLUMN = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME;
|
||||
public static final String NUMBER_TYPE_COLUMN = ContactsContract.CommonDataKinds.Phone.TYPE;
|
||||
public static final String NUMBER_COLUMN = ContactsContract.CommonDataKinds.Phone.NUMBER;
|
||||
public static final String LABEL_COLUMN = ContactsContract.CommonDataKinds.Phone.LABEL;
|
||||
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 + " COLLATE NOCASE ASC";
|
||||
private static final String[] ANDROID_PROJECTION = new String[]{ID_COLUMN,
|
||||
NAME_COLUMN,
|
||||
NUMBER_TYPE_COLUMN,
|
||||
LABEL_COLUMN,
|
||||
NUMBER_COLUMN};
|
||||
|
||||
private static final String[] CONTACTS_PROJECTION = new String[]{ID_COLUMN,
|
||||
NAME_COLUMN,
|
||||
NUMBER_TYPE_COLUMN,
|
||||
LABEL_COLUMN,
|
||||
NUMBER_COLUMN,
|
||||
TYPE_COLUMN};
|
||||
public static final String ID_COLUMN = "_id";
|
||||
public static final String NAME_COLUMN = "name";
|
||||
public static final String NUMBER_COLUMN = "number";
|
||||
public static final String NUMBER_TYPE_COLUMN = "number_type";
|
||||
public static final String LABEL_COLUMN = "label";
|
||||
public static final String CONTACT_TYPE_COLUMN = "contact_type";
|
||||
|
||||
public static final int NORMAL_TYPE = 0;
|
||||
public static final int PUSH_TYPE = 1;
|
||||
public static final int GROUP_TYPE = 2;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public ContactsDatabase(Context context) {
|
||||
this.dbHelper = new DatabaseOpenHelper(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
public synchronized void setRegisteredUsers(Account account, List<String> e164numbers)
|
||||
throws RemoteException, OperationApplicationException
|
||||
{
|
||||
@ -153,6 +125,7 @@ public class ContactsDatabase {
|
||||
.withValueBackReference(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, index)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, e164number)
|
||||
.withValue(ContactsContract.Data.SYNC2, "__TS")
|
||||
.build());
|
||||
|
||||
operations.add(ContentProviderOperation.newInsert(dataUri)
|
||||
@ -176,183 +149,184 @@ public class ContactsDatabase {
|
||||
.withSelection(BaseColumns._ID + " = ?", new String[] {String.valueOf(rowId)})
|
||||
.build());
|
||||
}
|
||||
|
||||
public @NonNull Cursor querySystemContacts(String filter) {
|
||||
Uri uri;
|
||||
|
||||
public Cursor query(String filter, boolean pushOnly) {
|
||||
// FIXME: This doesn't make sense to me. You pass in pushOnly, but then
|
||||
// conditionally check to see whether other contacts should be included
|
||||
// in the query method itself? I don't think this method should have any
|
||||
// understanding of that stuff.
|
||||
final boolean includeAndroidContacts = !pushOnly && TextSecurePreferences.isSmsEnabled(context);
|
||||
final Cursor localCursor = queryLocalDb(filter);
|
||||
final Cursor androidCursor;
|
||||
final MatrixCursor newNumberCursor;
|
||||
|
||||
if (includeAndroidContacts) {
|
||||
androidCursor = queryAndroidDb(filter);
|
||||
} else {
|
||||
androidCursor = null;
|
||||
}
|
||||
|
||||
if (!TextUtils.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),
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM, "\u21e2", 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(String filter) {
|
||||
final Uri baseUri;
|
||||
if (!TextUtils.isEmpty(filter)) {
|
||||
baseUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
|
||||
Uri.encode(filter));
|
||||
uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(filter));
|
||||
} else {
|
||||
baseUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
|
||||
uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
|
||||
}
|
||||
Cursor cursor = context.getContentResolver().query(baseUri, ANDROID_PROJECTION, null, null, CONTACT_LIST_SORT);
|
||||
return cursor == null ? null : new TypedCursorWrapper(cursor);
|
||||
|
||||
String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone._ID,
|
||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
ContactsContract.CommonDataKinds.Phone.LABEL};
|
||||
|
||||
String sort = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE NOCASE ASC";
|
||||
|
||||
Map<String, String> projectionMap = new HashMap<String, String>() {{
|
||||
put(ID_COLUMN, ContactsContract.CommonDataKinds.Phone._ID);
|
||||
put(NAME_COLUMN, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
|
||||
put(NUMBER_COLUMN, ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
put(NUMBER_TYPE_COLUMN, ContactsContract.CommonDataKinds.Phone.TYPE);
|
||||
put(LABEL_COLUMN, ContactsContract.CommonDataKinds.Phone.LABEL);
|
||||
}};
|
||||
|
||||
Cursor cursor = context.getContentResolver().query(uri, projection,
|
||||
ContactsContract.Data.SYNC2 + " IS NULL OR " +
|
||||
ContactsContract.Data.SYNC2 + " != ?",
|
||||
new String[] {"__TS"},
|
||||
sort);
|
||||
|
||||
return new ProjectionMappingCursor(cursor, projectionMap,
|
||||
new Pair<String, Object>(CONTACT_TYPE_COLUMN, NORMAL_TYPE));
|
||||
}
|
||||
|
||||
private Cursor queryLocalDb(String filter) {
|
||||
final String selection;
|
||||
final String[] selectionArgs;
|
||||
final String fuzzyFilter = "%" + filter + "%";
|
||||
if (!TextUtils.isEmpty(filter)) {
|
||||
selection = FILTER_SELECTION;
|
||||
selectionArgs = new String[]{fuzzyFilter, fuzzyFilter};
|
||||
public @NonNull Cursor queryTextSecureContacts(String filter) {
|
||||
String[] projection = new String[] {ContactsContract.Data._ID,
|
||||
ContactsContract.Contacts.DISPLAY_NAME,
|
||||
ContactsContract.Data.DATA1};
|
||||
|
||||
String sort = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE NOCASE ASC";
|
||||
|
||||
Map<String, String> projectionMap = new HashMap<String, String>(){{
|
||||
put(ID_COLUMN, ContactsContract.Data._ID);
|
||||
put(NAME_COLUMN, ContactsContract.Contacts.DISPLAY_NAME);
|
||||
put(NUMBER_COLUMN, ContactsContract.Data.DATA1);
|
||||
}};
|
||||
|
||||
Cursor cursor;
|
||||
|
||||
if (TextUtils.isEmpty(filter)) {
|
||||
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
ContactsContract.Data.MIMETYPE + " = ?",
|
||||
new String[] {"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact"},
|
||||
sort);
|
||||
} else {
|
||||
selection = null;
|
||||
selectionArgs = null;
|
||||
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?",
|
||||
new String[] {"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact",
|
||||
"%" + filter + "%"},
|
||||
sort);
|
||||
}
|
||||
return queryLocalDb(selection, selectionArgs, null);
|
||||
|
||||
return new ProjectionMappingCursor(cursor, projectionMap,
|
||||
new Pair<String, Object>(LABEL_COLUMN, "TextSecure"),
|
||||
new Pair<String, Object>(NUMBER_TYPE_COLUMN, 0),
|
||||
new Pair<String, Object>(CONTACT_TYPE_COLUMN, PUSH_TYPE));
|
||||
|
||||
}
|
||||
|
||||
private Cursor queryLocalDb(String selection, String[] selectionArgs, String[] columns) {
|
||||
SQLiteDatabase localDb = dbHelper.getReadableDatabase();
|
||||
final Cursor localCursor;
|
||||
if (localDb != null) localCursor = localDb.query(TABLE_NAME, columns, selection, selectionArgs, null, null, CONTACT_LIST_SORT);
|
||||
else localCursor = null;
|
||||
if (localCursor != null && !localCursor.moveToFirst()) {
|
||||
localCursor.close();
|
||||
return null;
|
||||
}
|
||||
return localCursor;
|
||||
public Cursor getNewNumberCursor(String filter) {
|
||||
MatrixCursor newNumberCursor = new MatrixCursor(new String[] {ID_COLUMN, NAME_COLUMN, NUMBER_COLUMN, NUMBER_TYPE_COLUMN, LABEL_COLUMN, CONTACT_TYPE_COLUMN}, 1);
|
||||
newNumberCursor.addRow(new Object[]{-1L, context.getString(R.string.contact_selection_list__unknown_contact),
|
||||
filter, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"\u21e2", NORMAL_TYPE});
|
||||
|
||||
return newNumberCursor;
|
||||
}
|
||||
|
||||
private static class DatabaseOpenHelper extends SQLiteOpenHelper {
|
||||
private static class ProjectionMappingCursor extends CursorWrapper {
|
||||
|
||||
private final Context context;
|
||||
private SQLiteDatabase mDatabase;
|
||||
private final Map<String, String> projectionMap;
|
||||
private final Pair<String, Object>[] extras;
|
||||
|
||||
private static final String TABLE_CREATE =
|
||||
"CREATE TABLE " + TABLE_NAME + " (" +
|
||||
ID_COLUMN + " INTEGER PRIMARY KEY, " +
|
||||
NAME_COLUMN + " TEXT, " +
|
||||
NUMBER_TYPE_COLUMN + " INTEGER, " +
|
||||
LABEL_COLUMN + " TEXT, " +
|
||||
NUMBER_COLUMN + " TEXT, " +
|
||||
TYPE_COLUMN + " INTEGER);";
|
||||
|
||||
DatabaseOpenHelper(Context context) {
|
||||
super(context, null, null, 1);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
Log.d(TAG, "onCreate called for contacts database.");
|
||||
mDatabase = db;
|
||||
mDatabase.execSQL(TABLE_CREATE);
|
||||
if (TextSecurePreferences.isPushRegistered(context)) {
|
||||
try {
|
||||
loadPushUsers();
|
||||
} catch (IOException ioe) {
|
||||
Log.e(TAG, "Issue when trying to load push users into memory db.", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
private void loadPushUsers() throws IOException {
|
||||
Log.d(TAG, "populating push users into virtual db.");
|
||||
Collection<ContactAccessor.ContactData> pushUsers = ContactAccessor.getInstance().getContactsWithPush(context);
|
||||
for (ContactAccessor.ContactData user : pushUsers) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ID_COLUMN, user.id);
|
||||
values.put(NAME_COLUMN, user.name);
|
||||
values.put(NUMBER_TYPE_COLUMN, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM);
|
||||
values.put(LABEL_COLUMN, (String)null);
|
||||
values.put(NUMBER_COLUMN, user.numbers.get(0).number);
|
||||
values.put(TYPE_COLUMN, PUSH_TYPE);
|
||||
mDatabase.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
}
|
||||
Log.d(TAG, "finished populating push users.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TypedCursorWrapper extends CursorWrapper {
|
||||
|
||||
private final int pushColumnIndex;
|
||||
|
||||
public TypedCursorWrapper(Cursor cursor) {
|
||||
@SafeVarargs
|
||||
public ProjectionMappingCursor(Cursor cursor,
|
||||
Map<String, String> projectionMap,
|
||||
Pair<String, Object>... extras)
|
||||
{
|
||||
super(cursor);
|
||||
pushColumnIndex = cursor.getColumnCount();
|
||||
this.projectionMap = projectionMap;
|
||||
this.extras = extras;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return super.getColumnCount() + 1;
|
||||
return super.getColumnCount() + extras.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
if (TYPE_COLUMN.equals(columnName)) return super.getColumnCount();
|
||||
else return super.getColumnIndex(columnName);
|
||||
for (int i=0;i<extras.length;i++) {
|
||||
if (extras[i].first.equals(columnName)) {
|
||||
return super.getColumnCount() + i;
|
||||
}
|
||||
}
|
||||
|
||||
return super.getColumnIndex(projectionMap.get(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
|
||||
if (TYPE_COLUMN.equals(columnName)) return super.getColumnCount();
|
||||
else return super.getColumnIndexOrThrow(columnName);
|
||||
int index = getColumnIndex(columnName);
|
||||
|
||||
if (index == -1) throw new IllegalArgumentException("Bad column name!");
|
||||
else return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
if (columnIndex == pushColumnIndex) return TYPE_COLUMN;
|
||||
else return super.getColumnName(columnIndex);
|
||||
int baseColumnCount = super.getColumnCount();
|
||||
|
||||
if (columnIndex >= baseColumnCount) {
|
||||
int offset = columnIndex - baseColumnCount;
|
||||
return extras[offset].first;
|
||||
}
|
||||
|
||||
return getReverseProjection(super.getColumnName(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
final String[] columns = new String[super.getColumnCount() + 1];
|
||||
System.arraycopy(super.getColumnNames(), 0, columns, 0, super.getColumnCount());
|
||||
columns[pushColumnIndex] = TYPE_COLUMN;
|
||||
return columns;
|
||||
String[] names = super.getColumnNames();
|
||||
String[] allNames = new String[names.length + extras.length];
|
||||
|
||||
for (int i=0;i<names.length;i++) {
|
||||
allNames[i] = getReverseProjection(names[i]);
|
||||
}
|
||||
|
||||
for (int i=0;i<extras.length;i++) {
|
||||
allNames[names.length + i] = extras[i].first;
|
||||
}
|
||||
|
||||
return allNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
if (columnIndex == pushColumnIndex) return NORMAL_TYPE;
|
||||
else return super.getInt(columnIndex);
|
||||
if (columnIndex >= super.getColumnCount()) {
|
||||
int offset = columnIndex - super.getColumnCount();
|
||||
return (Integer)extras[offset].second;
|
||||
}
|
||||
|
||||
return super.getInt(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex) {
|
||||
if (columnIndex >= super.getColumnCount()) {
|
||||
int offset = columnIndex - super.getColumnCount();
|
||||
return (String)extras[offset].second;
|
||||
}
|
||||
|
||||
return super.getString(columnIndex);
|
||||
}
|
||||
|
||||
|
||||
private @Nullable String getReverseProjection(String columnName) {
|
||||
for (Map.Entry<String, String> entry : projectionMap.entrySet()) {
|
||||
if (entry.getValue().equals(columnName)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.DatabaseUpgradeActivity;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
|
||||
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
|
||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
@ -86,6 +87,7 @@ public class DatabaseFactory {
|
||||
private final PushDatabase pushDatabase;
|
||||
private final GroupDatabase groupDatabase;
|
||||
private final RecipientPreferenceDatabase recipientPreferenceDatabase;
|
||||
private final ContactsDatabase contactsDatabase;
|
||||
|
||||
public static DatabaseFactory getInstance(Context context) {
|
||||
synchronized (lock) {
|
||||
@ -148,6 +150,10 @@ public class DatabaseFactory {
|
||||
return getInstance(context).recipientPreferenceDatabase;
|
||||
}
|
||||
|
||||
public static ContactsDatabase getContactsDatabase(Context context) {
|
||||
return getInstance(context).contactsDatabase;
|
||||
}
|
||||
|
||||
private DatabaseFactory(Context context) {
|
||||
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
this.sms = new SmsDatabase(context, databaseHelper);
|
||||
@ -163,6 +169,7 @@ public class DatabaseFactory {
|
||||
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
||||
this.groupDatabase = new GroupDatabase(context, databaseHelper);
|
||||
this.recipientPreferenceDatabase = new RecipientPreferenceDatabase(context, databaseHelper);
|
||||
this.contactsDatabase = new ContactsDatabase(context);
|
||||
}
|
||||
|
||||
public void reset(Context context) {
|
||||
|
@ -12,6 +12,7 @@ import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||
@ -94,7 +95,7 @@ public class DirectoryHelper {
|
||||
}
|
||||
|
||||
try {
|
||||
new ContactsDatabase(context).setRegisteredUsers(account.get(), e164numbers);
|
||||
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get(), e164numbers);
|
||||
} catch (RemoteException | OperationApplicationException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user