Search contacts via the RecipientDatabase.
parent
0e2d52026e
commit
582028f2c2
|
@ -29,7 +29,7 @@
|
|||
android:layout_marginEnd="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
<org.thoughtcrime.securesms.components.FromTextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -38,7 +38,7 @@
|
|||
android:ellipsize="marquee"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
tools:text="Frieeeeeeedrich Nieeeeeeeeeetzsche" />
|
||||
tools:text="Ottttooooooooo Ocataaaaaaaavius" />
|
||||
|
||||
<LinearLayout android:id="@+id/number_container"
|
||||
android:orientation="horizontal"
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context;
|
|||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
|
|
|
@ -37,6 +37,8 @@ import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
|||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
@ -45,6 +47,7 @@ import org.thoughtcrime.securesms.permissions.Permissions;
|
|||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
|
||||
import org.thoughtcrime.securesms.profiles.SystemProfileUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
|
@ -362,6 +365,7 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
|||
try {
|
||||
accountManager.setProfileName(profileKey, name);
|
||||
TextSecurePreferences.setProfileName(context, name);
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileName(Recipient.self().getId(), name);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.widget.Toast;
|
|||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
|
|
@ -63,6 +63,7 @@ import org.thoughtcrime.securesms.database.Address;
|
|||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.gcm.FcmUtil;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
|
@ -734,19 +735,21 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
|
|||
TextSecurePreferences.setFcmToken(RegistrationActivity.this, registrationState.gcmToken.orNull());
|
||||
TextSecurePreferences.setFcmDisabled(RegistrationActivity.this, !registrationState.gcmToken.isPresent());
|
||||
TextSecurePreferences.setWebsocketRegistered(RegistrationActivity.this, true);
|
||||
TextSecurePreferences.setLocalNumber(RegistrationActivity.this, registrationState.e164number);
|
||||
|
||||
DatabaseFactory.getIdentityDatabase(RegistrationActivity.this)
|
||||
.saveIdentity(Recipient.external(RegistrationActivity.this, registrationState.e164number).getId(),
|
||||
.saveIdentity(Recipient.self().getId(),
|
||||
identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED,
|
||||
true, System.currentTimeMillis(), true);
|
||||
|
||||
TextSecurePreferences.setVerifying(RegistrationActivity.this, false);
|
||||
TextSecurePreferences.setPushRegistered(RegistrationActivity.this, true);
|
||||
TextSecurePreferences.setLocalNumber(RegistrationActivity.this, registrationState.e164number);
|
||||
TextSecurePreferences.setPushServerPassword(RegistrationActivity.this, registrationState.password);
|
||||
TextSecurePreferences.setSignedPreKeyRegistered(RegistrationActivity.this, true);
|
||||
TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true);
|
||||
TextSecurePreferences.setUnauthorizedReceived(RegistrationActivity.this, false);
|
||||
DatabaseFactory.getRecipientDatabase(this).setProfileSharing(Recipient.self().getId(), true);
|
||||
DatabaseFactory.getRecipientDatabase(this).setRegistered(Recipient.self().getId(), RecipientDatabase.RegisteredState.REGISTERED);
|
||||
}
|
||||
|
||||
private void handleSuccessfulRegistration() {
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.database.Cursor;
|
|||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Process;
|
||||
import android.provider.OpenableColumns;
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -39,7 +38,6 @@ import android.widget.ImageView;
|
|||
import org.thoughtcrime.securesms.components.SearchToolbar;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
|
|
@ -85,7 +85,7 @@ public class ContactAccessor {
|
|||
}
|
||||
|
||||
public Cursor getAllSystemContacts(Context context) {
|
||||
return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY}, null, null, null);
|
||||
return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY, Phone.TYPE}, null, null, null);
|
||||
}
|
||||
|
||||
public boolean isSystemContact(Context context, String number) {
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Repository for all contacts. Allows you to filter them via queries.
|
||||
*
|
||||
* Currently this is implemented to return cursors. This is to ease the migration between this class
|
||||
* and the previous way we'd query contacts: {@link ContactsDatabase}. It's much easier in the
|
||||
* short-term to mock the cursor interface rather than try to switch everything over to models.
|
||||
*/
|
||||
public class ContactRepository {
|
||||
|
||||
private final RecipientDatabase recipientDatabase;
|
||||
private final String noteToSelfTitle;
|
||||
|
||||
public static final String ID_COLUMN = "id";
|
||||
static final String NAME_COLUMN = "name";
|
||||
static final String NUMBER_COLUMN = "number";
|
||||
static final String NUMBER_TYPE_COLUMN = "number_type";
|
||||
static final String LABEL_COLUMN = "label";
|
||||
static final String CONTACT_TYPE_COLUMN = "contact_type";
|
||||
|
||||
static final int NORMAL_TYPE = 0;
|
||||
static final int PUSH_TYPE = 1;
|
||||
static final int NEW_TYPE = 2;
|
||||
static final int RECENT_TYPE = 3;
|
||||
static final int DIVIDER_TYPE = 4;
|
||||
|
||||
/** Maps the recipient results to the legacy contact column names */
|
||||
private static final List<Pair<String, ValueMapper>> SEARCH_CURSOR_MAPPERS = new ArrayList<Pair<String, ValueMapper>>() {{
|
||||
add(new Pair<>(ID_COLUMN, cursor -> cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID))));
|
||||
|
||||
add(new Pair<>(NAME_COLUMN, cursor -> {
|
||||
String system = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_DISPLAY_NAME));
|
||||
String profile = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SIGNAL_PROFILE_NAME));
|
||||
|
||||
return !TextUtils.isEmpty(system) ? system : profile;
|
||||
}));
|
||||
|
||||
add(new Pair<>(NUMBER_COLUMN, cursor -> {
|
||||
String phone = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.PHONE));
|
||||
String email = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.EMAIL));
|
||||
|
||||
return !TextUtils.isEmpty(phone) ? phone : email;
|
||||
}));
|
||||
|
||||
add(new Pair<>(NUMBER_TYPE_COLUMN, cursor -> cursor.getInt(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_PHONE_TYPE))));
|
||||
|
||||
add(new Pair<>(LABEL_COLUMN, cursor -> cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_PHONE_LABEL))));
|
||||
|
||||
add(new Pair<>(CONTACT_TYPE_COLUMN, cursor -> {
|
||||
int registered = cursor.getInt(cursor.getColumnIndexOrThrow(RecipientDatabase.REGISTERED));
|
||||
return registered == RecipientDatabase.RegisteredState.REGISTERED.getId() ? PUSH_TYPE : NORMAL_TYPE;
|
||||
}));
|
||||
}};
|
||||
|
||||
public ContactRepository(@NonNull Context context) {
|
||||
this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
this.noteToSelfTitle = context.getString(R.string.note_to_self);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public Cursor querySignalContacts(@NonNull String query) {
|
||||
Cursor cursor = TextUtils.isEmpty(query) ? recipientDatabase.getSignalContacts()
|
||||
: recipientDatabase.querySignalContacts(query);
|
||||
|
||||
|
||||
if (noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) {
|
||||
Recipient self = Recipient.self();
|
||||
boolean nameMatch = self.getDisplayName().toLowerCase().contains(query.toLowerCase());
|
||||
boolean numberMatch = self.requireAddress().serialize() != null && self.requireAddress().serialize().contains(query);
|
||||
boolean shouldAdd = !nameMatch && !numberMatch;
|
||||
|
||||
if (shouldAdd) {
|
||||
MatrixCursor selfCursor = new MatrixCursor(RecipientDatabase.SEARCH_PROJECTION);
|
||||
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, null, self.requireAddress().serialize(), null, null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), noteToSelfTitle });
|
||||
|
||||
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
|
||||
}
|
||||
}
|
||||
|
||||
return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public Cursor queryNonSignalContacts(@NonNull String query) {
|
||||
Cursor cursor = TextUtils.isEmpty(query) ? recipientDatabase.getNonSignalContacts()
|
||||
: recipientDatabase.queryNonSignalContacts(query);
|
||||
return new SearchCursorWrapper(cursor, SEARCH_CURSOR_MAPPERS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This lets us mock the legacy cursor interface while using the new cursor, even though the data
|
||||
* doesn't quite match up exactly.
|
||||
*/
|
||||
private static class SearchCursorWrapper extends CursorWrapper {
|
||||
|
||||
private final Cursor wrapped;
|
||||
private final String[] columnNames;
|
||||
private final List<Pair<String, ValueMapper>> mappers;
|
||||
private final Map<String, Integer> positions;
|
||||
|
||||
SearchCursorWrapper(Cursor cursor, @NonNull List<Pair<String, ValueMapper>> mappers) {
|
||||
super(cursor);
|
||||
|
||||
this.wrapped = cursor;
|
||||
this.mappers = mappers;
|
||||
this.positions = new HashMap<>();
|
||||
this.columnNames = new String[mappers.size()];
|
||||
|
||||
for (int i = 0; i < mappers.size(); i++) {
|
||||
Pair<String, ValueMapper> pair = mappers.get(i);
|
||||
|
||||
positions.put(pair.first(), i);
|
||||
columnNames[i] = pair.first();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return mappers.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
|
||||
Integer index = positions.get(columnName);
|
||||
|
||||
if (index != null) {
|
||||
return index;
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex) {
|
||||
return String.valueOf(mappers.get(columnIndex).second().get(wrapped));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
return (int) mappers.get(columnIndex).second().get(wrapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int columnIndex) {
|
||||
return (long) mappers.get(columnIndex).second().get(wrapped);
|
||||
}
|
||||
}
|
||||
|
||||
private interface ValueMapper<T> {
|
||||
T get(@NonNull Cursor cursor);
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolde
|
|||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
|
@ -77,7 +78,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
super(itemView);
|
||||
}
|
||||
|
||||
public abstract void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect);
|
||||
public abstract void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect);
|
||||
public abstract void unbind(@NonNull GlideRequests glideRequests);
|
||||
public abstract void setChecked(boolean checked);
|
||||
}
|
||||
|
@ -96,8 +97,8 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
return (ContactSelectionListItem) itemView;
|
||||
}
|
||||
|
||||
public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
getView().set(glideRequests, type, name, number, label, color, multiSelect);
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
getView().set(glideRequests, recipientId, type, name, number, label, color, multiSelect);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -121,7 +122,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
this.label.setText(name);
|
||||
}
|
||||
|
||||
|
@ -158,7 +159,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
|
||||
int contactType = getContactType(i);
|
||||
|
||||
if (contactType == ContactsDatabase.DIVIDER_TYPE) return -1;
|
||||
if (contactType == ContactRepository.DIVIDER_TYPE) return -1;
|
||||
return Util.hashCode(getHeaderString(i), getContactType(i));
|
||||
}
|
||||
|
||||
|
@ -173,25 +174,27 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
|
||||
@Override
|
||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
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(getContext().getResources(),
|
||||
numberType, label).toString();
|
||||
String rawId = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN));
|
||||
RecipientId id = rawId != null ? RecipientId.from(rawId) : null;
|
||||
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN));
|
||||
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN ));
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN));
|
||||
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN ));
|
||||
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN ));
|
||||
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getContext().getResources(),
|
||||
numberType, label).toString();
|
||||
|
||||
int color = (contactType == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
|
||||
int color = (contactType == ContactRepository.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
|
||||
drawables.getColor(1, 0xff000000);
|
||||
|
||||
viewHolder.unbind(glideRequests);
|
||||
viewHolder.bind(glideRequests, contactType, name, number, labelText, color, multiSelect);
|
||||
viewHolder.bind(glideRequests, id, contactType, name, number, labelText, color, multiSelect);
|
||||
viewHolder.setChecked(selectedContacts.contains(number));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(@NonNull Cursor cursor) {
|
||||
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN)) == ContactsDatabase.DIVIDER_TYPE) {
|
||||
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN)) == ContactRepository.DIVIDER_TYPE) {
|
||||
return VIEW_TYPE_DIVIDER;
|
||||
} else {
|
||||
return VIEW_TYPE_CONTACT;
|
||||
|
@ -237,12 +240,12 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
private @NonNull String getHeaderString(int position) {
|
||||
int contactType = getContactType(position);
|
||||
|
||||
if (contactType == ContactsDatabase.RECENT_TYPE || contactType == ContactsDatabase.DIVIDER_TYPE) {
|
||||
if (contactType == ContactRepository.RECENT_TYPE || contactType == ContactRepository.DIVIDER_TYPE) {
|
||||
return " ";
|
||||
}
|
||||
|
||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
|
||||
String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN));
|
||||
|
||||
if (letter != null) {
|
||||
letter = letter.trim();
|
||||
|
@ -259,11 +262,11 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
|
||||
private int getContactType(int position) {
|
||||
final Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
|
||||
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN));
|
||||
}
|
||||
|
||||
private boolean isPush(int position) {
|
||||
return getContactType(position) == ContactsDatabase.PUSH_TYPE;
|
||||
return getContactType(position) == ContactRepository.PUSH_TYPE;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.contacts;
|
|||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
@ -11,11 +13,13 @@ import android.widget.TextView;
|
|||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
@ -27,7 +31,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
|
||||
private AvatarImageView contactPhotoImage;
|
||||
private TextView numberView;
|
||||
private TextView nameView;
|
||||
private FromTextView nameView;
|
||||
private TextView labelView;
|
||||
private CheckBox checkBox;
|
||||
|
||||
|
@ -55,16 +59,23 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
ViewUtil.setTextViewGravityStart(this.nameView, getContext());
|
||||
}
|
||||
|
||||
public void set(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
public void set(@NonNull GlideRequests glideRequests,
|
||||
@Nullable RecipientId recipientId,
|
||||
int type,
|
||||
String name,
|
||||
String number,
|
||||
String label,
|
||||
int color,
|
||||
boolean multiSelect)
|
||||
{
|
||||
this.glideRequests = glideRequests;
|
||||
this.number = number;
|
||||
|
||||
if (type == ContactsDatabase.NEW_TYPE) {
|
||||
if (type == ContactRepository.NEW_TYPE) {
|
||||
this.recipient = null;
|
||||
this.contactPhotoImage.setAvatar(glideRequests, Recipient.UNKNOWN, false);
|
||||
} else if (!TextUtils.isEmpty(number)) {
|
||||
// TODO [greyson] We really don't want to have to do a read like this here
|
||||
this.recipient = Recipient.external(getContext(), number).live();
|
||||
} else if (recipientId != null) {
|
||||
this.recipient = Recipient.live(recipientId);
|
||||
this.recipient.observeForever(this);
|
||||
|
||||
if (this.recipient.get().getName() != null) {
|
||||
|
@ -72,15 +83,13 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
}
|
||||
}
|
||||
|
||||
Recipient recipientSnapshot = recipient != null ? recipient.get() : null;
|
||||
|
||||
this.nameView.setTextColor(color);
|
||||
this.numberView.setTextColor(color);
|
||||
this.contactPhotoImage.setAvatar(glideRequests, recipient != null ? recipient.get() : null, false);
|
||||
this.contactPhotoImage.setAvatar(glideRequests, recipientSnapshot, false);
|
||||
|
||||
if (!multiSelect && recipient != null && recipient.get().isLocalNumber()) {
|
||||
name = getContext().getString(R.string.note_to_self);
|
||||
}
|
||||
|
||||
setText(type, name, number, label);
|
||||
setText(recipientSnapshot, type, name, number, label);
|
||||
|
||||
if (multiSelect) this.checkBox.setVisibility(View.VISIBLE);
|
||||
else this.checkBox.setVisibility(View.GONE);
|
||||
|
@ -97,12 +106,12 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
}
|
||||
}
|
||||
|
||||
private void setText(int type, String name, String number, String label) {
|
||||
private void setText(@Nullable Recipient recipient, int type, String name, String number, String label) {
|
||||
if (number == null || number.isEmpty() || GroupUtil.isEncodedGroup(number)) {
|
||||
this.nameView.setEnabled(false);
|
||||
this.numberView.setText("");
|
||||
this.labelView.setVisibility(View.GONE);
|
||||
} else if (type == ContactsDatabase.PUSH_TYPE) {
|
||||
} else if (type == ContactRepository.PUSH_TYPE) {
|
||||
this.numberView.setText(number);
|
||||
this.nameView.setEnabled(true);
|
||||
this.labelView.setVisibility(View.GONE);
|
||||
|
@ -113,7 +122,11 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
this.labelView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
this.nameView.setText(name);
|
||||
if (recipient != null) {
|
||||
this.nameView.setText(recipient);
|
||||
} else {
|
||||
this.nameView.setText(name);
|
||||
}
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
|
@ -123,6 +136,6 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
@Override
|
||||
public void onRecipientChanged(@NonNull Recipient recipient) {
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, false);
|
||||
nameView.setText(recipient.toShortString());
|
||||
nameView.setText(recipient);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -56,11 +57,12 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_GROUPS;
|
||||
}
|
||||
|
||||
private static final String[] CONTACT_PROJECTION = new String[]{ContactsDatabase.NAME_COLUMN,
|
||||
ContactsDatabase.NUMBER_COLUMN,
|
||||
ContactsDatabase.NUMBER_TYPE_COLUMN,
|
||||
ContactsDatabase.LABEL_COLUMN,
|
||||
ContactsDatabase.CONTACT_TYPE_COLUMN};
|
||||
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};
|
||||
|
||||
private static final int RECENT_CONVERSATION_MAX = 25;
|
||||
|
||||
|
@ -68,13 +70,16 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
private final int mode;
|
||||
private final boolean recents;
|
||||
|
||||
private final ContactRepository contactRepository;
|
||||
|
||||
public ContactsCursorLoader(@NonNull Context context, int mode, String filter, boolean recents)
|
||||
{
|
||||
super(context);
|
||||
|
||||
this.filter = filter;
|
||||
this.mode = mode;
|
||||
this.recents = recents;
|
||||
this.filter = filter == null ? "" : filter;
|
||||
this.mode = mode;
|
||||
this.recents = recents;
|
||||
this.contactRepository = new ContactRepository(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -131,31 +136,34 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
|
||||
private Cursor getRecentsHeaderCursor() {
|
||||
MatrixCursor recentsHeader = new MatrixCursor(CONTACT_PROJECTION);
|
||||
recentsHeader.addRow(new Object[]{ getContext().getString(R.string.ContactsCursorLoader_recent_chats),
|
||||
recentsHeader.addRow(new Object[]{ null,
|
||||
getContext().getString(R.string.ContactsCursorLoader_recent_chats),
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactsDatabase.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
return recentsHeader;
|
||||
}
|
||||
|
||||
private Cursor getContactsHeaderCursor() {
|
||||
MatrixCursor contactsHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
|
||||
contactsHeader.addRow(new Object[] { getContext().getString(R.string.ContactsCursorLoader_contacts),
|
||||
contactsHeader.addRow(new Object[] { null,
|
||||
getContext().getString(R.string.ContactsCursorLoader_contacts),
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactsDatabase.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
return contactsHeader;
|
||||
}
|
||||
|
||||
private Cursor getGroupsHeaderCursor() {
|
||||
MatrixCursor groupHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
|
||||
groupHeader.addRow(new Object[]{ getContext().getString(R.string.ContactsCursorLoader_groups),
|
||||
groupHeader.addRow(new Object[]{ null,
|
||||
getContext().getString(R.string.ContactsCursorLoader_groups),
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactsDatabase.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
return groupHeader;
|
||||
}
|
||||
|
||||
|
@ -168,32 +176,32 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
ThreadDatabase.Reader reader = threadDatabase.readerFor(rawConversations);
|
||||
ThreadRecord threadRecord;
|
||||
while ((threadRecord = reader.getNext()) != null) {
|
||||
recentConversations.addRow(new Object[] { threadRecord.getRecipient().toShortString(),
|
||||
recentConversations.addRow(new Object[] { threadRecord.getRecipient().getId().serialize(),
|
||||
threadRecord.getRecipient().toShortString(),
|
||||
threadRecord.getRecipient().requireAddress().serialize(),
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactsDatabase.RECENT_TYPE });
|
||||
ContactRepository.RECENT_TYPE });
|
||||
}
|
||||
}
|
||||
return recentConversations;
|
||||
}
|
||||
|
||||
private List<Cursor> getContactsCursors() {
|
||||
ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(getContext());
|
||||
List<Cursor> cursorList = new ArrayList<>(2);
|
||||
List<Cursor> cursorList = new ArrayList<>(2);
|
||||
|
||||
if (!Permissions.hasAny(getContext(), Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) {
|
||||
return cursorList;
|
||||
}
|
||||
|
||||
if (pushEnabled(mode)) {
|
||||
cursorList.add(contactsDatabase.queryTextSecureContacts(filter));
|
||||
cursorList.add(contactRepository.querySignalContacts(filter));
|
||||
}
|
||||
|
||||
if (pushEnabled(mode) && smsEnabled(mode)) {
|
||||
cursorList.add(contactsDatabase.querySystemContacts(filter));
|
||||
cursorList.add(contactRepository.queryNonSignalContacts(filter));
|
||||
} else if (smsEnabled(mode)) {
|
||||
cursorList.add(filterNonPushContacts(contactsDatabase.querySystemContacts(filter)));
|
||||
cursorList.add(filterNonPushContacts(contactRepository.queryNonSignalContacts(filter)));
|
||||
}
|
||||
return cursorList;
|
||||
}
|
||||
|
@ -203,11 +211,12 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
try (GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(getContext()).getGroupsFilteredByTitle(filter)) {
|
||||
GroupDatabase.GroupRecord groupRecord;
|
||||
while ((groupRecord = reader.getNext()) != null) {
|
||||
groupContacts.addRow(new Object[] { groupRecord.getTitle(),
|
||||
groupContacts.addRow(new Object[] { groupRecord.getRecipientId().serialize(),
|
||||
groupRecord.getTitle(),
|
||||
groupRecord.getEncodedId(),
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"",
|
||||
ContactsDatabase.NORMAL_TYPE });
|
||||
ContactRepository.NORMAL_TYPE });
|
||||
}
|
||||
}
|
||||
return groupContacts;
|
||||
|
@ -215,11 +224,12 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
|
||||
private Cursor getNewNumberCursor() {
|
||||
MatrixCursor newNumberCursor = new MatrixCursor(CONTACT_PROJECTION, 1);
|
||||
newNumberCursor.addRow(new Object[] { getContext().getString(R.string.contact_selection_list__unknown_contact),
|
||||
newNumberCursor.addRow(new Object[] { null,
|
||||
getContext().getString(R.string.contact_selection_list__unknown_contact),
|
||||
filter,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"\u21e2",
|
||||
ContactsDatabase.NEW_TYPE });
|
||||
ContactRepository.NEW_TYPE });
|
||||
return newNumberCursor;
|
||||
}
|
||||
|
||||
|
@ -228,15 +238,16 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
final long startMillis = System.currentTimeMillis();
|
||||
final MatrixCursor matrix = new MatrixCursor(CONTACT_PROJECTION);
|
||||
while (cursor.moveToNext()) {
|
||||
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
|
||||
final Recipient recipient = Recipient.external(getContext(), number);
|
||||
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.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)),
|
||||
number,
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN)),
|
||||
ContactsDatabase.NORMAL_TYPE});
|
||||
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});
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "filterNonPushContacts() -> " + (System.currentTimeMillis() - startMillis) + "ms");
|
||||
|
|
|
@ -17,31 +17,23 @@
|
|||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
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.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
|
@ -64,18 +56,6 @@ public class ContactsDatabase {
|
|||
private static final String CALL_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call";
|
||||
private static final String SYNC = "__TS";
|
||||
|
||||
static final String NAME_COLUMN = "name";
|
||||
static final String NUMBER_COLUMN = "number";
|
||||
static final String NUMBER_TYPE_COLUMN = "number_type";
|
||||
static final String LABEL_COLUMN = "label";
|
||||
static final String CONTACT_TYPE_COLUMN = "contact_type";
|
||||
|
||||
static final int NORMAL_TYPE = 0;
|
||||
static final int PUSH_TYPE = 1;
|
||||
static final int NEW_TYPE = 2;
|
||||
static final int RECENT_TYPE = 3;
|
||||
static final int DIVIDER_TYPE = 4;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public ContactsDatabase(Context context) {
|
||||
|
@ -151,109 +131,6 @@ public class ContactsDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
public @NonNull Cursor querySystemContacts(@Nullable String filter) {
|
||||
Uri uri;
|
||||
|
||||
if (!TextUtils.isEmpty(filter)) {
|
||||
uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(filter));
|
||||
} else {
|
||||
uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
uri = uri.buildUpon().appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build();
|
||||
}
|
||||
|
||||
String[] projection = new String[]{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 LOCALIZED ASC";
|
||||
|
||||
Map<String, String> projectionMap = new HashMap<String, String>() {{
|
||||
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);
|
||||
}};
|
||||
|
||||
String formattedNumber = "REPLACE(REPLACE(REPLACE(REPLACE(data1,' ',''),'-',''),'(',''),')','')";
|
||||
String excludeSelection = "(" + formattedNumber +" NOT IN " +
|
||||
"(SELECT data1 FROM view_data WHERE "+formattedNumber+" = data1) " +
|
||||
"OR "+formattedNumber+" = data1)" +
|
||||
"AND " + formattedNumber + "NOT IN (SELECT "+formattedNumber+" FROM view_data where mimetype = '"+CONTACT_MIMETYPE+"')" ;
|
||||
|
||||
String fallbackSelection = ContactsContract.Data.SYNC2 + " IS NULL OR " + ContactsContract.Data.SYNC2 + " != '" + SYNC + "'";
|
||||
|
||||
Cursor cursor;
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, excludeSelection, null, sort);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
cursor = context.getContentResolver().query(uri, projection, fallbackSelection, null, sort);
|
||||
}
|
||||
|
||||
return new ProjectionMappingCursor(cursor, projectionMap, new Pair<>(CONTACT_TYPE_COLUMN, NORMAL_TYPE));
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
public @NonNull Cursor queryTextSecureContacts(String filter) {
|
||||
String[] projection = new String[] {ContactsContract.Contacts.DISPLAY_NAME,
|
||||
ContactsContract.Data.DATA1};
|
||||
|
||||
String sort = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
|
||||
|
||||
Map<String, String> projectionMap = new HashMap<String, String>(){{
|
||||
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[] {CONTACT_MIMETYPE},
|
||||
sort);
|
||||
} else {
|
||||
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
ContactsContract.Data.MIMETYPE + " = ? AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? OR " + ContactsContract.Data.DATA1 + " LIKE ?)",
|
||||
new String[] {CONTACT_MIMETYPE,
|
||||
"%" + filter + "%", "%" + filter + "%"},
|
||||
sort);
|
||||
|
||||
if (context.getString(R.string.note_to_self).toLowerCase().contains(filter.toLowerCase())) {
|
||||
Optional<SystemContactInfo> self = getSystemContactInfo(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
|
||||
boolean shouldAdd = true;
|
||||
|
||||
if (self.isPresent()) {
|
||||
boolean nameMatch = self.get().name != null && self.get().name.toLowerCase().contains(filter.toLowerCase());
|
||||
boolean numberMatch = self.get().number != null && self.get().number.contains(filter);
|
||||
|
||||
shouldAdd = !nameMatch && !numberMatch;
|
||||
}
|
||||
|
||||
if (shouldAdd) {
|
||||
MatrixCursor selfCursor = new MatrixCursor(projection);
|
||||
selfCursor.addRow(new Object[]{ context.getString(R.string.note_to_self), TextSecurePreferences.getLocalNumber(context)});
|
||||
|
||||
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ProjectionMappingCursor(cursor, projectionMap,
|
||||
new Pair<>(LABEL_COLUMN, "TextSecure"),
|
||||
new Pair<>(NUMBER_TYPE_COLUMN, 0),
|
||||
new Pair<>(CONTACT_TYPE_COLUMN, PUSH_TYPE));
|
||||
|
||||
}
|
||||
|
||||
public @Nullable Cursor getNameDetails(long contactId) {
|
||||
String[] projection = new String[] { ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
|
||||
|
@ -573,105 +450,6 @@ public class ContactsDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
private static class ProjectionMappingCursor extends CursorWrapper {
|
||||
|
||||
private final Map<String, String> projectionMap;
|
||||
private final Pair<String, Object>[] extras;
|
||||
|
||||
@SafeVarargs
|
||||
ProjectionMappingCursor(Cursor cursor,
|
||||
Map<String, String> projectionMap,
|
||||
Pair<String, Object>... extras)
|
||||
{
|
||||
super(cursor);
|
||||
this.projectionMap = projectionMap;
|
||||
this.extras = extras;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return super.getColumnCount() + extras.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String 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 {
|
||||
int index = getColumnIndex(columnName);
|
||||
|
||||
if (index == -1) throw new IllegalArgumentException("Bad column name!");
|
||||
else return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int 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() {
|
||||
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 >= 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;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SystemContactInfo {
|
||||
private final String name;
|
||||
private final String number;
|
||||
|
|
|
@ -75,7 +75,7 @@ public class ContactShareEditActivity extends PassphraseRequiredActionBarActivit
|
|||
ContactShareEditAdapter contactAdapter = new ContactShareEditAdapter(GlideApp.with(this), dynamicLanguage.getCurrentLocale(), this);
|
||||
contactList.setAdapter(contactAdapter);
|
||||
|
||||
ContactRepository contactRepository = new ContactRepository(this,
|
||||
SharedContactRepository contactRepository = new SharedContactRepository(this,
|
||||
AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
DatabaseFactory.getContactsDatabase(this));
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@ class ContactShareEditViewModel extends ViewModel {
|
|||
|
||||
private final MutableLiveData<List<Contact>> contacts;
|
||||
private final SingleLiveEvent<Event> events;
|
||||
private final ContactRepository repo;
|
||||
private final SharedContactRepository repo;
|
||||
|
||||
ContactShareEditViewModel(@NonNull List<Uri> contactUris,
|
||||
@NonNull ContactRepository contactRepository)
|
||||
@NonNull SharedContactRepository contactRepository)
|
||||
{
|
||||
contacts = new MutableLiveData<>();
|
||||
events = new SingleLiveEvent<>();
|
||||
|
@ -98,9 +98,9 @@ class ContactShareEditViewModel extends ViewModel {
|
|||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final List<Uri> contactUris;
|
||||
private final ContactRepository contactRepository;
|
||||
private final SharedContactRepository contactRepository;
|
||||
|
||||
Factory(@NonNull List<Uri> contactUris, @NonNull ContactRepository contactRepository) {
|
||||
Factory(@NonNull List<Uri> contactUris, @NonNull SharedContactRepository contactRepository) {
|
||||
this.contactUris = contactUris;
|
||||
this.contactRepository = contactRepository;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.contactshare.Contact.Email;
|
|||
import org.thoughtcrime.securesms.contactshare.Contact.Name;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact.Phone;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
|
@ -36,17 +35,17 @@ import ezvcard.VCard;
|
|||
|
||||
import static org.thoughtcrime.securesms.contactshare.Contact.*;
|
||||
|
||||
public class ContactRepository {
|
||||
public class SharedContactRepository {
|
||||
|
||||
private static final String TAG = ContactRepository.class.getSimpleName();
|
||||
private static final String TAG = SharedContactRepository.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final Executor executor;
|
||||
private final ContactsDatabase contactsDatabase;
|
||||
|
||||
ContactRepository(@NonNull Context context,
|
||||
@NonNull Executor executor,
|
||||
@NonNull ContactsDatabase contactsDatabase)
|
||||
SharedContactRepository(@NonNull Context context,
|
||||
@NonNull Executor executor,
|
||||
@NonNull ContactsDatabase contactsDatabase)
|
||||
{
|
||||
this.context = context.getApplicationContext();
|
||||
this.executor = executor;
|
|
@ -7,6 +7,7 @@ import android.content.Context;
|
|||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactRepository;
|
||||
import org.thoughtcrime.securesms.database.CursorList;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.search.SearchRepository;
|
||||
|
@ -37,8 +38,8 @@ public class ConversationSearchViewModel extends AndroidViewModel {
|
|||
debouncer = new Debouncer(500);
|
||||
searchRepository = new SearchRepository(context,
|
||||
DatabaseFactory.getSearchDatabase(context),
|
||||
DatabaseFactory.getContactsDatabase(context),
|
||||
DatabaseFactory.getThreadDatabase(context),
|
||||
new ContactRepository(application),
|
||||
ContactAccessor.getInstance(),
|
||||
SignalExecutors.SERIAL);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -36,8 +38,8 @@ public class RecipientDatabase extends Database {
|
|||
static final String TABLE_NAME = "recipient";
|
||||
public static final String ID = "_id";
|
||||
private static final String UUID = "uuid";
|
||||
static final String PHONE = "phone";
|
||||
static final String EMAIL = "email";
|
||||
public static final String PHONE = "phone";
|
||||
public static final String EMAIL = "email";
|
||||
static final String GROUP_ID = "group_id";
|
||||
private static final String BLOCKED = "blocked";
|
||||
private static final String MESSAGE_RINGTONE = "message_ringtone";
|
||||
|
@ -50,22 +52,25 @@ public class RecipientDatabase extends Database {
|
|||
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
|
||||
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
||||
private static final String MESSAGE_EXPIRATION_TIME = "message_expiration_time";
|
||||
static final String REGISTERED = "registered";
|
||||
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
||||
public static final String REGISTERED = "registered";
|
||||
public static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
||||
private static final String SYSTEM_PHOTO_URI = "system_photo_uri";
|
||||
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
||||
public static final String SYSTEM_PHONE_TYPE = "system_phone_type";
|
||||
public static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
||||
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
|
||||
private static final String PROFILE_KEY = "profile_key";
|
||||
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
|
||||
public static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
|
||||
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
||||
private static final String PROFILE_SHARING = "profile_sharing";
|
||||
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
|
||||
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
|
||||
|
||||
private static final String SORT_NAME = "sort_name";
|
||||
|
||||
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
||||
UUID, PHONE, EMAIL, GROUP_ID,
|
||||
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
|
||||
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI,
|
||||
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
|
||||
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
|
||||
UNIDENTIFIED_ACCESS_MODE,
|
||||
FORCE_SMS_SELECTION,
|
||||
|
@ -73,6 +78,8 @@ public class RecipientDatabase extends Database {
|
|||
|
||||
private static final String[] ID_PROJECTION = new String[] { ID };
|
||||
|
||||
public static final String[] SEARCH_PROJECTION = new String[] { ID, SYSTEM_DISPLAY_NAME, SIGNAL_PROFILE_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, "IFNULL(" + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ") AS " + SORT_NAME };
|
||||
|
||||
private static Address addressFromCursor(Cursor cursor) {
|
||||
String phone = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
|
||||
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
|
||||
|
@ -159,6 +166,7 @@ public class RecipientDatabase extends Database {
|
|||
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " +
|
||||
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
|
||||
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
|
||||
SYSTEM_PHONE_TYPE + " INTEGER DEFAULT -1, " +
|
||||
SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " +
|
||||
PROFILE_KEY + " TEXT DEFAULT NULL, " +
|
||||
SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " +
|
||||
|
@ -262,8 +270,7 @@ public class RecipientDatabase extends Database {
|
|||
if (cursor != null && cursor.moveToNext()) {
|
||||
return getRecipientSettings(cursor);
|
||||
} else {
|
||||
// TODO: Maybe not make this an error?
|
||||
throw new AssertionError("Couldn't find recipient!");
|
||||
throw new AssertionError("Couldn't find recipient! id: " + id.serialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,6 +558,66 @@ public class RecipientDatabase extends Database {
|
|||
Stream.of(updates.entrySet()).forEach(entry -> Recipient.live(entry.getKey()).refresh());
|
||||
}
|
||||
}
|
||||
public @Nullable Cursor getSignalContacts() {
|
||||
String selection = BLOCKED + " = ? AND " +
|
||||
REGISTERED + " = ? AND " +
|
||||
GROUP_ID + " IS NULL AND " +
|
||||
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
|
||||
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + SIGNAL_PROFILE_NAME + " NOT NULL)";
|
||||
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1" };
|
||||
String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ", " + PHONE;
|
||||
|
||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||
}
|
||||
|
||||
public @Nullable Cursor querySignalContacts(@NonNull String query) {
|
||||
query = TextUtils.isEmpty(query) ? "*" : query;
|
||||
query = "%" + query + "%";
|
||||
|
||||
String selection = BLOCKED + " = ? AND " +
|
||||
REGISTERED + " = ? AND " +
|
||||
GROUP_ID + " IS NULL AND " +
|
||||
"(" + SYSTEM_DISPLAY_NAME + " NOT NULL OR " + PROFILE_SHARING + " = ?) AND " +
|
||||
"(" +
|
||||
PHONE + " LIKE ? OR " +
|
||||
SYSTEM_DISPLAY_NAME + " LIKE ? OR " +
|
||||
SIGNAL_PROFILE_NAME + " LIKE ?" +
|
||||
")";
|
||||
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), "1", query, query, query };
|
||||
String orderBy = SORT_NAME + ", " + SYSTEM_DISPLAY_NAME + ", " + SIGNAL_PROFILE_NAME + ", " + PHONE;
|
||||
|
||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||
}
|
||||
|
||||
public @Nullable Cursor getNonSignalContacts() {
|
||||
String selection = BLOCKED + " = ? AND " +
|
||||
REGISTERED + " != ? AND " +
|
||||
GROUP_ID + " IS NULL AND " +
|
||||
SYSTEM_DISPLAY_NAME + " NOT NULL AND " +
|
||||
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL)";
|
||||
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()) };
|
||||
String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE;
|
||||
|
||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||
}
|
||||
|
||||
public @Nullable Cursor queryNonSignalContacts(@NonNull String query) {
|
||||
query = TextUtils.isEmpty(query) ? "*" : query;
|
||||
query = "%" + query + "%";
|
||||
|
||||
String selection = BLOCKED + " = ? AND " +
|
||||
REGISTERED + " != ? AND " +
|
||||
GROUP_ID + " IS NULL AND " +
|
||||
"(" + PHONE + " NOT NULL OR " + EMAIL + " NOT NULL) AND " +
|
||||
"(" +
|
||||
PHONE + " LIKE ? OR " +
|
||||
SYSTEM_DISPLAY_NAME + " LIKE ?" +
|
||||
")";
|
||||
String[] args = new String[] { "0", String.valueOf(RegisteredState.REGISTERED.getId()), query, query };
|
||||
String orderBy = SYSTEM_DISPLAY_NAME + ", " + PHONE;
|
||||
|
||||
return databaseHelper.getReadableDatabase().query(TABLE_NAME, SEARCH_PROJECTION, selection, args, null, null, orderBy);
|
||||
}
|
||||
|
||||
private void update(@NonNull RecipientId id, ContentValues contentValues) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
@ -567,11 +634,18 @@ public class RecipientDatabase extends Database {
|
|||
this.database = database;
|
||||
}
|
||||
|
||||
public void setSystemContactInfo(@NonNull RecipientId id, @Nullable String displayName, @Nullable String photoUri, @Nullable String systemPhoneLabel, @Nullable String systemContactUri) {
|
||||
public void setSystemContactInfo(@NonNull RecipientId id,
|
||||
@Nullable String displayName,
|
||||
@Nullable String photoUri,
|
||||
@Nullable String systemPhoneLabel,
|
||||
int systemPhoneType,
|
||||
@Nullable String systemContactUri)
|
||||
{
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(SYSTEM_DISPLAY_NAME, displayName);
|
||||
contentValues.put(SYSTEM_PHOTO_URI, photoUri);
|
||||
contentValues.put(SYSTEM_PHONE_LABEL, systemPhoneLabel);
|
||||
contentValues.put(SYSTEM_PHONE_TYPE, systemPhoneType);
|
||||
contentValues.put(SYSTEM_CONTACT_URI, systemContactUri);
|
||||
|
||||
update(id, contentValues);
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
|
@ -76,6 +77,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
private static final int REVEALABLE_MESSAGES = 22;
|
||||
private static final int VIEW_ONCE_ONLY = 23;
|
||||
private static final int RECIPIENT_IDS = 24;
|
||||
private static final int RECIPIENT_SEARCH = 25;
|
||||
|
||||
private static final int DATABASE_VERSION = 25;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
@ -490,6 +492,27 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
RecipientIdMigrationHelper.execute(db);
|
||||
}
|
||||
|
||||
if (oldVersion < RECIPIENT_SEARCH) {
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN system_phone_type INTEGER DEFAULT -1");
|
||||
|
||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
||||
if (!TextUtils.isEmpty(localNumber)) {
|
||||
try (Cursor cursor = db.query("recipient", null, "phone = ?", new String[] { localNumber }, null, null, null)) {
|
||||
if (cursor == null || !cursor.moveToFirst()) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("phone", localNumber);
|
||||
values.put("registered", 1);
|
||||
values.put("profile_sharing", 1);
|
||||
values.put("signal_profile_name", TextSecurePreferences.getProfileName(context));
|
||||
db.insert("recipient", null, values);
|
||||
} else {
|
||||
db.execSQL("UPDATE recipient SET registered = ?, profile_sharing = ?, signal_profile_name = ? WHERE phone = ?",
|
||||
new String[] { "1", "1", TextSecurePreferences.getProfileName(context), localNumber });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration;
|
|||
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
||||
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
@ -82,6 +83,7 @@ public final class JobManagerFactories {
|
|||
put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory());
|
||||
put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory());
|
||||
put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory());
|
||||
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
|
||||
|
||||
// Dead jobs
|
||||
put("PushContentReceiveJob", new FailingJob.Factory());
|
||||
|
|
|
@ -7,8 +7,7 @@ import android.text.TextUtils;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.contacts.ContactRepository;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
|
@ -32,15 +31,15 @@ class CameraContactsRepository {
|
|||
private final Context context;
|
||||
private final ThreadDatabase threadDatabase;
|
||||
private final GroupDatabase groupDatabase;
|
||||
private final ContactsDatabase contactsDatabase;
|
||||
private final RecipientDatabase recipientDatabase;
|
||||
private final ContactRepository contactRepository;
|
||||
|
||||
CameraContactsRepository(@NonNull Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
this.groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
this.contactsDatabase = DatabaseFactory.getContactsDatabase(context);
|
||||
this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
this.contactRepository = new ContactRepository(context);
|
||||
}
|
||||
|
||||
void getCameraContacts(@NonNull Callback<CameraContacts> callback) {
|
||||
|
@ -80,9 +79,10 @@ class CameraContactsRepository {
|
|||
private @NonNull List<Recipient> getContacts(@NonNull String query) {
|
||||
List<Recipient> recipients = new ArrayList<>();
|
||||
|
||||
try (Cursor cursor = contactsDatabase.queryTextSecureContacts(query)) {
|
||||
try (Cursor cursor = contactRepository.querySignalContacts(query)) {
|
||||
while (cursor.moveToNext()) {
|
||||
Recipient recipient = Recipient.external(context, cursor.getString(1));
|
||||
RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN)));
|
||||
Recipient recipient = Recipient.resolved(id);
|
||||
recipients.add(recipient);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.annimon.stream.Stream;
|
|||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
@ -38,8 +39,9 @@ public class ApplicationMigrations {
|
|||
private static final MutableLiveData<Boolean> UI_BLOCKING_MIGRATION_RUNNING = new MutableLiveData<>();
|
||||
|
||||
private static final class Version {
|
||||
static final int LEGACY = 455;
|
||||
static final int RECIPIENT_ID = 525; // TODO [greyson] USE PROPER APPLICATION VERSION
|
||||
static final int LEGACY = 455;
|
||||
static final int RECIPIENT_ID = 525; // TODO [greyson] USE PROPER APPLICATION VERSION
|
||||
static final int RECIPIENT_SEARCH = 525; // TODO [greyson] USE PROPER APPLICATION VERSION
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,6 +135,10 @@ public class ApplicationMigrations {
|
|||
jobs.add(new DatabaseMigrationJob());
|
||||
}
|
||||
|
||||
if (lastSeenVersion < Version.RECIPIENT_SEARCH) {
|
||||
jobs.add(new RecipientSearchMigrationJob());
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.thoughtcrime.securesms.migrations;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* We added a column for keeping track of the phone number type ("mobile", "home", etc) to the
|
||||
* recipient database, and therefore we need to do a directory sync to fill in that column.
|
||||
*/
|
||||
public class RecipientSearchMigrationJob extends MigrationJob {
|
||||
|
||||
public static final String KEY = "RecipientSearchMigrationJob";
|
||||
|
||||
RecipientSearchMigrationJob() {
|
||||
this(new Job.Parameters.Builder().addConstraint(NetworkConstraint.KEY).build());
|
||||
}
|
||||
|
||||
private RecipientSearchMigrationJob(@NonNull Job.Parameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isUiBlocking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void performMigration() throws Exception {
|
||||
DirectoryHelper.refreshDirectory(context, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldRetry(@NonNull Exception e) {
|
||||
return e instanceof IOException;
|
||||
}
|
||||
|
||||
public static class Factory implements Job.Factory<RecipientSearchMigrationJob> {
|
||||
@Override
|
||||
public @NonNull RecipientSearchMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new RecipientSearchMigrationJob(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -152,7 +152,7 @@ public final class LiveRecipient {
|
|||
private @NonNull RecipientDetails getIndividualRecipientDetails(RecipientSettings settings) {
|
||||
boolean systemContact = !TextUtils.isEmpty(settings.getSystemDisplayName());
|
||||
boolean isLocalNumber = settings.getAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context));
|
||||
return new RecipientDetails(null, Optional.absent(), systemContact, isLocalNumber, settings, null);
|
||||
return new RecipientDetails(context, null, Optional.absent(), systemContact, isLocalNumber, settings, null);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
@ -172,10 +172,10 @@ public final class LiveRecipient {
|
|||
avatarId = Optional.of(groupRecord.get().getAvatarId());
|
||||
}
|
||||
|
||||
return new RecipientDetails(title, avatarId, false, false, settings, members);
|
||||
return new RecipientDetails(context, title, avatarId, false, false, settings, members);
|
||||
}
|
||||
|
||||
return new RecipientDetails(unnamedGroupName, Optional.absent(), false, false, settings, null);
|
||||
return new RecipientDetails(context, unnamedGroupName, Optional.absent(), false, false, settings, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -207,6 +207,20 @@ public class Recipient {
|
|||
return this.name;
|
||||
}
|
||||
|
||||
public @NonNull String getDisplayName() {
|
||||
String name = getName();
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
String profileName = getProfileName();
|
||||
if (!TextUtils.isEmpty(profileName)) {
|
||||
return profileName;
|
||||
}
|
||||
|
||||
return requireAddress().serialize();
|
||||
}
|
||||
|
||||
public @NonNull MaterialColor getColor() {
|
||||
if (isGroup()) return MaterialColor.GROUP;
|
||||
else if (color != null) return color;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.thoughtcrime.securesms.recipients;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -11,6 +12,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
|||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
|
@ -33,7 +35,7 @@ public class RecipientDetails {
|
|||
final VibrateState callVibrateState;
|
||||
final boolean blocked;
|
||||
final int expireMessages;
|
||||
final List<Recipient> participants;
|
||||
final List<Recipient> participants;
|
||||
final String profileName;
|
||||
final boolean seenInviteReminder;
|
||||
final Optional<Integer> defaultSubscriptionId;
|
||||
|
@ -47,7 +49,8 @@ public class RecipientDetails {
|
|||
final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||
final boolean forceSmsSelection;
|
||||
|
||||
RecipientDetails(@Nullable String name,
|
||||
RecipientDetails(@NonNull Context context,
|
||||
@Nullable String name,
|
||||
@NonNull Optional<Long> groupAvatarId,
|
||||
boolean systemContact,
|
||||
boolean isLocalNumber,
|
||||
|
@ -68,7 +71,7 @@ public class RecipientDetails {
|
|||
this.blocked = settings.isBlocked();
|
||||
this.expireMessages = settings.getExpireMessages();
|
||||
this.participants = participants == null ? new LinkedList<>() : participants;
|
||||
this.profileName = settings.getProfileName();
|
||||
this.profileName = isLocalNumber ? TextSecurePreferences.getProfileName(context) : settings.getProfileName();
|
||||
this.seenInviteReminder = settings.hasSeenInviteReminder();
|
||||
this.defaultSubscriptionId = settings.getDefaultSubscriptionId();
|
||||
this.registered = settings.getRegistered();
|
||||
|
|
|
@ -16,6 +16,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactRepository;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -67,8 +68,8 @@ public class SearchFragment extends Fragment implements SearchListAdapter.EventL
|
|||
|
||||
SearchRepository searchRepository = new SearchRepository(getContext(),
|
||||
DatabaseFactory.getSearchDatabase(getContext()),
|
||||
DatabaseFactory.getContactsDatabase(getContext()),
|
||||
DatabaseFactory.getThreadDatabase(getContext()),
|
||||
new ContactRepository(requireContext()),
|
||||
ContactAccessor.getInstance(),
|
||||
SignalExecutors.SERIAL);
|
||||
viewModel = ViewModelProviders.of(this, new SearchViewModel.Factory(searchRepository)).get(SearchViewModel.class);
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.annimon.stream.Stream;
|
|||
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactRepository;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.CursorList;
|
||||
|
@ -56,26 +57,26 @@ public class SearchRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
private final SearchDatabase searchDatabase;
|
||||
private final ContactsDatabase contactsDatabase;
|
||||
private final ThreadDatabase threadDatabase;
|
||||
private final ContactAccessor contactAccessor;
|
||||
private final Executor executor;
|
||||
private final Context context;
|
||||
private final SearchDatabase searchDatabase;
|
||||
private final ContactRepository contactRepository;
|
||||
private final ThreadDatabase threadDatabase;
|
||||
private final ContactAccessor contactAccessor;
|
||||
private final Executor executor;
|
||||
|
||||
public SearchRepository(@NonNull Context context,
|
||||
@NonNull SearchDatabase searchDatabase,
|
||||
@NonNull ContactsDatabase contactsDatabase,
|
||||
@NonNull ThreadDatabase threadDatabase,
|
||||
@NonNull ContactRepository contactRepository,
|
||||
@NonNull ContactAccessor contactAccessor,
|
||||
@NonNull Executor executor)
|
||||
{
|
||||
this.context = context.getApplicationContext();
|
||||
this.searchDatabase = searchDatabase;
|
||||
this.contactsDatabase = contactsDatabase;
|
||||
this.threadDatabase = threadDatabase;
|
||||
this.contactAccessor = contactAccessor;
|
||||
this.executor = executor;
|
||||
this.context = context.getApplicationContext();
|
||||
this.searchDatabase = searchDatabase;
|
||||
this.threadDatabase = threadDatabase;
|
||||
this.contactRepository = contactRepository;
|
||||
this.contactAccessor = contactAccessor;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public void query(@NonNull String query, @NonNull Callback<SearchResult> callback) {
|
||||
|
@ -125,8 +126,8 @@ public class SearchRepository {
|
|||
return CursorList.emptyList();
|
||||
}
|
||||
|
||||
Cursor textSecureContacts = contactsDatabase.queryTextSecureContacts(query);
|
||||
Cursor systemContacts = contactsDatabase.querySystemContacts(query);
|
||||
Cursor textSecureContacts = contactRepository.querySignalContacts(query);
|
||||
Cursor systemContacts = contactRepository.queryNonSignalContacts(query);
|
||||
MergeCursor contacts = new MergeCursor(new Cursor[]{ textSecureContacts, systemContacts });
|
||||
|
||||
return new CursorList<>(contacts, new RecipientModelBuilder(context));
|
||||
|
|
|
@ -205,11 +205,12 @@ public class DirectoryHelper {
|
|||
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
|
||||
String contactPhotoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
|
||||
String contactLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LABEL));
|
||||
int phoneType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.TYPE));
|
||||
Uri contactUri = ContactsContract.Contacts.getLookupUri(cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
|
||||
|
||||
|
||||
handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, contactUri.toString());
|
||||
handle.setSystemContactInfo(recipientId, displayName, contactPhotoUri, contactLabel, phoneType, contactUri.toString());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
|
Loading…
Reference in New Issue