Refactor ContactsCursorLoader to implement factory pattern.

Utilization of the factory pattern will enable us to more easily change what contacts we present to the user for a specific screen in the future instead of continuing to modify and potentially introduce bugs to this screen.
This commit is contained in:
Alex Hart 2021-03-26 09:24:40 -03:00
parent e068fde8f2
commit 243b4b9414
4 changed files with 297 additions and 184 deletions

View file

@ -58,6 +58,7 @@ import com.pnikosis.materialishprogress.ProgressWheel;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.components.emoji.WarningTextView;
import org.thoughtcrime.securesms.contacts.AbstractContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactChip;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
@ -114,22 +115,23 @@ public final class ContactSelectionListFragment extends LoggingFragment
public static final String CAN_SELECT_SELF = "can_select_self";
public static final String DISPLAY_CHIPS = "display_chips";
private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
private ChipGroup chipGroup;
private HorizontalScrollView chipGroupScrollContainer;
private WarningTextView groupLimit;
private OnSelectionLimitReachedListener onSelectionLimitReachedListener;
private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
private ChipGroup chipGroup;
private HorizontalScrollView chipGroupScrollContainer;
private WarningTextView groupLimit;
private OnSelectionLimitReachedListener onSelectionLimitReachedListener;
private AbstractContactsCursorLoaderFactoryProvider cursorFactoryProvider;
@Nullable private FixedViewsAdapter headerAdapter;
@ -162,6 +164,14 @@ public final class ContactSelectionListFragment extends LoggingFragment
if (context instanceof OnSelectionLimitReachedListener) {
onSelectionLimitReachedListener = (OnSelectionLimitReachedListener) context;
}
if (context instanceof AbstractContactsCursorLoaderFactoryProvider) {
cursorFactoryProvider = (AbstractContactsCursorLoaderFactoryProvider) context;
}
if (getParentFragment() instanceof AbstractContactsCursorLoaderFactoryProvider) {
cursorFactoryProvider = (AbstractContactsCursorLoaderFactoryProvider) context;
}
}
@Override
@ -387,10 +397,15 @@ public final class ContactSelectionListFragment extends LoggingFragment
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
FragmentActivity activity = requireActivity();
return new ContactsCursorLoader(activity,
activity.getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
cursorFilter, activity.getIntent().getBooleanExtra(RECENTS, false));
FragmentActivity activity = requireActivity();
int displayMode = activity.getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL);
boolean displayRecents = activity.getIntent().getBooleanExtra(RECENTS, false);
if (cursorFactoryProvider != null) {
return cursorFactoryProvider.get().create();
} else {
return new ContactsCursorLoader.Factory(activity, displayMode, cursorFilter, displayRecents).create();
}
}
@Override
@ -696,4 +711,8 @@ public final class ContactSelectionListFragment extends LoggingFragment
public interface ScrollCallback {
void onBeginScroll();
}
public interface AbstractContactsCursorLoaderFactoryProvider {
@NonNull AbstractContactsCursorLoader.Factory get();
}
}

View file

@ -0,0 +1,55 @@
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.content.CursorLoader;
import java.util.List;
public abstract class AbstractContactsCursorLoader extends CursorLoader {
private final String filter;
protected AbstractContactsCursorLoader(@NonNull Context context, @Nullable String filter) {
super(context);
this.filter = sanitizeFilter(filter);
}
@Override
public final Cursor loadInBackground() {
List<Cursor> cursorList = TextUtils.isEmpty(filter) ? getUnfilteredResults()
: getFilteredResults();
if (cursorList.size() > 0) {
return new MergeCursor(cursorList.toArray(new Cursor[0]));
}
return null;
}
protected final String getFilter() {
return filter;
}
protected abstract List<Cursor> getUnfilteredResults();
protected abstract List<Cursor> getFilteredResults();
private static @NonNull String sanitizeFilter(@Nullable String filter) {
if (filter == null) {
return "";
} else if (filter.startsWith("@")) {
return filter.substring(1);
} else {
return filter;
}
}
public interface Factory {
@NonNull AbstractContactsCursorLoader create();
}
}

View file

@ -20,13 +20,8 @@ import android.Manifest;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.provider.ContactsContract;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.content.CursorLoader;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
@ -37,7 +32,6 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.FeatureFlags;
@ -51,7 +45,7 @@ import java.util.List;
*
* @author Jake McGinty
*/
public class ContactsCursorLoader extends CursorLoader {
public class ContactsCursorLoader extends AbstractContactsCursorLoader {
private static final String TAG = ContactsCursorLoader.class.getSimpleName();
@ -64,60 +58,30 @@ public class ContactsCursorLoader extends CursorLoader {
public static final int FLAG_BLOCK = 1 << 5;
public static final int FLAG_HIDE_GROUPS_V1 = 1 << 5;
public static final int FLAG_HIDE_NEW = 1 << 6;
public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_ACTIVE_GROUPS | FLAG_INACTIVE_GROUPS | FLAG_SELF;
public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_ACTIVE_GROUPS | FLAG_INACTIVE_GROUPS | FLAG_SELF;
}
private static final String[] CONTACT_PROJECTION = new String[]{ContactRepository.ID_COLUMN,
ContactRepository.NAME_COLUMN,
ContactRepository.NUMBER_COLUMN,
ContactRepository.NUMBER_TYPE_COLUMN,
ContactRepository.LABEL_COLUMN,
ContactRepository.CONTACT_TYPE_COLUMN,
ContactRepository.ABOUT_COLUMN};
private static final int RECENT_CONVERSATION_MAX = 25;
private final String filter;
private final int mode;
private final boolean recents;
private final ContactRepository contactRepository;
public ContactsCursorLoader(@NonNull Context context, int mode, String filter, boolean recents)
private ContactsCursorLoader(@NonNull Context context, int mode, String filter, boolean recents)
{
super(context);
super(context, filter);
if (flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS) && !flagSet(mode, DisplayMode.FLAG_ACTIVE_GROUPS)) {
throw new AssertionError("Inactive group flag set, but the active group flag isn't!");
}
this.filter = sanitizeFilter(filter);
this.mode = mode;
this.recents = recents;
this.contactRepository = new ContactRepository(context);
}
@Override
public Cursor loadInBackground() {
List<Cursor> cursorList = TextUtils.isEmpty(filter) ? getUnfilteredResults()
: getFilteredResults();
if (cursorList.size() > 0) {
return new MergeCursor(cursorList.toArray(new Cursor[0]));
}
return null;
}
private static @NonNull String sanitizeFilter(@Nullable String filter) {
if (filter == null) {
return "";
} else if (filter.startsWith("@")) {
return filter.substring(1);
} else {
return filter;
}
}
private List<Cursor> getUnfilteredResults() {
protected final List<Cursor> getUnfilteredResults() {
ArrayList<Cursor> cursorList = new ArrayList<>();
if (groupsOnly(mode)) {
@ -131,7 +95,7 @@ public class ContactsCursorLoader extends CursorLoader {
return cursorList;
}
private List<Cursor> getFilteredResults() {
protected final List<Cursor> getFilteredResults() {
ArrayList<Cursor> cursorList = new ArrayList<>();
addContactsSection(cursorList);
@ -153,7 +117,7 @@ public class ContactsCursorLoader extends CursorLoader {
Cursor recentConversations = getRecentConversationsCursor();
if (recentConversations.getCount() > 0) {
cursorList.add(getRecentsHeaderCursor());
cursorList.add(ContactsCursorRows.forRecentsHeader(getContext()));
cursorList.add(recentConversations);
}
}
@ -162,7 +126,7 @@ public class ContactsCursorLoader extends CursorLoader {
List<Cursor> contacts = getContactsCursors();
if (!isCursorListEmpty(contacts)) {
cursorList.add(getContactsHeaderCursor());
cursorList.add(ContactsCursorRows.forContactsHeader(getContext()));
cursorList.addAll(contacts);
}
}
@ -175,7 +139,7 @@ public class ContactsCursorLoader extends CursorLoader {
Cursor groups = getRecentConversationsCursor(true);
if (groups.getCount() > 0) {
cursorList.add(getRecentsHeaderCursor());
cursorList.add(ContactsCursorRows.forRecentsHeader(getContext()));
cursorList.add(groups);
}
}
@ -188,89 +152,28 @@ public class ContactsCursorLoader extends CursorLoader {
Cursor groups = getGroupsCursor();
if (groups.getCount() > 0) {
cursorList.add(getGroupsHeaderCursor());
cursorList.add(ContactsCursorRows.forGroupsHeader(getContext()));
cursorList.add(groups);
}
}
private void addNewNumberSection(@NonNull List<Cursor> cursorList) {
if (FeatureFlags.usernames() && NumberUtil.isVisuallyValidNumberOrEmail(filter)) {
cursorList.add(getPhoneNumberSearchHeaderCursor());
if (FeatureFlags.usernames() && NumberUtil.isVisuallyValidNumberOrEmail(getFilter())) {
cursorList.add(ContactsCursorRows.forPhoneNumberSearchHeader(getContext()));
cursorList.add(getNewNumberCursor());
} else if (!FeatureFlags.usernames() && NumberUtil.isValidSmsOrEmail(filter)){
cursorList.add(getPhoneNumberSearchHeaderCursor());
} else if (!FeatureFlags.usernames() && NumberUtil.isValidSmsOrEmail(getFilter())) {
cursorList.add(ContactsCursorRows.forPhoneNumberSearchHeader(getContext()));
cursorList.add(getNewNumberCursor());
}
}
private void addUsernameSearchSection(@NonNull List<Cursor> cursorList) {
if (FeatureFlags.usernames() && UsernameUtil.isValidUsernameForSearch(filter)) {
cursorList.add(getUsernameSearchHeaderCursor());
if (FeatureFlags.usernames() && UsernameUtil.isValidUsernameForSearch(getFilter())) {
cursorList.add(ContactsCursorRows.forUsernameSearchHeader(getContext()));
cursorList.add(getUsernameSearchCursor());
}
}
private Cursor getRecentsHeaderCursor() {
MatrixCursor recentsHeader = new MatrixCursor(CONTACT_PROJECTION);
recentsHeader.addRow(new Object[]{ null,
getContext().getString(R.string.ContactsCursorLoader_recent_chats),
"",
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.DIVIDER_TYPE,
"" });
return recentsHeader;
}
private Cursor getContactsHeaderCursor() {
MatrixCursor contactsHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
contactsHeader.addRow(new Object[] { null,
getContext().getString(R.string.ContactsCursorLoader_contacts),
"",
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.DIVIDER_TYPE,
"" });
return contactsHeader;
}
private Cursor getGroupsHeaderCursor() {
MatrixCursor groupHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
groupHeader.addRow(new Object[]{ null,
getContext().getString(R.string.ContactsCursorLoader_groups),
"",
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.DIVIDER_TYPE,
"" });
return groupHeader;
}
private Cursor getPhoneNumberSearchHeaderCursor() {
MatrixCursor contactsHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
contactsHeader.addRow(new Object[] { null,
getContext().getString(R.string.ContactsCursorLoader_phone_number_search),
"",
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.DIVIDER_TYPE,
"" });
return contactsHeader;
}
private Cursor getUsernameSearchHeaderCursor() {
MatrixCursor contactsHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
contactsHeader.addRow(new Object[] { null,
getContext().getString(R.string.ContactsCursorLoader_username_search),
"",
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.DIVIDER_TYPE,
"" });
return contactsHeader;
}
private Cursor getRecentConversationsCursor() {
return getRecentConversationsCursor(false);
}
@ -278,21 +181,12 @@ public class ContactsCursorLoader extends CursorLoader {
private Cursor getRecentConversationsCursor(boolean groupsOnly) {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(getContext());
MatrixCursor recentConversations = new MatrixCursor(CONTACT_PROJECTION, RECENT_CONVERSATION_MAX);
MatrixCursor recentConversations = ContactsCursorRows.createMatrixCursor(RECENT_CONVERSATION_MAX);
try (Cursor rawConversations = threadDatabase.getRecentConversationList(RECENT_CONVERSATION_MAX, flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), groupsOnly, hideGroupsV1(mode), !smsEnabled(mode))) {
ThreadDatabase.Reader reader = threadDatabase.readerFor(rawConversations);
ThreadRecord threadRecord;
ThreadRecord threadRecord;
while ((threadRecord = reader.getNext()) != null) {
Recipient recipient = threadRecord.getRecipient();
String stringId = recipient.isGroup() ? recipient.requireGroupId().toString() : recipient.getE164().transform(PhoneNumberFormatter::prettyPrint).or(recipient.getEmail()).or("");
recentConversations.addRow(new Object[] { recipient.getId().serialize(),
recipient.getDisplayName(getContext()),
stringId,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.RECENT_TYPE | (recipient.isRegistered() && !recipient.isForceSmsSelection() ? ContactRepository.PUSH_TYPE : 0),
recipient.getCombinedAboutAndEmoji() });
recentConversations.addRow(ContactsCursorRows.forRecipient(getContext(), threadRecord.getRecipient()));
}
}
return recentConversations;
@ -306,56 +200,34 @@ public class ContactsCursorLoader extends CursorLoader {
}
if (pushEnabled(mode)) {
cursorList.add(contactRepository.querySignalContacts(filter, selfEnabled(mode)));
cursorList.add(contactRepository.querySignalContacts(getFilter(), selfEnabled(mode)));
}
if (pushEnabled(mode) && smsEnabled(mode)) {
cursorList.add(contactRepository.queryNonSignalContacts(filter));
cursorList.add(contactRepository.queryNonSignalContacts(getFilter()));
} else if (smsEnabled(mode)) {
cursorList.add(filterNonPushContacts(contactRepository.queryNonSignalContacts(filter)));
cursorList.add(filterNonPushContacts(contactRepository.queryNonSignalContacts(getFilter())));
}
return cursorList;
}
private Cursor getGroupsCursor() {
MatrixCursor groupContacts = new MatrixCursor(CONTACT_PROJECTION);
try (GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(getContext()).getGroupsFilteredByTitle(filter, flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode))) {
MatrixCursor groupContacts = ContactsCursorRows.createMatrixCursor();
try (GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(getContext()).getGroupsFilteredByTitle(getFilter(), flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode))) {
GroupDatabase.GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
groupContacts.addRow(new Object[] { groupRecord.getRecipientId().serialize(),
groupRecord.getTitle(),
groupRecord.getId(),
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"",
ContactRepository.NORMAL_TYPE,
"" });
groupContacts.addRow(ContactsCursorRows.forGroup(groupRecord));
}
}
return groupContacts;
}
private Cursor getNewNumberCursor() {
MatrixCursor newNumberCursor = new MatrixCursor(CONTACT_PROJECTION, 1);
newNumberCursor.addRow(new Object[] { null,
getUnknownContactTitle(),
filter,
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"\u21e2",
ContactRepository.NEW_PHONE_TYPE,
"" });
return newNumberCursor;
return ContactsCursorRows.forNewNumber(getUnknownContactTitle(), getFilter());
}
private Cursor getUsernameSearchCursor() {
MatrixCursor cursor = new MatrixCursor(CONTACT_PROJECTION, 1);
cursor.addRow(new Object[] { null,
getUnknownContactTitle(),
filter,
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"\u21e2",
ContactRepository.NEW_USERNAME_TYPE,
"" });
return cursor;
return ContactsCursorRows.forUsernameSearch(getUnknownContactTitle(), getFilter());
}
private String getUnknownContactTitle() {
@ -370,20 +242,14 @@ public class ContactsCursorLoader extends CursorLoader {
private @NonNull Cursor filterNonPushContacts(@NonNull Cursor cursor) {
try {
final long startMillis = System.currentTimeMillis();
final MatrixCursor matrix = new MatrixCursor(CONTACT_PROJECTION);
final long startMillis = System.currentTimeMillis();
final MatrixCursor matrix = ContactsCursorRows.createMatrixCursor();
while (cursor.moveToNext()) {
final RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN)));
final Recipient recipient = Recipient.resolved(id);
if (recipient.resolve().getRegistered() != RecipientDatabase.RegisteredState.REGISTERED) {
matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN)),
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN)),
ContactRepository.NORMAL_TYPE,
"" });
matrix.addRow(ContactsCursorRows.forNonPushContact(cursor));
}
}
Log.i(TAG, "filterNonPushContacts() -> " + (System.currentTimeMillis() - startMillis) + "ms");
@ -440,4 +306,24 @@ public class ContactsCursorLoader extends CursorLoader {
private static boolean flagSet(int mode, int flag) {
return (mode & flag) > 0;
}
public static class Factory implements AbstractContactsCursorLoader.Factory {
private final Context context;
private final int displayMode;
private final String cursorFilter;
private final boolean displayRecents;
public Factory(Context context, int displayMode, String cursorFilter, boolean displayRecents) {
this.context = context;
this.displayMode = displayMode;
this.cursorFilter = cursorFilter;
this.displayRecents = displayRecents;
}
@Override
public @NonNull AbstractContactsCursorLoader create() {
return new ContactsCursorLoader(context, displayMode, cursorFilter, displayRecents);
}
}
}

View file

@ -0,0 +1,153 @@
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.provider.ContactsContract;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
/**
* Helper utility for generating cursors and cursor rows for subclasses of {@link AbstractContactsCursorLoader}.
*/
public final class ContactsCursorRows {
private static final String[] CONTACT_PROJECTION = new String[]{ContactRepository.ID_COLUMN,
ContactRepository.NAME_COLUMN,
ContactRepository.NUMBER_COLUMN,
ContactRepository.NUMBER_TYPE_COLUMN,
ContactRepository.LABEL_COLUMN,
ContactRepository.CONTACT_TYPE_COLUMN,
ContactRepository.ABOUT_COLUMN};
/**
* Create a {@link MatrixCursor} with the proper projection for a subclass of {@link AbstractContactsCursorLoader}
*/
public static @NonNull MatrixCursor createMatrixCursor() {
return new MatrixCursor(CONTACT_PROJECTION);
}
/**
* Create a {@link MatrixCursor} with the proper projection for a subclass of {@link AbstractContactsCursorLoader}
*
* @param initialCapacity The initial capacity to hand to the {@link MatrixCursor}
*/
public static @NonNull MatrixCursor createMatrixCursor(int initialCapacity) {
return new MatrixCursor(CONTACT_PROJECTION, initialCapacity);
}
/**
* Create a row for a contacts cursor based off the given recipient.
*/
public static @NonNull Object[] forRecipient(@NonNull Context context, @NonNull Recipient recipient) {
String stringId = recipient.isGroup() ? recipient.requireGroupId().toString()
: recipient.getE164().transform(PhoneNumberFormatter::prettyPrint).or(recipient.getEmail()).or("");
return new Object[]{recipient.getId().serialize(),
recipient.getDisplayName(context),
stringId,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.RECENT_TYPE | (recipient.isRegistered() && !recipient.isForceSmsSelection() ? ContactRepository.PUSH_TYPE : 0),
recipient.getCombinedAboutAndEmoji()};
}
/**
* Create a row for a contacts cursor based off the given system contact.
*/
public static @NonNull Object[] forNonPushContact(@NonNull Cursor systemContactCursor) {
return new Object[]{systemContactCursor.getLong(systemContactCursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN)),
systemContactCursor.getString(systemContactCursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN)),
systemContactCursor.getString(systemContactCursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN)),
systemContactCursor.getString(systemContactCursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN)),
systemContactCursor.getString(systemContactCursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN)),
ContactRepository.NORMAL_TYPE,
""};
}
/**
* Create a row for a contacts cursor based off the given group record.
*/
public static @NonNull Object[] forGroup(@NonNull GroupDatabase.GroupRecord groupRecord) {
return new Object[]{groupRecord.getRecipientId().serialize(),
groupRecord.getTitle(),
groupRecord.getId(),
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"",
ContactRepository.NORMAL_TYPE,
""};
}
/**
* Create a row for a contacts cursor for a new number the user is entering or has entered.
*/
public static @NonNull MatrixCursor forNewNumber(@NonNull String unknownContactTitle, @NonNull String filter) {
MatrixCursor matrixCursor = createMatrixCursor(1);
matrixCursor.addRow(new Object[]{null,
unknownContactTitle,
filter,
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"\u21e2",
ContactRepository.NEW_PHONE_TYPE,
""});
return matrixCursor;
}
/**
* Create a row for a contacts cursor for a username the user is entering or has entered.
*/
public static @NonNull MatrixCursor forUsernameSearch(@NonNull String unknownContactTitle, @NonNull String filter) {
MatrixCursor matrixCursor = createMatrixCursor(1);
matrixCursor.addRow(new Object[]{null,
unknownContactTitle,
filter,
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
"\u21e2",
ContactRepository.NEW_USERNAME_TYPE,
""});
return matrixCursor;
}
public static @NonNull MatrixCursor forUsernameSearchHeader(@NonNull Context context) {
return forHeader(context.getString(R.string.ContactsCursorLoader_username_search));
}
public static @NonNull MatrixCursor forPhoneNumberSearchHeader(@NonNull Context context) {
return forHeader(context.getString(R.string.ContactsCursorLoader_phone_number_search));
}
public static @NonNull MatrixCursor forGroupsHeader(@NonNull Context context) {
return forHeader(context.getString(R.string.ContactsCursorLoader_groups));
}
public static @NonNull MatrixCursor forRecentsHeader(@NonNull Context context) {
return forHeader(context.getString(R.string.ContactsCursorLoader_recent_chats));
}
public static @NonNull MatrixCursor forContactsHeader(@NonNull Context context) {
return forHeader(context.getString(R.string.ContactsCursorLoader_contacts));
}
public static @NonNull MatrixCursor forHeader(@NonNull String name) {
MatrixCursor matrixCursor = createMatrixCursor(1);
matrixCursor.addRow(new Object[]{null,
name,
"",
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
"",
ContactRepository.DIVIDER_TYPE,
""});
return matrixCursor;
}
}