Fix contact search query returning outdated or bad recipients.

This commit is contained in:
Alex Hart
2021-07-16 13:53:17 -03:00
committed by GitHub
parent 61f880fd78
commit f1a87518e1
2 changed files with 256 additions and 76 deletions

View File

@@ -8,6 +8,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
@@ -2314,20 +2315,13 @@ public class RecipientDatabase extends Database {
}
public @Nullable Cursor getSignalContacts(boolean includeSelf) {
String selection = BLOCKED + " = ? AND " +
REGISTERED + " = ? AND " +
GROUP_ID + " IS NULL AND " +
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)";
String[] args;
if (includeSelf) {
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1" };
} else {
selection += " AND " + ID + " != ?";
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", Recipient.self().getId().serialize() };
}
ContactSearchSelection searchSelection = new ContactSearchSelection.Builder().withRegistered(true)
.withGroups(false)
.excludeId(includeSelf ? null : Recipient.self().getId())
.build();
String selection = searchSelection.getWhere();
String[] args = searchSelection.getArgs();
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
@@ -2336,23 +2330,14 @@ public class RecipientDatabase extends Database {
public @Nullable Cursor querySignalContacts(@NonNull String query, boolean includeSelf) {
query = buildCaseInsensitiveGlobPattern(query);
String selection = BLOCKED + " = ? AND " +
REGISTERED + " = ? AND " +
GROUP_ID + " IS NULL AND " +
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" +
PHONE + " GLOB ? OR " +
SORT_NAME + " GLOB ? OR " +
USERNAME + " GLOB ?" +
")";
String[] args;
ContactSearchSelection searchSelection = new ContactSearchSelection.Builder().withRegistered(true)
.withGroups(false)
.excludeId(includeSelf ? null : Recipient.self().getId())
.withSearchQuery(query)
.build();
if (includeSelf) {
args = new String[]{"0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query};
} else {
selection += " AND " + ID + " != ?";
args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query, String.valueOf(Recipient.self().getId().toLong()) };
}
String selection = searchSelection.getWhere();
String[] args = searchSelection.getArgs();
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + PHONE;
@@ -2360,12 +2345,12 @@ public class RecipientDatabase extends Database {
}
public @Nullable Cursor getNonSignalContacts() {
String selection = BLOCKED + " = ? AND " +
REGISTERED + " != ? AND " +
GROUP_ID + " IS NULL AND " +
SYSTEM_CONTACT_URI + " NOT NULL AND " +
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)";
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) };
ContactSearchSelection searchSelection = new ContactSearchSelection.Builder().withNonRegistered(true)
.withGroups(false)
.build();
String selection = searchSelection.getWhere();
String[] args = searchSelection.getArgs();
String orderBy = SYSTEM_JOINED_NAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
@@ -2374,61 +2359,42 @@ public class RecipientDatabase extends Database {
public @Nullable Cursor queryNonSignalContacts(@NonNull String query) {
query = buildCaseInsensitiveGlobPattern(query);
String selection = BLOCKED + " = ? AND " +
REGISTERED + " != ? AND " +
GROUP_ID + " IS NULL AND " +
SYSTEM_CONTACT_URI + " NOT NULL AND " +
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " +
"(" +
PHONE + " GLOB ? OR " +
EMAIL + " GLOB ? OR " +
SYSTEM_JOINED_NAME + " GLOB ?" +
")";
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query, query };
ContactSearchSelection searchSelection = new ContactSearchSelection.Builder().withNonRegistered(true)
.withGroups(false)
.withSearchQuery(query)
.build();
String selection = searchSelection.getWhere();
String[] args = searchSelection.getArgs();
String orderBy = SYSTEM_JOINED_NAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
}
public @Nullable Cursor getNonGroupContacts(boolean includeSelf) {
String selection = BLOCKED + " = ? AND " +
GROUP_ID + " IS NULL AND " +
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)";
String[] args;
ContactSearchSelection searchSelection = new ContactSearchSelection.Builder().withRegistered(true)
.withNonRegistered(true)
.withGroups(false)
.excludeId(includeSelf ? null : Recipient.self().getId())
.build();
if (includeSelf) {
args = SqlUtil.buildArgs("0", "1");
} else {
selection += " AND " + ID + " != ?";
args = SqlUtil.buildArgs("0", "1", Recipient.self().getId().serialize());
}
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE;
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + USERNAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, searchSelection.where, searchSelection.args, null, null, orderBy);
}
public @Nullable Cursor queryNonGroupContacts(@NonNull String query, boolean includeSelf) {
query = buildCaseInsensitiveGlobPattern(query);
String selection = BLOCKED + " = ? AND " +
GROUP_ID + " IS NULL AND " +
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" +
PHONE + " GLOB ? OR " +
SORT_NAME + " GLOB ? OR " +
USERNAME + " GLOB ?" +
")";
String[] args;
if (includeSelf) {
args = SqlUtil.buildArgs("0", "1", query, query, query);
} else {
selection += " AND " + ID + " != ?";
args = SqlUtil.buildArgs("0", "1", query, query, query, Recipient.self().getId().toLong());
}
ContactSearchSelection searchSelection = new ContactSearchSelection.Builder().withRegistered(true)
.withNonRegistered(true)
.withGroups(false)
.excludeId(includeSelf ? null : Recipient.self().getId())
.withSearchQuery(query)
.build();
String selection = searchSelection.getWhere();
String[] args = searchSelection.getArgs();
String orderBy = SORT_NAME + ", " + SYSTEM_JOINED_NAME + ", " + SEARCH_PROFILE_NAME + ", " + PHONE;
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
@@ -3589,4 +3555,146 @@ public class RecipientDatabase extends Database {
this.neededInsert = neededInsert;
}
}
@VisibleForTesting
static final class ContactSearchSelection {
static final String FILTER_GROUPS = " AND " + GROUP_ID + " IS NULL";
static final String FILTER_ID = " AND " + ID + " != ?";
static final String FILTER_BLOCKED = " AND " + BLOCKED + " = ?";
static final String NON_SIGNAL_CONTACT = REGISTERED + " != ? AND " +
SYSTEM_CONTACT_URI + " NOT NULL AND " +
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)";
static final String QUERY_NON_SIGNAL_CONTACT = NON_SIGNAL_CONTACT +
" AND (" +
PHONE + " GLOB ? OR " +
EMAIL + " GLOB ? OR " +
SYSTEM_JOINED_NAME + " GLOB ?" +
")";
static final String SIGNAL_CONTACT = REGISTERED + " = ? AND " +
"(" + SYSTEM_JOINED_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
"(" + SORT_NAME + " NOT NULL OR " + USERNAME + " NOT NULL)";
static final String QUERY_SIGNAL_CONTACT = SIGNAL_CONTACT + " AND (" +
PHONE + " GLOB ? OR " +
SORT_NAME + " GLOB ? OR " +
USERNAME + " GLOB ?" +
")";
private final String where;
private final String[] args;
private ContactSearchSelection(@NonNull String where, @NonNull String[] args) {
this.where = where;
this.args = args;
}
String getWhere() {
return where;
}
String[] getArgs() {
return args;
}
@VisibleForTesting
static final class Builder {
private boolean includeRegistered;
private boolean includeNonRegistered;
private RecipientId excludeId;
private boolean excludeGroups;
private String searchQuery;
@NonNull Builder withRegistered(boolean includeRegistered) {
this.includeRegistered = includeRegistered;
return this;
}
@NonNull Builder withNonRegistered(boolean includeNonRegistered) {
this.includeNonRegistered = includeNonRegistered;
return this;
}
@NonNull Builder excludeId(@Nullable RecipientId recipientId) {
this.excludeId = recipientId;
return this;
}
@NonNull Builder withGroups(boolean includeGroups) {
this.excludeGroups = !includeGroups;
return this;
}
@NonNull Builder withSearchQuery(@NonNull String searchQuery) {
this.searchQuery = searchQuery;
return this;
}
@NonNull ContactSearchSelection build() {
if (!includeRegistered && !includeNonRegistered) {
throw new IllegalStateException("Must include either registered or non-registered recipients in search");
}
StringBuilder stringBuilder = new StringBuilder("(");
List<Object> args = new LinkedList<>();
if (includeRegistered) {
stringBuilder.append("(");
args.add(RegisteredState.REGISTERED.id);
args.add(1);
if (Util.isEmpty(searchQuery)) {
stringBuilder.append(SIGNAL_CONTACT);
} else {
stringBuilder.append(QUERY_SIGNAL_CONTACT);
args.add(searchQuery);
args.add(searchQuery);
args.add(searchQuery);
}
stringBuilder.append(")");
}
if (includeRegistered && includeNonRegistered) {
stringBuilder.append(" OR ");
}
if (includeNonRegistered) {
stringBuilder.append("(");
args.add(RegisteredState.REGISTERED.id);
if (Util.isEmpty(searchQuery)) {
stringBuilder.append(NON_SIGNAL_CONTACT);
} else {
stringBuilder.append(QUERY_SIGNAL_CONTACT);
args.add(searchQuery);
args.add(searchQuery);
args.add(searchQuery);
}
stringBuilder.append(")");
}
stringBuilder.append(")");
stringBuilder.append(FILTER_BLOCKED);
args.add(0);
if (excludeGroups) {
stringBuilder.append(FILTER_GROUPS);
}
if (excludeId != null) {
stringBuilder.append(FILTER_ID);
args.add(excludeId.serialize());
}
return new ContactSearchSelection(stringBuilder.toString(), args.stream().map(Object::toString).toArray(String[]::new));
}
}
}
}