UUID migration.
parent
3dcc2d8171
commit
c60909272b
|
@ -97,7 +97,7 @@ dependencies {
|
||||||
implementation 'org.conscrypt:conscrypt-android:2.0.0'
|
implementation 'org.conscrypt:conscrypt-android:2.0.0'
|
||||||
implementation 'org.signal:aesgcmprovider:0.0.3'
|
implementation 'org.signal:aesgcmprovider:0.0.3'
|
||||||
|
|
||||||
implementation 'org.whispersystems:signal-service-android:2.13.9'
|
implementation 'org.whispersystems:signal-service-android:2.14.0'
|
||||||
|
|
||||||
implementation 'org.signal:ringrtc-android:0.1.7.2'
|
implementation 'org.signal:ringrtc-android:0.1.7.2'
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.annotation.Nullable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
import org.thoughtcrime.securesms.util.VerifySpan;
|
||||||
|
@ -95,7 +96,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... params) {
|
protected Void doInBackground(Void... params) {
|
||||||
synchronized (SESSION_LOCK) {
|
synchronized (SESSION_LOCK) {
|
||||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireAddress().toPhoneString(), 1);
|
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
|
||||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
||||||
|
|
||||||
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
|
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
|
||||||
|
@ -167,7 +168,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||||
boolean legacy = !messageRecord.isContentBundleKeyExchange();
|
boolean legacy = !messageRecord.isContentBundleKeyExchange();
|
||||||
|
|
||||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
||||||
messageRecord.getIndividualRecipient().requireAddress().toPhoneString(),
|
RecipientUtil.toSignalServiceAddress(getContext(), messageRecord.getIndividualRecipient()),
|
||||||
messageRecord.getRecipientDeviceId(),
|
messageRecord.getRecipientDeviceId(),
|
||||||
messageRecord.getDateSent(),
|
messageRecord.getDateSent(),
|
||||||
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||||
|
|
|
@ -205,7 +205,7 @@ public class ConversationListItem extends RelativeLayout
|
||||||
|
|
||||||
fromView.setText(contact);
|
fromView.setText(contact);
|
||||||
fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), new SpannableString(fromView.getText()), highlightSubstring));
|
fromView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), new SpannableString(fromView.getText()), highlightSubstring));
|
||||||
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.requireAddress().toString(), highlightSubstring));
|
subjectView.setText(SearchUtil.getHighlightedSpan(locale, () -> new StyleSpan(Typeface.BOLD), contact.getE164().or(""), highlightSubstring));
|
||||||
dateView.setText("");
|
dateView.setText("");
|
||||||
archivedView.setVisibility(GONE);
|
archivedView.setVisibility(GONE);
|
||||||
unreadIndicator.setVisibility(GONE);
|
unreadIndicator.setVisibility(GONE);
|
||||||
|
|
|
@ -36,9 +36,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
|
||||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
|
@ -20,6 +20,7 @@ import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||||
|
@ -177,7 +178,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Context context = DeviceActivity.this;
|
Context context = DeviceActivity.this;
|
||||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
|
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||||
String verificationCode = accountManager.getNewDeviceVerificationCode();
|
String verificationCode = accountManager.getNewDeviceVerificationCode();
|
||||||
String ephemeralId = uri.getQueryParameter("uuid");
|
String ephemeralId = uri.getQueryParameter("uuid");
|
||||||
String publicKeyEncoded = uri.getQueryParameter("pub_key");
|
String publicKeyEncoded = uri.getQueryParameter("pub_key");
|
||||||
|
|
|
@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||||
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
|
@ -93,7 +92,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
||||||
|
|
||||||
public static final String GROUP_ADDRESS_EXTRA = "group_recipient";
|
public static final String GROUP_ID_EXTRA = "group_id";
|
||||||
public static final String GROUP_THREAD_EXTRA = "group_thread";
|
public static final String GROUP_THREAD_EXTRA = "group_thread";
|
||||||
|
|
||||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||||
|
@ -203,10 +202,10 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeExistingGroup() {
|
private void initializeExistingGroup() {
|
||||||
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
|
final String groupId = getIntent().getStringExtra(GROUP_ID_EXTRA);
|
||||||
|
|
||||||
if (groupAddress != null) {
|
if (groupId != null) {
|
||||||
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupAddress.toGroupString());
|
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +457,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||||
if (!activity.isFinishing()) {
|
if (!activity.isFinishing()) {
|
||||||
Intent intent = activity.getIntent();
|
Intent intent = activity.getIntent();
|
||||||
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
|
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
|
||||||
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().requireAddress());
|
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId());
|
||||||
activity.setResult(RESULT_OK, intent);
|
activity.setResult(RESULT_OK, intent);
|
||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
|
@ -501,7 +500,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||||
if (failIfNotPush && !isPush) {
|
if (failIfNotPush && !isPush) {
|
||||||
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
|
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
|
||||||
recipient.toShortString())));
|
recipient.toShortString())));
|
||||||
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.requireAddress().serialize())) {
|
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getE164().or(""))) {
|
||||||
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
|
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
|
||||||
} else {
|
} else {
|
||||||
results.add(new Result(recipient, isPush, null));
|
results.add(new Result(recipient, isPush, null));
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Recipient> doInBackground(Void... params) {
|
protected List<Recipient> doInBackground(Void... params) {
|
||||||
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireAddress().toGroupString(), true);
|
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -90,7 +90,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||||
|
|
||||||
public GroupMembers(List<Recipient> recipients) {
|
public GroupMembers(List<Recipient> recipients) {
|
||||||
for (Recipient recipient : recipients) {
|
for (Recipient recipient : recipients) {
|
||||||
if (isLocalNumber(recipient)) {
|
if (recipient.isLocalNumber()) {
|
||||||
members.push(recipient);
|
members.push(recipient);
|
||||||
} else {
|
} else {
|
||||||
members.add(recipient);
|
members.add(recipient);
|
||||||
|
@ -102,7 +102,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||||
List<String> recipientStrings = new LinkedList<>();
|
List<String> recipientStrings = new LinkedList<>();
|
||||||
|
|
||||||
for (Recipient recipient : members) {
|
for (Recipient recipient : members) {
|
||||||
if (isLocalNumber(recipient)) {
|
if (recipient.isLocalNumber()) {
|
||||||
recipientStrings.add(context.getString(R.string.GroupMembersDialog_me));
|
recipientStrings.add(context.getString(R.string.GroupMembersDialog_me));
|
||||||
} else {
|
} else {
|
||||||
String name = recipient.toShortString();
|
String name = recipient.toShortString();
|
||||||
|
@ -121,9 +121,5 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||||
public Recipient get(int index) {
|
public Recipient get(int index) {
|
||||||
return members.get(index);
|
return members.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLocalNumber(Recipient recipient) {
|
|
||||||
return Util.isOwnNumber(context, recipient.requireAddress());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||||
|
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@ -81,12 +82,7 @@ public class IncomingMessageProcessor {
|
||||||
*/
|
*/
|
||||||
public @Nullable String processEnvelope(@NonNull SignalServiceEnvelope envelope) {
|
public @Nullable String processEnvelope(@NonNull SignalServiceEnvelope envelope) {
|
||||||
if (envelope.hasSource()) {
|
if (envelope.hasSource()) {
|
||||||
Recipient recipient = Recipient.external(context, envelope.getSource());
|
Recipient.externalPush(context, envelope.getSourceAddress());
|
||||||
|
|
||||||
if (!isActiveNumber(recipient)) {
|
|
||||||
recipientDatabase.setRegistered(recipient.getId(), RecipientDatabase.RegisteredState.REGISTERED);
|
|
||||||
jobManager.add(new DirectoryRefreshJob(recipient, false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (envelope.isReceipt()) {
|
if (envelope.isReceipt()) {
|
||||||
|
@ -113,14 +109,10 @@ public class IncomingMessageProcessor {
|
||||||
|
|
||||||
private void processReceipt(@NonNull SignalServiceEnvelope envelope) {
|
private void processReceipt(@NonNull SignalServiceEnvelope envelope) {
|
||||||
Log.i(TAG, String.format(Locale.ENGLISH, "Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
|
Log.i(TAG, String.format(Locale.ENGLISH, "Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
|
||||||
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.external(context, envelope.getSource()).getId(), envelope.getTimestamp()),
|
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.externalPush(context, envelope.getSourceAddress()).getId(), envelope.getTimestamp()),
|
||||||
System.currentTimeMillis());
|
System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isActiveNumber(@NonNull Recipient recipient) {
|
|
||||||
return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
release();
|
release();
|
||||||
|
|
|
@ -52,7 +52,6 @@ import androidx.viewpager.widget.ViewPager;
|
||||||
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
|
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
|
||||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||||
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
|
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||||
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
||||||
|
|
|
@ -368,7 +368,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||||
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
|
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
|
||||||
|
|
||||||
if (receiptInfoList.isEmpty()) {
|
if (receiptInfoList.isEmpty()) {
|
||||||
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireAddress().toGroupString(), false);
|
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireGroupId(), false);
|
||||||
|
|
||||||
for (Recipient recipient : group) {
|
for (Recipient recipient : group) {
|
||||||
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
|
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
|
||||||
|
|
|
@ -11,7 +11,9 @@ import android.widget.BaseAdapter;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
|
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
@ -19,11 +21,12 @@ import java.util.List;
|
||||||
|
|
||||||
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
|
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final GlideRequests glideRequests;
|
private final GlideRequests glideRequests;
|
||||||
private final MessageRecord record;
|
private final MessageRecord record;
|
||||||
private final List<RecipientDeliveryStatus> members;
|
private final List<RecipientDeliveryStatus> members;
|
||||||
private final boolean isPushGroup;
|
private final boolean isPushGroup;
|
||||||
|
private final StableIdGenerator<RecipientId> idGenerator;
|
||||||
|
|
||||||
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
||||||
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
|
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
|
||||||
|
@ -34,6 +37,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
|
||||||
this.record = record;
|
this.record = record;
|
||||||
this.isPushGroup = isPushGroup;
|
this.isPushGroup = isPushGroup;
|
||||||
this.members = members;
|
this.members = members;
|
||||||
|
this.idGenerator = new StableIdGenerator<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,11 +52,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
try {
|
return idGenerator.getId(members.get(position).recipient.getId());
|
||||||
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.requireAddress().serialize().getBytes()));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -57,7 +57,6 @@ import org.thoughtcrime.securesms.color.MaterialColor;
|
||||||
import org.thoughtcrime.securesms.color.MaterialColors;
|
import org.thoughtcrime.securesms.color.MaterialColors;
|
||||||
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
|
@ -421,10 +420,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||||
if (aboutDivider != null) aboutDivider.setVisible(false);
|
if (aboutDivider != null) aboutDivider.setVisible(false);
|
||||||
if (divider != null) divider.setVisible(false);
|
if (divider != null) divider.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity()));
|
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(requireActivity()));
|
||||||
colorPreference.setColor(recipient.getColor().toActionBarColor(getActivity()));
|
colorPreference.setColor(recipient.getColor().toActionBarColor(requireActivity()));
|
||||||
|
|
||||||
aboutPreference.setTitle(formatAddress(recipient.requireAddress()));
|
aboutPreference.setTitle(formatRecipient(recipient));
|
||||||
aboutPreference.setSummary(recipient.getCustomLabel());
|
aboutPreference.setSummary(recipient.getCustomLabel());
|
||||||
aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED);
|
aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED);
|
||||||
|
|
||||||
|
@ -453,10 +452,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull String formatAddress(@NonNull Address address) {
|
private @NonNull String formatRecipient(@NonNull Recipient recipient) {
|
||||||
if (address.isPhone()) return PhoneNumberUtils.formatNumber(address.toPhoneString());
|
if (recipient.getE164().isPresent()) return PhoneNumberUtils.formatNumber(recipient.requireE164());
|
||||||
else if (address.isEmail()) return address.toEmailString();
|
else if (recipient.getEmail().isPresent()) return recipient.requireEmail();
|
||||||
else return "";
|
else return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) {
|
private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) {
|
||||||
|
@ -697,7 +696,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||||
if (recipient.get().isGroup()) {
|
if (recipient.get().isGroup()) {
|
||||||
bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description;
|
bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description;
|
||||||
|
|
||||||
if (recipient.get().isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.get().requireAddress().toGroupString())) {
|
if (recipient.get().isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.get().requireGroupId())) {
|
||||||
titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group;
|
titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group;
|
||||||
} else {
|
} else {
|
||||||
titleRes = R.string.RecipientPreferenceActivity_block_group;
|
titleRes = R.string.RecipientPreferenceActivity_block_group;
|
||||||
|
@ -745,7 +744,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||||
DatabaseFactory.getRecipientDatabase(context)
|
DatabaseFactory.getRecipientDatabase(context)
|
||||||
.setBlocked(recipient.getId(), blocked);
|
.setBlocked(recipient.getId(), blocked);
|
||||||
|
|
||||||
if (recipient.isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireAddress().toGroupString())) {
|
if (recipient.isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireGroupId())) {
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient);
|
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient);
|
||||||
|
|
||||||
|
@ -753,7 +752,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||||
MessageSender.send(context, leaveMessage.get(), threadId, false, null);
|
MessageSender.send(context, leaveMessage.get(), threadId, false, null);
|
||||||
|
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
String groupId = recipient.requireAddress().toGroupString();
|
String groupId = recipient.requireGroupId();
|
||||||
groupDatabase.setActive(groupId, false);
|
groupDatabase.setActive(groupId, false);
|
||||||
groupDatabase.remove(groupId, Recipient.self().getId());
|
groupDatabase.remove(groupId, Recipient.self().getId());
|
||||||
} else {
|
} else {
|
||||||
|
@ -790,7 +789,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||||
public void onInSecureCallClicked() {
|
public void onInSecureCallClicked() {
|
||||||
try {
|
try {
|
||||||
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
|
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
|
||||||
Uri.parse("tel:" + recipient.get().requireAddress().serialize()));
|
Uri.parse("tel:" + recipient.get().requireE164()));
|
||||||
startActivity(dialIntent);
|
startActivity(dialIntent);
|
||||||
} catch (ActivityNotFoundException anfe) {
|
} catch (ActivityNotFoundException anfe) {
|
||||||
Log.w(TAG, anfe);
|
Log.w(TAG, anfe);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.Rfc5724Uri;
|
import org.thoughtcrime.securesms.util.Rfc5724Uri;
|
||||||
|
|
|
@ -269,12 +269,16 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
|
||||||
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
|
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
|
||||||
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
|
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
|
||||||
|
|
||||||
this.localNumber = getArguments().getString(LOCAL_NUMBER);
|
this.localNumber = TextSecurePreferences.getLocalNumber(requireContext());
|
||||||
this.localIdentity = localIdentityParcelable.get();
|
this.localIdentity = localIdentityParcelable.get();
|
||||||
this.recipient = Recipient.live(recipientId);
|
this.recipient = Recipient.live(recipientId);
|
||||||
this.remoteNumber = recipient.get().requireAddress().serialize();
|
this.remoteNumber = recipient.resolve().getE164().or("");
|
||||||
this.remoteIdentity = remoteIdentityParcelable.get();
|
this.remoteIdentity = remoteIdentityParcelable.get();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(remoteNumber)) {
|
||||||
|
Log.w(TAG, "Empty remote number! Did we get a UUID-only message somehow?");
|
||||||
|
}
|
||||||
|
|
||||||
this.recipient.observe(this, this::setRecipientText);
|
this.recipient.observe(this, this::setRecipientText);
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Fingerprint>() {
|
new AsyncTask<Void, Void, Fingerprint>() {
|
||||||
|
@ -573,7 +577,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
|
||||||
protected Void doInBackground(Recipient... params) {
|
protected Void doInBackground(Recipient... params) {
|
||||||
synchronized (SESSION_LOCK) {
|
synchronized (SESSION_LOCK) {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
Log.i(TAG, "Saving identity: " + params[0].requireAddress());
|
Log.i(TAG, "Saving identity: " + params[0].getId());
|
||||||
DatabaseFactory.getIdentityDatabase(getActivity())
|
DatabaseFactory.getIdentityDatabase(getActivity())
|
||||||
.saveIdentity(params[0].getId(),
|
.saveIdentity(params[0].getId(),
|
||||||
remoteIdentity,
|
remoteIdentity,
|
||||||
|
|
|
@ -281,7 +281,7 @@ public class WebRtcCallActivity extends Activity {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
synchronized (SESSION_LOCK) {
|
synchronized (SESSION_LOCK) {
|
||||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this);
|
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(WebRtcCallActivity.this);
|
||||||
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.requireAddress().serialize(), 1), theirIdentity, true);
|
identityKeyStore.saveIdentity(new SignalProtocolAddress(recipient.requireServiceId(), 1), theirIdentity, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
|
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
|
||||||
|
|
|
@ -20,12 +20,10 @@ import org.thoughtcrime.securesms.backup.BackupProtos.SqlStatement;
|
||||||
import org.thoughtcrime.securesms.backup.BackupProtos.Sticker;
|
import org.thoughtcrime.securesms.backup.BackupProtos.Sticker;
|
||||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
|
import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SearchDatabase;
|
import org.thoughtcrime.securesms.database.SearchDatabase;
|
||||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
|
@ -31,7 +31,6 @@ import com.annimon.stream.Stream;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
|
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
|
||||||
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||||
|
|
||||||
|
|
|
@ -187,11 +187,10 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setQuoteAuthor(@NonNull Recipient author) {
|
private void setQuoteAuthor(@NonNull Recipient author) {
|
||||||
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
|
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
|
||||||
boolean isOwnNumber = Util.isOwnNumber(getContext(), author.requireAddress());
|
|
||||||
|
|
||||||
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you)
|
authorView.setText(author.isLocalNumber() ? getContext().getString(R.string.QuoteView_you)
|
||||||
: author.toShortString());
|
: author.toShortString());
|
||||||
|
|
||||||
// We use the raw color resource because Android 4.x was struggling with tints here
|
// We use the raw color resource because Android 4.x was struggling with tints here
|
||||||
quoteBarView.setImageResource(author.getColor().toQuoteBarColorResource(getContext(), outgoing));
|
quoteBarView.setImageResource(author.getColor().toQuoteBarColorResource(getContext(), outgoing));
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class TypingStatusRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void onTypingStarted(@NonNull Context context, long threadId, @NonNull Recipient author, int device) {
|
public synchronized void onTypingStarted(@NonNull Context context, long threadId, @NonNull Recipient author, int device) {
|
||||||
if (author.requireAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
|
if (author.isLocalNumber()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ public class TypingStatusRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void onTypingStopped(@NonNull Context context, long threadId, @NonNull Recipient author, int device, boolean isReplacedByIncomingMessage) {
|
public synchronized void onTypingStopped(@NonNull Context context, long threadId, @NonNull Recipient author, int device, boolean isReplacedByIncomingMessage) {
|
||||||
if (author.requireAddress().serialize().equals(TextSecurePreferences.getLocalNumber(context))) {
|
if (author.isLocalNumber()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -308,9 +308,10 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
|
||||||
this.name.setText(recipient.getName());
|
this.name.setText(recipient.getName());
|
||||||
|
|
||||||
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||||
this.phoneNumber.setText(recipient.requireAddress().serialize() + " (~" + recipient.getProfileName() + ")");
|
// TODO [greyson] This will need to change in UI PR?
|
||||||
|
this.phoneNumber.setText(recipient.requireE164() + " (~" + recipient.getProfileName() + ")");
|
||||||
} else {
|
} else {
|
||||||
this.phoneNumber.setText(recipient.requireAddress().serialize());
|
this.phoneNumber.setText(recipient.requireE164());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ import android.text.TextUtils;
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
|
@ -70,13 +69,13 @@ public class ContactAccessor {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Address> getAllContactsWithNumbers(Context context) {
|
public Set<String> getAllContactsWithNumbers(Context context) {
|
||||||
Set<Address> results = new HashSet<>();
|
Set<String> results = new HashSet<>();
|
||||||
|
|
||||||
try (Cursor cursor = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER}, null ,null, null)) {
|
try (Cursor cursor = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER}, null ,null, null)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
if (!TextUtils.isEmpty(cursor.getString(0))) {
|
if (!TextUtils.isEmpty(cursor.getString(0))) {
|
||||||
results.add(Address.fromSerialized(PhoneNumberFormatter.get(context).format(cursor.getString(0))));
|
results.add(PhoneNumberFormatter.get(context).format(cursor.getString(0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,17 +108,21 @@ public class ContactAccessor {
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
||||||
|
|
||||||
final List<Address> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered()).map(Recipient::resolved).map(Recipient::requireAddress).toList();
|
final List<String> registeredAddresses = Stream.of(DatabaseFactory.getRecipientDatabase(context).getRegistered())
|
||||||
|
.map(Recipient::resolved)
|
||||||
|
.filter(r -> r.getE164().isPresent())
|
||||||
|
.map(Recipient::requireE164)
|
||||||
|
.toList();
|
||||||
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
|
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
|
||||||
|
|
||||||
for (Address registeredAddress : registeredAddresses) {
|
for (String registeredAddress : registeredAddresses) {
|
||||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress.serialize()));
|
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress));
|
||||||
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (lookupCursor != null && lookupCursor.moveToFirst()) {
|
if (lookupCursor != null && lookupCursor.moveToFirst()) {
|
||||||
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
|
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
|
||||||
contactData.numbers.add(new NumberData("TextSecure", registeredAddress.serialize()));
|
contactData.numbers.add(new NumberData("TextSecure", registeredAddress));
|
||||||
lookupData.add(contactData);
|
lookupData.add(contactData);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -88,12 +88,12 @@ public class ContactRepository {
|
||||||
if (noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) {
|
if (noteToSelfTitle.toLowerCase().contains(query.toLowerCase())) {
|
||||||
Recipient self = Recipient.self();
|
Recipient self = Recipient.self();
|
||||||
boolean nameMatch = self.getDisplayName().toLowerCase().contains(query.toLowerCase());
|
boolean nameMatch = self.getDisplayName().toLowerCase().contains(query.toLowerCase());
|
||||||
boolean numberMatch = self.requireAddress().serialize() != null && self.requireAddress().serialize().contains(query);
|
boolean numberMatch = self.getE164().isPresent() && self.requireE164().contains(query);
|
||||||
boolean shouldAdd = !nameMatch && !numberMatch;
|
boolean shouldAdd = !nameMatch && !numberMatch;
|
||||||
|
|
||||||
if (shouldAdd) {
|
if (shouldAdd) {
|
||||||
MatrixCursor selfCursor = new MatrixCursor(RecipientDatabase.SEARCH_PROJECTION);
|
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 });
|
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, null, self.getE164().or(""), self.getEmail().orNull(), null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), noteToSelfTitle });
|
||||||
|
|
||||||
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
|
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,12 @@ import android.widget.TextView;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.components.FromTextView;
|
import org.thoughtcrime.securesms.components.FromTextView;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
|
|
||||||
public class ContactSelectionListItem extends LinearLayout implements RecipientForeverObserver {
|
public class ContactSelectionListItem extends LinearLayout implements RecipientForeverObserver {
|
||||||
|
|
|
@ -183,7 +183,7 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||||
while ((threadRecord = reader.getNext()) != null) {
|
while ((threadRecord = reader.getNext()) != null) {
|
||||||
recentConversations.addRow(new Object[] { threadRecord.getRecipient().getId().serialize(),
|
recentConversations.addRow(new Object[] { threadRecord.getRecipient().getId().serialize(),
|
||||||
threadRecord.getRecipient().toShortString(),
|
threadRecord.getRecipient().toShortString(),
|
||||||
threadRecord.getRecipient().requireAddress().serialize(),
|
threadRecord.getRecipient().requireStringId(),
|
||||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||||
"",
|
"",
|
||||||
ContactRepository.RECENT_TYPE });
|
ContactRepository.RECENT_TYPE });
|
||||||
|
|
|
@ -31,7 +31,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
@ -82,17 +81,17 @@ public class ContactsDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setRegisteredUsers(@NonNull Account account,
|
public synchronized void setRegisteredUsers(@NonNull Account account,
|
||||||
@NonNull List<Address> registeredAddressList,
|
@NonNull List<String> registeredAddressList,
|
||||||
boolean remove)
|
boolean remove)
|
||||||
throws RemoteException, OperationApplicationException
|
throws RemoteException, OperationApplicationException
|
||||||
{
|
{
|
||||||
Set<Address> registeredAddressSet = new HashSet<>(registeredAddressList);
|
Set<String> registeredAddressSet = new HashSet<>(registeredAddressList);
|
||||||
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
|
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
|
||||||
Map<Address, SignalContact> currentContacts = getSignalRawContacts(account);
|
Map<String, SignalContact> currentContacts = getSignalRawContacts(account);
|
||||||
List<List<Address>> registeredChunks = Util.chunk(registeredAddressList, 50);
|
List<List<String>> registeredChunks = Util.chunk(registeredAddressList, 50);
|
||||||
|
|
||||||
for (List<Address> registeredChunk : registeredChunks) {
|
for (List<String> registeredChunk : registeredChunks) {
|
||||||
for (Address registeredAddress : registeredChunk) {
|
for (String registeredAddress : registeredChunk) {
|
||||||
if (!currentContacts.containsKey(registeredAddress)) {
|
if (!currentContacts.containsKey(registeredAddress)) {
|
||||||
Optional<SystemContactInfo> systemContactInfo = getSystemContactInfo(registeredAddress);
|
Optional<SystemContactInfo> systemContactInfo = getSystemContactInfo(registeredAddress);
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ public class ContactsDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Map.Entry<Address, SignalContact> currentContactEntry : currentContacts.entrySet()) {
|
for (Map.Entry<String, SignalContact> currentContactEntry : currentContacts.entrySet()) {
|
||||||
if (!registeredAddressSet.contains(currentContactEntry.getKey())) {
|
if (!registeredAddressSet.contains(currentContactEntry.getKey())) {
|
||||||
if (remove) {
|
if (remove) {
|
||||||
Log.i(TAG, "Removing number: " + currentContactEntry.getKey());
|
Log.i(TAG, "Removing number: " + currentContactEntry.getKey());
|
||||||
|
@ -240,7 +239,7 @@ public class ContactsDatabase {
|
||||||
|
|
||||||
|
|
||||||
private void addContactVoiceSupport(List<ContentProviderOperation> operations,
|
private void addContactVoiceSupport(List<ContentProviderOperation> operations,
|
||||||
@NonNull Address address, long rawContactId)
|
@NonNull String address, long rawContactId)
|
||||||
{
|
{
|
||||||
operations.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI)
|
operations.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI)
|
||||||
.withSelection(RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)})
|
.withSelection(RawContacts._ID + " = ?", new String[] {String.valueOf(rawContactId)})
|
||||||
|
@ -250,9 +249,9 @@ public class ContactsDatabase {
|
||||||
operations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build())
|
operations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build())
|
||||||
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
|
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
|
||||||
.withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE)
|
.withValue(ContactsContract.Data.MIMETYPE, CALL_MIMETYPE)
|
||||||
.withValue(ContactsContract.Data.DATA1, address.toPhoneString())
|
.withValue(ContactsContract.Data.DATA1, address)
|
||||||
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
|
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
|
||||||
.withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address.toPhoneString()))
|
.withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_signal_call_s, address))
|
||||||
.withYieldAllowed(true)
|
.withYieldAllowed(true)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
@ -348,13 +347,13 @@ public class ContactsDatabase {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull Map<Address, SignalContact> getSignalRawContacts(@NonNull Account account) {
|
private @NonNull Map<String, SignalContact> getSignalRawContacts(@NonNull Account account) {
|
||||||
Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon()
|
Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon()
|
||||||
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
|
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
|
||||||
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build();
|
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build();
|
||||||
|
|
||||||
Map<Address, SignalContact> signalContacts = new HashMap<>();
|
Map<String, SignalContact> signalContacts = new HashMap<>();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1, RawContacts.SYNC4, RawContacts.CONTACT_ID, RawContacts.DISPLAY_NAME_PRIMARY, RawContacts.DISPLAY_NAME_SOURCE};
|
String[] projection = new String[] {BaseColumns._ID, RawContacts.SYNC1, RawContacts.SYNC4, RawContacts.CONTACT_ID, RawContacts.DISPLAY_NAME_PRIMARY, RawContacts.DISPLAY_NAME_SOURCE};
|
||||||
|
@ -362,7 +361,7 @@ public class ContactsDatabase {
|
||||||
cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null);
|
cursor = context.getContentResolver().query(currentContactsUri, projection, null, null, null);
|
||||||
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
Address currentAddress = Address.fromSerialized(PhoneNumberFormatter.get(context).format(cursor.getString(1)));
|
String currentAddress = PhoneNumberFormatter.get(context).format(cursor.getString(1));
|
||||||
long rawContactId = cursor.getLong(0);
|
long rawContactId = cursor.getLong(0);
|
||||||
long contactId = cursor.getLong(3);
|
long contactId = cursor.getLong(3);
|
||||||
String supportsVoice = cursor.getString(2);
|
String supportsVoice = cursor.getString(2);
|
||||||
|
@ -380,11 +379,9 @@ public class ContactsDatabase {
|
||||||
return signalContacts;
|
return signalContacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<SystemContactInfo> getSystemContactInfo(@NonNull Address address)
|
private Optional<SystemContactInfo> getSystemContactInfo(@NonNull String address)
|
||||||
{
|
{
|
||||||
if (!address.isPhone()) return Optional.absent();
|
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address));
|
||||||
|
|
||||||
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString()));
|
|
||||||
String[] projection = {ContactsContract.PhoneLookup.NUMBER,
|
String[] projection = {ContactsContract.PhoneLookup.NUMBER,
|
||||||
ContactsContract.PhoneLookup._ID,
|
ContactsContract.PhoneLookup._ID,
|
||||||
ContactsContract.PhoneLookup.DISPLAY_NAME};
|
ContactsContract.PhoneLookup.DISPLAY_NAME};
|
||||||
|
@ -395,8 +392,8 @@ public class ContactsDatabase {
|
||||||
numberCursor = context.getContentResolver().query(uri, projection, null, null, null);
|
numberCursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||||
|
|
||||||
while (numberCursor != null && numberCursor.moveToNext()) {
|
while (numberCursor != null && numberCursor.moveToNext()) {
|
||||||
String systemNumber = numberCursor.getString(0);
|
String systemNumber = numberCursor.getString(0);
|
||||||
Address systemAddress = Address.fromSerialized(PhoneNumberFormatter.get(context).format(systemNumber));
|
String systemAddress = PhoneNumberFormatter.get(context).format(systemNumber);
|
||||||
|
|
||||||
if (systemAddress.equals(address)) {
|
if (systemAddress.equals(address)) {
|
||||||
idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI,
|
idCursor = context.getContentResolver().query(RawContacts.CONTENT_URI,
|
||||||
|
|
|
@ -187,7 +187,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
|
||||||
|
|
||||||
public static CharSequence contactToToken(Recipient c) {
|
public static CharSequence contactToToken(Recipient c) {
|
||||||
String name = c.getName();
|
String name = c.getName();
|
||||||
String number = c.requireAddress().serialize();
|
String number = c.getE164().or(c.getEmail()).or("");
|
||||||
SpannableString s = new SpannableString(RecipientsFormatter.formatNameAndNumber(name, number));
|
SpannableString s = new SpannableString(RecipientsFormatter.formatNameAndNumber(name, number));
|
||||||
int len = s.length();
|
int len = s.length();
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ public class RecipientsEditor extends AppCompatMultiAutoCompleteTextView {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.setSpan(new Annotation("number", c.requireAddress().serialize()), 0, len,
|
s.setSpan(new Annotation("number", number), 0, len,
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
|
@ -19,24 +18,24 @@ import java.security.MessageDigest;
|
||||||
|
|
||||||
public class GroupRecordContactPhoto implements ContactPhoto {
|
public class GroupRecordContactPhoto implements ContactPhoto {
|
||||||
|
|
||||||
private final @NonNull Address address;
|
private final String groupId;
|
||||||
private final long avatarId;
|
private final long avatarId;
|
||||||
|
|
||||||
public GroupRecordContactPhoto(@NonNull Address address, long avatarId) {
|
public GroupRecordContactPhoto(@NonNull String groupId, long avatarId) {
|
||||||
this.address = address;
|
this.groupId = groupId;
|
||||||
this.avatarId = avatarId;
|
this.avatarId = avatarId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInputStream(Context context) throws IOException {
|
public InputStream openInputStream(Context context) throws IOException {
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(address.toGroupString());
|
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(groupId);
|
||||||
|
|
||||||
if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) {
|
if (groupRecord.isPresent() && groupRecord.get().getAvatar() != null) {
|
||||||
return new ByteArrayInputStream(groupRecord.get().getAvatar());
|
return new ByteArrayInputStream(groupRecord.get().getAvatar());
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IOException("Couldn't load avatar for group: " + address.toGroupString());
|
throw new IOException("Couldn't load avatar for group: " + groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,7 +50,7 @@ public class GroupRecordContactPhoto implements ContactPhoto {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||||
messageDigest.update(address.serialize().getBytes());
|
messageDigest.update(groupId.getBytes());
|
||||||
messageDigest.update(Conversions.longToByteArray(avatarId));
|
messageDigest.update(Conversions.longToByteArray(avatarId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +59,11 @@ public class GroupRecordContactPhoto implements ContactPhoto {
|
||||||
if (other == null || !(other instanceof GroupRecordContactPhoto)) return false;
|
if (other == null || !(other instanceof GroupRecordContactPhoto)) return false;
|
||||||
|
|
||||||
GroupRecordContactPhoto that = (GroupRecordContactPhoto)other;
|
GroupRecordContactPhoto that = (GroupRecordContactPhoto)other;
|
||||||
return this.address.equals(that.address) && this.avatarId == that.avatarId;
|
return this.groupId.equals(that.groupId) && this.avatarId == that.avatarId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return this.address.hashCode() ^ (int) avatarId;
|
return this.groupId.hashCode() ^ (int) avatarId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -15,12 +15,12 @@ import java.security.MessageDigest;
|
||||||
|
|
||||||
public class SystemContactPhoto implements ContactPhoto {
|
public class SystemContactPhoto implements ContactPhoto {
|
||||||
|
|
||||||
private final @NonNull Address address;
|
private final RecipientId recipientId;
|
||||||
private final @NonNull Uri contactPhotoUri;
|
private final Uri contactPhotoUri;
|
||||||
private final long lastModifiedTime;
|
private final long lastModifiedTime;
|
||||||
|
|
||||||
public SystemContactPhoto(@NonNull Address address, @NonNull Uri contactPhotoUri, long lastModifiedTime) {
|
public SystemContactPhoto(@NonNull RecipientId recipientId, @NonNull Uri contactPhotoUri, long lastModifiedTime) {
|
||||||
this.address = address;
|
this.recipientId = recipientId;
|
||||||
this.contactPhotoUri = contactPhotoUri;
|
this.contactPhotoUri = contactPhotoUri;
|
||||||
this.lastModifiedTime = lastModifiedTime;
|
this.lastModifiedTime = lastModifiedTime;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class SystemContactPhoto implements ContactPhoto {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||||
messageDigest.update(address.serialize().getBytes());
|
messageDigest.update(recipientId.serialize().getBytes());
|
||||||
messageDigest.update(contactPhotoUri.toString().getBytes());
|
messageDigest.update(contactPhotoUri.toString().getBytes());
|
||||||
messageDigest.update(Conversions.longToByteArray(lastModifiedTime));
|
messageDigest.update(Conversions.longToByteArray(lastModifiedTime));
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,12 @@ public class SystemContactPhoto implements ContactPhoto {
|
||||||
|
|
||||||
SystemContactPhoto that = (SystemContactPhoto)other;
|
SystemContactPhoto that = (SystemContactPhoto)other;
|
||||||
|
|
||||||
return this.address.equals(that.address) && this.contactPhotoUri.equals(that.contactPhotoUri) && this.lastModifiedTime == that.lastModifiedTime;
|
return this.recipientId.equals(that.recipientId) && this.contactPhotoUri.equals(that.contactPhotoUri) && this.lastModifiedTime == that.lastModifiedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return address.hashCode() ^ contactPhotoUri.hashCode() ^ (int)lastModifiedTime;
|
return recipientId.hashCode() ^ contactPhotoUri.hashCode() ^ (int)lastModifiedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,527 +1,35 @@
|
||||||
package org.thoughtcrime.securesms.contacts.sync;
|
package org.thoughtcrime.securesms.contacts.sync;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.accounts.Account;
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.OperationApplicationException;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.provider.ContactsContract;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.WorkerThread;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
|
|
||||||
import com.annimon.stream.Collectors;
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.BuildConfig;
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
|
||||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
|
||||||
import org.thoughtcrime.securesms.push.IasTrustStore;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
||||||
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
|
||||||
import org.whispersystems.signalservice.api.push.TrustStore;
|
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
public class DirectoryHelper {
|
public class DirectoryHelper {
|
||||||
|
|
||||||
private static final String TAG = DirectoryHelper.class.getSimpleName();
|
@WorkerThread
|
||||||
|
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers) throws IOException {
|
||||||
private static final int CONTACT_DISCOVERY_BATCH_SIZE = 2048;
|
if (FeatureFlags.UUIDS) {
|
||||||
|
// TODO [greyson] Create a DirectoryHelperV2 when appropriate.
|
||||||
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers)
|
DirectoryHelperV1.refreshDirectory(context, notifyOfNewUsers);
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return;
|
|
||||||
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) return;
|
|
||||||
|
|
||||||
List<RecipientId> newlyActiveUsers = refreshDirectory(context, AccountManagerFactory.createManager(context));
|
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notifyOfNewUsers) notifyNewUsers(context, newlyActiveUsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
private static @NonNull List<RecipientId> refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
|
||||||
Stream<String> eligibleRecipientDatabaseContactNumbers = Stream.of(recipientDatabase.getAllAddresses()).filter(Address::isPhone).map(Address::toPhoneString);
|
|
||||||
Stream<String> eligibleSystemDatabaseContactNumbers = Stream.of(ContactAccessor.getInstance().getAllContactsWithNumbers(context)).map(Address::serialize);
|
|
||||||
Set<String> eligibleContactNumbers = Stream.concat(eligibleRecipientDatabaseContactNumbers, eligibleSystemDatabaseContactNumbers).collect(Collectors.toSet());
|
|
||||||
|
|
||||||
Future<DirectoryResult> legacyRequest = getLegacyDirectoryResult(context, accountManager, recipientDatabase, eligibleContactNumbers);
|
|
||||||
List<Future<Set<String>>> contactServiceRequest = getContactServiceDirectoryResult(context, accountManager, eligibleContactNumbers);
|
|
||||||
|
|
||||||
try {
|
|
||||||
DirectoryResult legacyResult = legacyRequest.get();
|
|
||||||
Optional<Set<String>> contactServiceResult = executeAndMergeContactDiscoveryRequests(accountManager, contactServiceRequest);
|
|
||||||
|
|
||||||
if (!contactServiceResult.isPresent()) {
|
|
||||||
Log.i(TAG, "[Batch] New contact discovery service failed, so we're skipping the comparison.");
|
|
||||||
return legacyResult.getNewlyActiveRecipients();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legacyResult.getNumbers().size() == contactServiceResult.get().size() && legacyResult.getNumbers().containsAll(contactServiceResult.get())) {
|
|
||||||
Log.i(TAG, "[Batch] New contact discovery service request matched existing results.");
|
|
||||||
accountManager.reportContactDiscoveryServiceMatch();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "[Batch] New contact discovery service request did NOT match existing results.");
|
|
||||||
accountManager.reportContactDiscoveryServiceMismatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
return legacyResult.getNewlyActiveRecipients();
|
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException("[Batch] Operation was interrupted.", e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
if (e.getCause() instanceof IOException) {
|
|
||||||
throw (IOException) e.getCause();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "[Batch] Experienced an unexpected exception.", e);
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RegisteredState refreshDirectoryFor(@NonNull Context context,
|
|
||||||
@NonNull Recipient recipient)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
|
||||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
|
|
||||||
|
|
||||||
Future<RegisteredState> legacyRequest = getLegacyRegisteredState(context, accountManager, recipientDatabase, recipient);
|
|
||||||
List<Future<Set<String>>> contactServiceRequest = getContactServiceDirectoryResult(context, accountManager, Collections.singleton(recipient.requireAddress().serialize()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
RegisteredState legacyState = legacyRequest.get();
|
|
||||||
Optional<Set<String>> contactServiceResult = executeAndMergeContactDiscoveryRequests(accountManager, contactServiceRequest);
|
|
||||||
|
|
||||||
if (!contactServiceResult.isPresent()) {
|
|
||||||
Log.i(TAG, "[Singular] New contact discovery service failed, so we're skipping the comparison.");
|
|
||||||
return legacyState;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisteredState contactServiceState = contactServiceResult.get().size() == 1 ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
|
|
||||||
|
|
||||||
if (legacyState == contactServiceState) {
|
|
||||||
Log.i(TAG, "[Singular] New contact discovery service request matched existing results.");
|
|
||||||
accountManager.reportContactDiscoveryServiceMatch();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "[Singular] New contact discovery service request did NOT match existing results.");
|
|
||||||
accountManager.reportContactDiscoveryServiceMismatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
return legacyState;
|
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException("[Singular] Operation was interrupted.", e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
if (e.getCause() instanceof IOException) {
|
|
||||||
throw (IOException) e.getCause();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "[Singular] Experienced an unexpected exception.", e);
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void updateContactsDatabase(@NonNull Context context, @NonNull List<RecipientId> activeIds, boolean removeMissing) {
|
|
||||||
Optional<AccountHolder> account = getOrCreateAccount(context);
|
|
||||||
|
|
||||||
if (account.isPresent()) {
|
|
||||||
try {
|
|
||||||
List<Address> activeAddresses = Stream.of(activeIds).map(Recipient::resolved).map(Recipient::requireAddress).toList();
|
|
||||||
|
|
||||||
DatabaseFactory.getContactsDatabase(context).removeDeletedRawContacts(account.get().getAccount());
|
|
||||||
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get().getAccount(), activeAddresses, removeMissing);
|
|
||||||
|
|
||||||
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
|
|
||||||
RecipientDatabase.BulkOperationsHandle handle = DatabaseFactory.getRecipientDatabase(context).resetAllSystemContactInfo();
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
|
||||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(number)) {
|
|
||||||
RecipientId recipientId = Recipient.external(context, number).getId();
|
|
||||||
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, phoneType, contactUri.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
handle.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NotificationChannels.supported()) {
|
|
||||||
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
|
|
||||||
Recipient recipient;
|
|
||||||
while ((recipient = recipients.getNext()) != null) {
|
|
||||||
NotificationChannels.updateContactChannelName(context, recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (RemoteException | OperationApplicationException e) {
|
|
||||||
Log.w(TAG, "Failed to update contacts.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void notifyNewUsers(@NonNull Context context,
|
|
||||||
@NonNull List<RecipientId> newUsers)
|
|
||||||
{
|
|
||||||
if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return;
|
|
||||||
|
|
||||||
for (RecipientId newUser: newUsers) {
|
|
||||||
Recipient recipient = Recipient.resolved(newUser);
|
|
||||||
if (!SessionUtil.hasSession(context, recipient.requireAddress()) && !recipient.isLocalNumber()) {
|
|
||||||
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
|
|
||||||
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
|
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
|
||||||
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
|
|
||||||
if (hour >= 9 && hour < 23) {
|
|
||||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), true);
|
|
||||||
} else {
|
|
||||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Optional<AccountHolder> getOrCreateAccount(Context context) {
|
|
||||||
AccountManager accountManager = AccountManager.get(context);
|
|
||||||
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
|
|
||||||
|
|
||||||
Optional<AccountHolder> account;
|
|
||||||
|
|
||||||
if (accounts.length == 0) account = createAccount(context);
|
|
||||||
else account = Optional.of(new AccountHolder(accounts[0], false));
|
|
||||||
|
|
||||||
if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY)) {
|
|
||||||
ContentResolver.setSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Optional<AccountHolder> createAccount(Context context) {
|
|
||||||
AccountManager accountManager = AccountManager.get(context);
|
|
||||||
Account account = new Account(context.getString(R.string.app_name), "org.thoughtcrime.securesms");
|
|
||||||
|
|
||||||
if (accountManager.addAccountExplicitly(account, null, null)) {
|
|
||||||
Log.i(TAG, "Created new account...");
|
|
||||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
|
||||||
return Optional.of(new AccountHolder(account, true));
|
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Failed to create account!");
|
DirectoryHelperV1.refreshDirectory(context, notifyOfNewUsers);
|
||||||
return Optional.absent();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Future<DirectoryResult> getLegacyDirectoryResult(@NonNull Context context,
|
@WorkerThread
|
||||||
@NonNull SignalServiceAccountManager accountManager,
|
public static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
|
||||||
@NonNull RecipientDatabase recipientDatabase,
|
if (FeatureFlags.UUIDS) {
|
||||||
@NonNull Set<String> eligibleContactNumbers)
|
// TODO [greyson] Create a DirectoryHelperV2 when appropriate.
|
||||||
{
|
return DirectoryHelperV1.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
|
||||||
return SignalExecutors.UNBOUNDED.submit(() -> {
|
|
||||||
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
|
|
||||||
|
|
||||||
if (activeTokens != null) {
|
|
||||||
List<RecipientId> activeIds = new LinkedList<>();
|
|
||||||
List<RecipientId> inactiveIds = new LinkedList<>();
|
|
||||||
|
|
||||||
Set<String> inactiveContactNumbers = new HashSet<>(eligibleContactNumbers);
|
|
||||||
|
|
||||||
for (ContactTokenDetails activeToken : activeTokens) {
|
|
||||||
activeIds.add(recipientDatabase.getOrInsertFromE164(activeToken.getNumber()));
|
|
||||||
inactiveContactNumbers.remove(activeToken.getNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String inactiveContactNumber : inactiveContactNumbers) {
|
|
||||||
inactiveIds.add(recipientDatabase.getOrInsertFromE164(inactiveContactNumber));
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<RecipientId> currentActiveIds = new HashSet<>(recipientDatabase.getRegistered());
|
|
||||||
Set<RecipientId> contactIds = new HashSet<>(recipientDatabase.getSystemContacts());
|
|
||||||
List<RecipientId> newlyActiveIds = Stream.of(activeIds)
|
|
||||||
.filter(id -> !currentActiveIds.contains(id))
|
|
||||||
.filter(contactIds::contains)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
recipientDatabase.setRegistered(activeIds, inactiveIds);
|
|
||||||
updateContactsDatabase(context, activeIds, true);
|
|
||||||
|
|
||||||
Set<String> activeContactNumbers = Stream.of(activeIds).map(Recipient::resolved).map(Recipient::requireAddress).map(Address::serialize).collect(Collectors.toSet());
|
|
||||||
|
|
||||||
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context)) {
|
|
||||||
return new DirectoryResult(activeContactNumbers, newlyActiveIds);
|
|
||||||
} else {
|
|
||||||
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
|
|
||||||
return new DirectoryResult(activeContactNumbers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new DirectoryResult(Collections.emptySet(), Collections.emptyList());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Future<RegisteredState> getLegacyRegisteredState(@NonNull Context context,
|
|
||||||
@NonNull SignalServiceAccountManager accountManager,
|
|
||||||
@NonNull RecipientDatabase recipientDatabase,
|
|
||||||
@NonNull Recipient recipient)
|
|
||||||
{
|
|
||||||
return SignalExecutors.UNBOUNDED.submit(() -> {
|
|
||||||
boolean activeUser = recipient.resolve().getRegistered() == RegisteredState.REGISTERED;
|
|
||||||
boolean systemContact = recipient.isSystemContact();
|
|
||||||
String number = recipient.requireAddress().serialize();
|
|
||||||
Optional<ContactTokenDetails> details = accountManager.getContact(number);
|
|
||||||
|
|
||||||
if (details.isPresent()) {
|
|
||||||
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.REGISTERED);
|
|
||||||
|
|
||||||
if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
|
|
||||||
updateContactsDatabase(context, Util.asList(recipient.getId()), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!activeUser && TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!activeUser && systemContact && !TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
|
|
||||||
notifyNewUsers(context, Collections.singletonList(recipient.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return RegisteredState.REGISTERED;
|
|
||||||
} else {
|
|
||||||
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.NOT_REGISTERED);
|
|
||||||
return RegisteredState.NOT_REGISTERED;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Future<Set<String>>> getContactServiceDirectoryResult(@NonNull Context context,
|
|
||||||
@NonNull SignalServiceAccountManager accountManager,
|
|
||||||
@NonNull Set<String> eligibleContactNumbers)
|
|
||||||
{
|
|
||||||
Set<String> sanitizedNumbers = sanitizeNumbers(eligibleContactNumbers);
|
|
||||||
List<Set<String>> batches = splitIntoBatches(sanitizedNumbers, CONTACT_DISCOVERY_BATCH_SIZE);
|
|
||||||
List<Future<Set<String>>> futures = new ArrayList<>(batches.size());
|
|
||||||
KeyStore iasKeyStore = getIasKeyStore(context);
|
|
||||||
|
|
||||||
for (Set<String> batch : batches) {
|
|
||||||
Future<Set<String>> future = SignalExecutors.UNBOUNDED.submit(() -> {
|
|
||||||
return new HashSet<>(accountManager.getRegisteredUsers(iasKeyStore, batch, BuildConfig.MRENCLAVE));
|
|
||||||
});
|
|
||||||
futures.add(future);
|
|
||||||
}
|
|
||||||
return futures;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<String> sanitizeNumbers(@NonNull Set<String> numbers) {
|
|
||||||
return Stream.of(numbers).filter(number -> {
|
|
||||||
try {
|
|
||||||
return number.startsWith("+") && number.length() > 1 && number.charAt(1) != '0' && Long.parseLong(number.substring(1)) > 0;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Set<String>> splitIntoBatches(@NonNull Set<String> numbers, int batchSize) {
|
|
||||||
List<String> numberList = new ArrayList<>(numbers);
|
|
||||||
List<Set<String>> batches = new LinkedList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < numberList.size(); i += batchSize) {
|
|
||||||
List<String> batch = numberList.subList(i, Math.min(numberList.size(), i + batchSize));
|
|
||||||
batches.add(new HashSet<>(batch));
|
|
||||||
}
|
|
||||||
|
|
||||||
return batches;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Optional<Set<String>> executeAndMergeContactDiscoveryRequests(@NonNull SignalServiceAccountManager accountManager, @NonNull List<Future<Set<String>>> futures) {
|
|
||||||
Set<String> results = new HashSet<>();
|
|
||||||
try {
|
|
||||||
for (Future<Set<String>> future : futures) {
|
|
||||||
results.addAll(future.get());
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.w(TAG, "Contact discovery batch was interrupted.", e);
|
|
||||||
accountManager.reportContactDiscoveryServiceUnexpectedError(buildErrorReason(e));
|
|
||||||
return Optional.absent();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
if (isAttestationError(e.getCause())) {
|
|
||||||
Log.w(TAG, "Failed during attestation.", e);
|
|
||||||
accountManager.reportContactDiscoveryServiceAttestationError(buildErrorReason(e.getCause()));
|
|
||||||
return Optional.absent();
|
|
||||||
} else if (e.getCause() instanceof PushNetworkException) {
|
|
||||||
Log.w(TAG, "Failed due to poor network.", e);
|
|
||||||
return Optional.absent();
|
|
||||||
} else if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
|
|
||||||
Log.w(TAG, "Failed due to non successful response code.", e);
|
|
||||||
return Optional.absent();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Failed for an unknown reason.", e);
|
|
||||||
accountManager.reportContactDiscoveryServiceUnexpectedError(buildErrorReason(e.getCause()));
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.of(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isAttestationError(Throwable e) {
|
|
||||||
return e instanceof CertificateException ||
|
|
||||||
e instanceof SignatureException ||
|
|
||||||
e instanceof UnauthenticatedQuoteException ||
|
|
||||||
e instanceof UnauthenticatedResponseException ||
|
|
||||||
e instanceof Quote.InvalidQuoteFormatException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static KeyStore getIasKeyStore(@NonNull Context context) {
|
|
||||||
try {
|
|
||||||
TrustStore contactTrustStore = new IasTrustStore(context);
|
|
||||||
|
|
||||||
KeyStore keyStore = KeyStore.getInstance("BKS");
|
|
||||||
keyStore.load(contactTrustStore.getKeyStoreInputStream(), contactTrustStore.getKeyStorePassword().toCharArray());
|
|
||||||
|
|
||||||
return keyStore;
|
|
||||||
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String buildErrorReason(@Nullable Throwable t) {
|
|
||||||
if (t == null) {
|
|
||||||
return "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
String rawString = android.util.Log.getStackTraceString(t);
|
|
||||||
List<String> lines = Arrays.asList(rawString.split("\\n"));
|
|
||||||
|
|
||||||
String errorString;
|
|
||||||
|
|
||||||
if (lines.size() > 1) {
|
|
||||||
errorString = t.getClass().getName() + "\n" + Util.join(lines.subList(1, lines.size()), "\n");
|
|
||||||
} else {
|
} else {
|
||||||
errorString = t.getClass().getName();
|
return DirectoryHelperV1.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
|
||||||
}
|
|
||||||
|
|
||||||
if (errorString.length() > 1000) {
|
|
||||||
return errorString.substring(0, 1000);
|
|
||||||
} else {
|
|
||||||
return errorString;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DirectoryResult {
|
|
||||||
|
|
||||||
private final Set<String> numbers;
|
|
||||||
private final List<RecipientId> newlyActiveRecipients;
|
|
||||||
|
|
||||||
DirectoryResult(@NonNull Set<String> numbers) {
|
|
||||||
this(numbers, Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectoryResult(@NonNull Set<String> numbers, @NonNull List<RecipientId> newlyActiveRecipients) {
|
|
||||||
this.numbers = numbers;
|
|
||||||
this.newlyActiveRecipients = newlyActiveRecipients;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> getNumbers() {
|
|
||||||
return numbers;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<RecipientId> getNewlyActiveRecipients() {
|
|
||||||
return newlyActiveRecipients;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AccountHolder {
|
|
||||||
|
|
||||||
private final boolean fresh;
|
|
||||||
private final Account account;
|
|
||||||
|
|
||||||
private AccountHolder(Account account, boolean fresh) {
|
|
||||||
this.fresh = fresh;
|
|
||||||
this.account = account;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public boolean isFresh() {
|
|
||||||
return fresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Account getAccount() {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,351 @@
|
||||||
|
package org.thoughtcrime.securesms.contacts.sync;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.OperationApplicationException;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
|
import com.annimon.stream.Collectors;
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
|
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
|
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
class DirectoryHelperV1 {
|
||||||
|
|
||||||
|
private static final String TAG = DirectoryHelperV1.class.getSimpleName();
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers) throws IOException {
|
||||||
|
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return;
|
||||||
|
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) return;
|
||||||
|
|
||||||
|
List<RecipientId> newlyActiveUsers = refreshDirectory(context, ApplicationDependencies.getSignalServiceAccountManager());
|
||||||
|
|
||||||
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||||
|
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifyOfNewUsers) notifyNewUsers(context, newlyActiveUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
private static @NonNull List<RecipientId> refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager) throws IOException {
|
||||||
|
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
Stream<String> eligibleRecipientDatabaseContactNumbers = Stream.of(recipientDatabase.getAllPhoneNumbers());
|
||||||
|
Stream<String> eligibleSystemDatabaseContactNumbers = Stream.of(ContactAccessor.getInstance().getAllContactsWithNumbers(context));
|
||||||
|
Set<String> eligibleContactNumbers = Stream.concat(eligibleRecipientDatabaseContactNumbers, eligibleSystemDatabaseContactNumbers).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Future<DirectoryResult> legacyRequest = getLegacyDirectoryResult(context, accountManager, recipientDatabase, eligibleContactNumbers);
|
||||||
|
DirectoryResult legacyResult = legacyRequest.get();
|
||||||
|
|
||||||
|
return legacyResult.getNewlyActiveRecipients();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException("[Batch] Operation was interrupted.", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
if (e.getCause() instanceof IOException) {
|
||||||
|
throw (IOException) e.getCause();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "[Batch] Experienced an unexpected exception.", e);
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
|
||||||
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||||
|
Future<RegisteredState> legacyRequest = getLegacyRegisteredState(context, accountManager, recipientDatabase, recipient);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return legacyRequest.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException("[Singular] Operation was interrupted.", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
if (e.getCause() instanceof IOException) {
|
||||||
|
throw (IOException) e.getCause();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "[Singular] Experienced an unexpected exception.", e);
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateContactsDatabase(@NonNull Context context, @NonNull List<RecipientId> activeIds, boolean removeMissing) {
|
||||||
|
Optional<AccountHolder> account = getOrCreateAccount(context);
|
||||||
|
|
||||||
|
if (account.isPresent()) {
|
||||||
|
try {
|
||||||
|
List<String> activeAddresses = Stream.of(activeIds).map(Recipient::resolved).filter(Recipient::hasE164).map(Recipient::requireE164).toList();
|
||||||
|
|
||||||
|
DatabaseFactory.getContactsDatabase(context).removeDeletedRawContacts(account.get().getAccount());
|
||||||
|
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get().getAccount(), activeAddresses, removeMissing);
|
||||||
|
|
||||||
|
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
|
||||||
|
RecipientDatabase.BulkOperationsHandle handle = DatabaseFactory.getRecipientDatabase(context).resetAllSystemContactInfo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
|
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(number)) {
|
||||||
|
RecipientId recipientId = Recipient.external(context, number).getId();
|
||||||
|
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, phoneType, contactUri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
handle.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NotificationChannels.supported()) {
|
||||||
|
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
|
||||||
|
Recipient recipient;
|
||||||
|
while ((recipient = recipients.getNext()) != null) {
|
||||||
|
NotificationChannels.updateContactChannelName(context, recipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException | OperationApplicationException e) {
|
||||||
|
Log.w(TAG, "Failed to update contacts.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void notifyNewUsers(@NonNull Context context,
|
||||||
|
@NonNull List<RecipientId> newUsers)
|
||||||
|
{
|
||||||
|
if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return;
|
||||||
|
|
||||||
|
for (RecipientId newUser: newUsers) {
|
||||||
|
Recipient recipient = Recipient.resolved(newUser);
|
||||||
|
if (!SessionUtil.hasSession(context, recipient.getId()) && !recipient.isLocalNumber()) {
|
||||||
|
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
|
||||||
|
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
|
||||||
|
|
||||||
|
if (insertResult.isPresent()) {
|
||||||
|
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
|
||||||
|
if (hour >= 9 && hour < 23) {
|
||||||
|
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), true);
|
||||||
|
} else {
|
||||||
|
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<AccountHolder> getOrCreateAccount(Context context) {
|
||||||
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
|
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
|
||||||
|
|
||||||
|
Optional<AccountHolder> account;
|
||||||
|
|
||||||
|
if (accounts.length == 0) account = createAccount(context);
|
||||||
|
else account = Optional.of(new AccountHolder(accounts[0], false));
|
||||||
|
|
||||||
|
if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY)) {
|
||||||
|
ContentResolver.setSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<AccountHolder> createAccount(Context context) {
|
||||||
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
|
Account account = new Account(context.getString(R.string.app_name), "org.thoughtcrime.securesms");
|
||||||
|
|
||||||
|
if (accountManager.addAccountExplicitly(account, null, null)) {
|
||||||
|
Log.i(TAG, "Created new account...");
|
||||||
|
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||||
|
return Optional.of(new AccountHolder(account, true));
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed to create account!");
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Future<DirectoryResult> getLegacyDirectoryResult(@NonNull Context context,
|
||||||
|
@NonNull SignalServiceAccountManager accountManager,
|
||||||
|
@NonNull RecipientDatabase recipientDatabase,
|
||||||
|
@NonNull Set<String> eligibleContactNumbers)
|
||||||
|
{
|
||||||
|
return SignalExecutors.UNBOUNDED.submit(() -> {
|
||||||
|
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
|
||||||
|
|
||||||
|
if (activeTokens != null) {
|
||||||
|
List<RecipientId> activeIds = new LinkedList<>();
|
||||||
|
List<RecipientId> inactiveIds = new LinkedList<>();
|
||||||
|
|
||||||
|
Set<String> inactiveContactNumbers = new HashSet<>(eligibleContactNumbers);
|
||||||
|
|
||||||
|
for (ContactTokenDetails activeToken : activeTokens) {
|
||||||
|
activeIds.add(recipientDatabase.getOrInsertFromE164(activeToken.getNumber()));
|
||||||
|
inactiveContactNumbers.remove(activeToken.getNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String inactiveContactNumber : inactiveContactNumbers) {
|
||||||
|
inactiveIds.add(recipientDatabase.getOrInsertFromE164(inactiveContactNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<RecipientId> currentActiveIds = new HashSet<>(recipientDatabase.getRegistered());
|
||||||
|
Set<RecipientId> contactIds = new HashSet<>(recipientDatabase.getSystemContacts());
|
||||||
|
List<RecipientId> newlyActiveIds = Stream.of(activeIds)
|
||||||
|
.filter(id -> !currentActiveIds.contains(id))
|
||||||
|
.filter(contactIds::contains)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
recipientDatabase.setRegistered(activeIds, inactiveIds);
|
||||||
|
updateContactsDatabase(context, activeIds, true);
|
||||||
|
|
||||||
|
Set<String> activeContactNumbers = Stream.of(activeIds).map(Recipient::resolved).filter(Recipient::hasSmsAddress).map(Recipient::requireSmsAddress).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context)) {
|
||||||
|
return new DirectoryResult(activeContactNumbers, newlyActiveIds);
|
||||||
|
} else {
|
||||||
|
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
|
||||||
|
return new DirectoryResult(activeContactNumbers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new DirectoryResult(Collections.emptySet(), Collections.emptyList());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Future<RegisteredState> getLegacyRegisteredState(@NonNull Context context,
|
||||||
|
@NonNull SignalServiceAccountManager accountManager,
|
||||||
|
@NonNull RecipientDatabase recipientDatabase,
|
||||||
|
@NonNull Recipient recipient)
|
||||||
|
{
|
||||||
|
return SignalExecutors.UNBOUNDED.submit(() -> {
|
||||||
|
boolean activeUser = recipient.resolve().getRegistered() == RegisteredState.REGISTERED;
|
||||||
|
boolean systemContact = recipient.isSystemContact();
|
||||||
|
Optional<ContactTokenDetails> details = recipient.hasE164() ? accountManager.getContact(recipient.requireE164()) : Optional.absent();
|
||||||
|
|
||||||
|
if (details.isPresent()) {
|
||||||
|
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.REGISTERED);
|
||||||
|
|
||||||
|
if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
|
||||||
|
updateContactsDatabase(context, Util.asList(recipient.getId()), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activeUser && TextSecurePreferences.isMultiDevice(context)) {
|
||||||
|
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activeUser && systemContact && !TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
|
||||||
|
notifyNewUsers(context, Collections.singletonList(recipient.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegisteredState.REGISTERED;
|
||||||
|
} else {
|
||||||
|
recipientDatabase.setRegistered(recipient.getId(), RegisteredState.NOT_REGISTERED);
|
||||||
|
return RegisteredState.NOT_REGISTERED;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class DirectoryResult {
|
||||||
|
|
||||||
|
private final Set<String> numbers;
|
||||||
|
private final List<RecipientId> newlyActiveRecipients;
|
||||||
|
|
||||||
|
DirectoryResult(@NonNull Set<String> numbers) {
|
||||||
|
this(numbers, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryResult(@NonNull Set<String> numbers, @NonNull List<RecipientId> newlyActiveRecipients) {
|
||||||
|
this.numbers = numbers;
|
||||||
|
this.newlyActiveRecipients = newlyActiveRecipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> getNumbers() {
|
||||||
|
return numbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RecipientId> getNewlyActiveRecipients() {
|
||||||
|
return newlyActiveRecipients;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AccountHolder {
|
||||||
|
|
||||||
|
private final boolean fresh;
|
||||||
|
private final Account account;
|
||||||
|
|
||||||
|
private AccountHolder(Account account, boolean fresh) {
|
||||||
|
this.fresh = fresh;
|
||||||
|
this.account = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public boolean isFresh() {
|
||||||
|
return fresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getAccount() {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
|
@ -123,7 +122,7 @@ public final class ContactUtil {
|
||||||
CharSequence[] values = new CharSequence[choices.size()];
|
CharSequence[] values = new CharSequence[choices.size()];
|
||||||
|
|
||||||
for (int i = 0; i < values.length; i++) {
|
for (int i = 0; i < values.length; i++) {
|
||||||
values[i] = getPrettyPhoneNumber(choices.get(i).requireAddress().toPhoneString(), locale);
|
values[i] = getPrettyPhoneNumber(choices.get(i).requireE164(), locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context)
|
||||||
|
|
|
@ -228,7 +228,7 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
|
||||||
|
|
||||||
inviteButtonView.setOnClickListener(v -> {
|
inviteButtonView.setOnClickListener(v -> {
|
||||||
ContactUtil.selectRecipientThroughDialog(this, systemUsers, dynamicLanguage.getCurrentLocale(), recipient -> {
|
ContactUtil.selectRecipientThroughDialog(this, systemUsers, dynamicLanguage.getCurrentLocale(), recipient -> {
|
||||||
CommunicationActions.composeSmsThroughDefaultApp(this, recipient.requireAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
|
CommunicationActions.composeSmsThroughDefaultApp(this, recipient, getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -940,7 +940,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
composeText.appendInvite(inviteText);
|
composeText.appendInvite(inviteText);
|
||||||
} else {
|
} else {
|
||||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||||
intent.setData(Uri.parse("smsto:" + recipient.get().requireAddress().serialize()));
|
intent.setData(Uri.parse("smsto:" + recipient.get().requireSmsAddress()));
|
||||||
intent.putExtra("sms_body", inviteText);
|
intent.putExtra("sms_body", inviteText);
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
|
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
@ -984,7 +984,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAddShortcut() {
|
private void handleAddShortcut() {
|
||||||
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.get().requireAddress());
|
Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.get().getId());
|
||||||
|
|
||||||
new AsyncTask<Void, Void, IconCompat>() {
|
new AsyncTask<Void, Void, IconCompat>() {
|
||||||
|
|
||||||
|
@ -1018,7 +1018,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
.or(Optional.fromNullable(recipient.get().getProfileName()))
|
.or(Optional.fromNullable(recipient.get().getProfileName()))
|
||||||
.or(recipient.get().toShortString());
|
.or(recipient.get().toShortString());
|
||||||
|
|
||||||
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.get().requireAddress().serialize() + '-' + System.currentTimeMillis())
|
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.get().getId().serialize() + '-' + System.currentTimeMillis())
|
||||||
.setShortLabel(name)
|
.setShortLabel(name)
|
||||||
.setIcon(icon)
|
.setIcon(icon)
|
||||||
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getId()))
|
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getId()))
|
||||||
|
@ -1056,7 +1056,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
MessageSender.send(this, leaveMessage.get(), threadId, false, null);
|
MessageSender.send(this, leaveMessage.get(), threadId, false, null);
|
||||||
|
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
||||||
String groupId = groupRecipient.requireAddress().toGroupString();
|
String groupId = groupRecipient.requireGroupId();
|
||||||
groupDatabase.setActive(groupId, false);
|
groupDatabase.setActive(groupId, false);
|
||||||
groupDatabase.remove(groupId, Recipient.self().getId());
|
groupDatabase.remove(groupId, Recipient.self().getId());
|
||||||
|
|
||||||
|
@ -1072,7 +1072,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
private void handleEditPushGroup() {
|
private void handleEditPushGroup() {
|
||||||
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
|
Intent intent = new Intent(ConversationActivity.this, GroupCreateActivity.class);
|
||||||
intent.putExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA, recipient.get().requireAddress());
|
intent.putExtra(GroupCreateActivity.GROUP_ID_EXTRA, recipient.get().requireGroupId());
|
||||||
startActivityForResult(intent, GROUP_EDIT);
|
startActivityForResult(intent, GROUP_EDIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1116,7 +1116,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
|
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
|
||||||
Uri.parse("tel:" + recipient.requireAddress().serialize()));
|
Uri.parse("tel:" + recipient.requireSmsAddress()));
|
||||||
startActivity(dialIntent);
|
startActivity(dialIntent);
|
||||||
} catch (ActivityNotFoundException anfe) {
|
} catch (ActivityNotFoundException anfe) {
|
||||||
Log.w(TAG, anfe);
|
Log.w(TAG, anfe);
|
||||||
|
@ -1132,7 +1132,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAddToContacts() {
|
private void handleAddToContacts() {
|
||||||
if (recipient.get().requireAddress().isGroup()) return;
|
if (recipient.get().isGroup()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startActivityForResult(RecipientExporter.export(recipient.get()).asAddContactIntent(), ADD_CONTACT);
|
startActivityForResult(RecipientExporter.export(recipient.get()).asAddContactIntent(), ADD_CONTACT);
|
||||||
|
@ -1142,7 +1142,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleDisplayQuickContact() {
|
private boolean handleDisplayQuickContact() {
|
||||||
if (recipient.get().requireAddress().isGroup()) return false;
|
if (recipient.get().isGroup()) return false;
|
||||||
|
|
||||||
if (recipient.get().getContactUri() != null) {
|
if (recipient.get().getContactUri() != null) {
|
||||||
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.get().getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
|
ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.get().getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
|
||||||
|
@ -1397,8 +1397,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
if (registeredState == RegisteredState.UNKNOWN) {
|
if (registeredState == RegisteredState.UNKNOWN) {
|
||||||
try {
|
try {
|
||||||
Log.i(TAG, "Refreshing directory for user: " + recipient.requireAddress().serialize());
|
Log.i(TAG, "Refreshing directory for user: " + recipient.getId().serialize());
|
||||||
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient);
|
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient, false);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
|
@ -1487,13 +1487,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
if (params[0].isGroup()) {
|
if (params[0].isGroup()) {
|
||||||
recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this)
|
recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this)
|
||||||
.getGroupMembers(params[0].requireAddress().toGroupString(), false));
|
.getGroupMembers(params[0].requireGroupId(), false));
|
||||||
} else {
|
} else {
|
||||||
recipients.add(params[0]);
|
recipients.add(params[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
for (Recipient recipient : recipients) {
|
||||||
Log.i(TAG, "Loading identity for: " + recipient.requireAddress());
|
Log.i(TAG, "Loading identity for: " + recipient.getId());
|
||||||
identityRecordList.add(identityDatabase.getIdentity(recipient.getId()));
|
identityRecordList.add(identityDatabase.getIdentity(recipient.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2023,9 +2023,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
@SuppressWarnings("SimplifiableIfStatement")
|
@SuppressWarnings("SimplifiableIfStatement")
|
||||||
private boolean isSelfConversation() {
|
private boolean isSelfConversation() {
|
||||||
if (!TextSecurePreferences.isPushRegistered(this)) return false;
|
if (!TextSecurePreferences.isPushRegistered(this)) return false;
|
||||||
if (recipient.get().isGroup()) return false;
|
if (recipient.get().isGroup()) return false;
|
||||||
|
|
||||||
return Util.isOwnNumber(this, recipient.get().requireAddress());
|
return recipient.get().isLocalNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isGroupConversation() {
|
private boolean isGroupConversation() {
|
||||||
|
@ -2152,8 +2152,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
boolean initiating = threadId == -1;
|
boolean initiating = threadId == -1;
|
||||||
boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
|
boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
|
||||||
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
|
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
|
||||||
recipient.isGroup() ||
|
recipient.isGroup() ||
|
||||||
recipient.requireAddress().isEmail() ||
|
recipient.getEmail().isPresent() ||
|
||||||
inputPanel.getQuote().isPresent() ||
|
inputPanel.getQuote().isPresent() ||
|
||||||
linkPreviewViewModel.hasLinkPreview() ||
|
linkPreviewViewModel.hasLinkPreview() ||
|
||||||
needsSplit;
|
needsSplit;
|
||||||
|
@ -2161,7 +2161,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection());
|
Log.i(TAG, "isManual Selection: " + sendButton.isManualSelection());
|
||||||
Log.i(TAG, "forceSms: " + forceSms);
|
Log.i(TAG, "forceSms: " + forceSms);
|
||||||
|
|
||||||
if ((recipient.isMmsGroup() || recipient.requireAddress().isEmail()) && !isMmsEnabled) {
|
if ((recipient.isMmsGroup() || recipient.getEmail().isPresent()) && !isMmsEnabled) {
|
||||||
handleManualMmsRequired();
|
handleManualMmsRequired();
|
||||||
} else if (!forceSms && identityRecords.isUnverified()) {
|
} else if (!forceSms && identityRecords.isUnverified()) {
|
||||||
handleUnverifiedRecipients();
|
handleUnverifiedRecipients();
|
||||||
|
|
|
@ -1083,7 +1083,7 @@ public class ConversationFragment extends Fragment
|
||||||
if (getContext() == null) return;
|
if (getContext() == null) return;
|
||||||
|
|
||||||
ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> {
|
ContactUtil.selectRecipientThroughDialog(getContext(), choices, locale, recipient -> {
|
||||||
CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient.requireAddress(), getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
|
CommunicationActions.composeSmsThroughDefaultApp(getContext(), recipient, getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
|
|
||||||
public class ConversationTitleView extends RelativeLayout {
|
public class ConversationTitleView extends RelativeLayout {
|
||||||
|
@ -116,7 +117,7 @@ public class ConversationTitleView extends RelativeLayout {
|
||||||
|
|
||||||
this.title.setText(recipient.getName());
|
this.title.setText(recipient.getName());
|
||||||
this.subtitle.setText(Stream.of(recipient.getParticipants())
|
this.subtitle.setText(Stream.of(recipient.getParticipants())
|
||||||
.filter(r -> !r.requireAddress().serialize().equals(localNumber))
|
.filterNot(Recipient::isLocalNumber)
|
||||||
.map(Recipient::toShortString)
|
.map(Recipient::toShortString)
|
||||||
.collect(Collectors.joining(", ")));
|
.collect(Collectors.joining(", ")));
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@ public class ConversationTitleView extends RelativeLayout {
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
private void setNonContactRecipientTitle(Recipient recipient) {
|
private void setNonContactRecipientTitle(Recipient recipient) {
|
||||||
this.title.setText(recipient.requireAddress().serialize());
|
this.title.setText(Util.getFirstNonEmpty(recipient.getE164().orNull(), recipient.getUuid().orNull()));
|
||||||
|
|
||||||
if (TextUtils.isEmpty(recipient.getProfileName())) {
|
if (TextUtils.isEmpty(recipient.getProfileName())) {
|
||||||
this.subtitle.setText(null);
|
this.subtitle.setText(null);
|
||||||
|
|
|
@ -4,16 +4,17 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.state.SessionStore;
|
import org.whispersystems.libsignal.state.SessionStore;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
public class SessionUtil {
|
public class SessionUtil {
|
||||||
|
|
||||||
public static boolean hasSession(Context context, @NonNull Address address) {
|
public static boolean hasSession(@NonNull Context context, @NonNull RecipientId id) {
|
||||||
SessionStore sessionStore = new TextSecureSessionStore(context);
|
SessionStore sessionStore = new TextSecureSessionStore(context);
|
||||||
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID);
|
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(Recipient.resolved(id).requireServiceId(), SignalServiceAddress.DEFAULT_DEVICE_ID);
|
||||||
|
|
||||||
return sessionStore.containsSession(axolotlAddress);
|
return sessionStore.containsSession(axolotlAddress);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ public class UnidentifiedAccessUtil {
|
||||||
try {
|
try {
|
||||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
byte[] ourUnidentifiedAccessCertificate = recipient.resolve().isUuidSupported() ? TextSecurePreferences.getUnidentifiedAccessCertificate(context)
|
||||||
|
: TextSecurePreferences.getUnidentifiedAccessCertificateLegacy(context) ;
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||||
|
@ -56,7 +57,8 @@ public class UnidentifiedAccessUtil {
|
||||||
|
|
||||||
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null) +
|
||||||
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
|
" | Our access key present? " + (ourUnidentifiedAccessKey != null) +
|
||||||
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null));
|
" | Our certificate present? " + (ourUnidentifiedAccessCertificate != null) +
|
||||||
|
" | UUID certificate supported? " + recipient.isUuidSupported());
|
||||||
|
|
||||||
if (theirUnidentifiedAccessKey != null &&
|
if (theirUnidentifiedAccessKey != null &&
|
||||||
ourUnidentifiedAccessKey != null &&
|
ourUnidentifiedAccessKey != null &&
|
||||||
|
@ -83,7 +85,8 @@ public class UnidentifiedAccessUtil {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
byte[] ourUnidentifiedAccessCertificate = Recipient.self().isUuidSupported() ? TextSecurePreferences.getUnidentifiedAccessCertificate(context)
|
||||||
|
: TextSecurePreferences.getUnidentifiedAccessCertificateLegacy(context);
|
||||||
|
|
||||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||||
|
|
|
@ -5,12 +5,10 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
|
|
|
@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.crypto.storage;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SessionDatabase;
|
import org.thoughtcrime.securesms.database.SessionDatabase;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -13,8 +13,10 @@ import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||||
import org.whispersystems.libsignal.state.SessionRecord;
|
import org.whispersystems.libsignal.state.SessionRecord;
|
||||||
import org.whispersystems.libsignal.state.SessionStore;
|
import org.whispersystems.libsignal.state.SessionStore;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class TextSecureSessionStore implements SessionStore {
|
public class TextSecureSessionStore implements SessionStore {
|
||||||
|
|
||||||
|
@ -54,12 +56,16 @@ public class TextSecureSessionStore implements SessionStore {
|
||||||
@Override
|
@Override
|
||||||
public boolean containsSession(SignalProtocolAddress address) {
|
public boolean containsSession(SignalProtocolAddress address) {
|
||||||
synchronized (FILE_LOCK) {
|
synchronized (FILE_LOCK) {
|
||||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||||
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||||
|
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
||||||
|
|
||||||
return sessionRecord != null &&
|
return sessionRecord != null &&
|
||||||
sessionRecord.getSessionState().hasSenderChain() &&
|
sessionRecord.getSessionState().hasSenderChain() &&
|
||||||
sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
|
sessionRecord.getSessionState().getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +101,7 @@ public class TextSecureSessionStore implements SessionStore {
|
||||||
for (SessionDatabase.SessionRow row : sessions) {
|
for (SessionDatabase.SessionRow row : sessions) {
|
||||||
if (row.getDeviceId() != address.getDeviceId()) {
|
if (row.getDeviceId() != address.getDeviceId()) {
|
||||||
row.getRecord().archiveCurrentState();
|
row.getRecord().archiveCurrentState();
|
||||||
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireAddress().serialize(), row.getDeviceId()), row.getRecord());
|
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireServiceId(), row.getDeviceId()), row.getRecord());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,7 +113,7 @@ public class TextSecureSessionStore implements SessionStore {
|
||||||
|
|
||||||
for (SessionDatabase.SessionRow row : sessions) {
|
for (SessionDatabase.SessionRow row : sessions) {
|
||||||
row.getRecord().archiveCurrentState();
|
row.getRecord().archiveCurrentState();
|
||||||
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireAddress().serialize(), row.getDeviceId()), row.getRecord());
|
storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireServiceId(), row.getDeviceId()), row.getRecord());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.database;
|
|
||||||
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
|
||||||
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
|
||||||
|
|
||||||
public class Address implements Parcelable, Comparable<Address> {
|
|
||||||
|
|
||||||
public static final Parcelable.Creator<Address> CREATOR = new Parcelable.Creator<Address>() {
|
|
||||||
public Address createFromParcel(Parcel in) {
|
|
||||||
return new Address(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Address[] newArray(int size) {
|
|
||||||
return new Address[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final Address UNKNOWN = new Address("Unknown");
|
|
||||||
|
|
||||||
private static final String TAG = Address.class.getSimpleName();
|
|
||||||
|
|
||||||
private final String address;
|
|
||||||
|
|
||||||
private Address(@NonNull String address) {
|
|
||||||
if (address == null) throw new AssertionError(address);
|
|
||||||
this.address = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Address(Parcel in) {
|
|
||||||
this(in.readString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NonNull Address fromSerialized(@NonNull String serialized) {
|
|
||||||
return new Address(serialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroup() {
|
|
||||||
return GroupUtil.isEncodedGroup(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMmsGroup() {
|
|
||||||
return GroupUtil.isMmsGroup(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmail() {
|
|
||||||
return NumberUtil.isValidEmail(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPhone() {
|
|
||||||
return !isGroup() && !isEmail();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull String toGroupString() {
|
|
||||||
if (!isGroup()) throw new AssertionError("Not group");
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull String toPhoneString() {
|
|
||||||
if (!isPhone()) {
|
|
||||||
if (isEmail()) throw new AssertionError("Not e164, is email");
|
|
||||||
if (isGroup()) throw new AssertionError("Not e164, is group");
|
|
||||||
throw new AssertionError("Not e164, unknown");
|
|
||||||
}
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull String toEmailString() {
|
|
||||||
if (!isEmail()) throw new AssertionError("Not email");
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String toString() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String serialize() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (this == other) return true;
|
|
||||||
if (other == null || !(other instanceof Address)) return false;
|
|
||||||
return address.equals(((Address) other).address);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return address.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeString(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(@NonNull Address other) {
|
|
||||||
return address.compareTo(other.address);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1014,8 +1014,8 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
|
|
||||||
long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), contentValues, insertListener);
|
long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), contentValues, insertListener);
|
||||||
|
|
||||||
if (message.getRecipient().requireAddress().isGroup()) {
|
if (message.getRecipient().isGroup()) {
|
||||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireAddress().toGroupString(), false);
|
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), false);
|
||||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||||
|
|
||||||
receiptDatabase.insert(Stream.of(members).map(Recipient::getId).toList(),
|
receiptDatabase.insert(Stream.of(members).map(Recipient::getId).toList(),
|
||||||
|
|
|
@ -12,6 +12,8 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
import org.whispersystems.signalservice.internal.util.Util;
|
import org.whispersystems.signalservice.internal.util.Util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -23,7 +25,8 @@ public class PushDatabase extends Database {
|
||||||
private static final String TABLE_NAME = "push";
|
private static final String TABLE_NAME = "push";
|
||||||
public static final String ID = "_id";
|
public static final String ID = "_id";
|
||||||
public static final String TYPE = "type";
|
public static final String TYPE = "type";
|
||||||
public static final String SOURCE = "source";
|
public static final String SOURCE_E164 = "source";
|
||||||
|
public static final String SOURCE_UUID = "source_uuid";
|
||||||
public static final String DEVICE_ID = "device_id";
|
public static final String DEVICE_ID = "device_id";
|
||||||
public static final String LEGACY_MSG = "body";
|
public static final String LEGACY_MSG = "body";
|
||||||
public static final String CONTENT = "content";
|
public static final String CONTENT = "content";
|
||||||
|
@ -32,7 +35,7 @@ public class PushDatabase extends Database {
|
||||||
public static final String SERVER_GUID = "server_guid";
|
public static final String SERVER_GUID = "server_guid";
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
|
TYPE + " INTEGER, " + SOURCE_E164 + " TEXT, " + SOURCE_UUID + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
|
||||||
SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);";
|
SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);";
|
||||||
|
|
||||||
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||||
|
@ -47,7 +50,8 @@ public class PushDatabase extends Database {
|
||||||
} else {
|
} else {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(TYPE, envelope.getType());
|
values.put(TYPE, envelope.getType());
|
||||||
values.put(SOURCE, envelope.getSource());
|
values.put(SOURCE_UUID, envelope.getSourceUuid().orNull());
|
||||||
|
values.put(SOURCE_E164, envelope.getSourceE164().orNull());
|
||||||
values.put(DEVICE_ID, envelope.getSourceDevice());
|
values.put(DEVICE_ID, envelope.getSourceDevice());
|
||||||
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
|
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
|
||||||
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
|
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
|
||||||
|
@ -68,11 +72,14 @@ public class PushDatabase extends Database {
|
||||||
null, null, null);
|
null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
||||||
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
||||||
|
String uuid = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_UUID));
|
||||||
|
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_E164));
|
||||||
|
SignalServiceAddress address = new SignalServiceAddress(UuidUtil.parseOrNull(uuid), e164);
|
||||||
|
|
||||||
return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
|
return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
|
address,
|
||||||
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
|
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
||||||
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
|
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
|
||||||
|
@ -105,27 +112,30 @@ public class PushDatabase extends Database {
|
||||||
|
|
||||||
private Optional<Long> find(SignalServiceEnvelope envelope) {
|
private Optional<Long> find(SignalServiceEnvelope envelope) {
|
||||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
String query = TYPE + " = ? AND " +
|
||||||
|
DEVICE_ID + " = ? AND " +
|
||||||
|
LEGACY_MSG + " = ? AND " +
|
||||||
|
CONTENT + " = ? AND " +
|
||||||
|
TIMESTAMP + " = ? AND " +
|
||||||
|
"(" +
|
||||||
|
"(" + SOURCE_E164 + " NOT NULL AND " + SOURCE_E164 + " = ?) OR " +
|
||||||
|
"(" + SOURCE_UUID + " NOT NULL AND " + SOURCE_UUID + " = ?)" +
|
||||||
|
")";
|
||||||
|
String[] args = new String[] { String.valueOf(envelope.getType()),
|
||||||
|
String.valueOf(envelope.getSourceDevice()),
|
||||||
|
envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "",
|
||||||
|
envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "",
|
||||||
|
String.valueOf(envelope.getTimestamp()),
|
||||||
|
String.valueOf(envelope.getSourceUuid().orNull()),
|
||||||
|
String.valueOf(envelope.getSourceE164().orNull()) };
|
||||||
|
|
||||||
try {
|
|
||||||
cursor = database.query(TABLE_NAME, null, TYPE + " = ? AND " + SOURCE + " = ? AND " +
|
|
||||||
DEVICE_ID + " = ? AND " + LEGACY_MSG + " = ? AND " +
|
|
||||||
CONTENT + " = ? AND " + TIMESTAMP + " = ?" ,
|
|
||||||
new String[] {String.valueOf(envelope.getType()),
|
|
||||||
envelope.getSource(),
|
|
||||||
String.valueOf(envelope.getSourceDevice()),
|
|
||||||
envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "",
|
|
||||||
envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "",
|
|
||||||
String.valueOf(envelope.getTimestamp())},
|
|
||||||
null, null, null);
|
|
||||||
|
|
||||||
|
try (Cursor cursor = database.query(TABLE_NAME, null, query, args, null, null, null)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
return Optional.of(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
|
return Optional.of(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
|
||||||
} else {
|
} else {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
if (cursor != null) cursor.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +152,8 @@ public class PushDatabase extends Database {
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
||||||
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
|
String sourceUuid = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_UUID));
|
||||||
|
String sourceE164 = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE_E164));
|
||||||
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
|
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
|
||||||
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
||||||
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
||||||
|
@ -150,10 +161,14 @@ public class PushDatabase extends Database {
|
||||||
long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP));
|
long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP));
|
||||||
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
|
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
|
||||||
|
|
||||||
return new SignalServiceEnvelope(type, source, deviceId, timestamp,
|
return new SignalServiceEnvelope(type,
|
||||||
|
new SignalServiceAddress(UuidUtil.parseOrNull(sourceUuid), sourceE164),
|
||||||
|
deviceId,
|
||||||
|
timestamp,
|
||||||
legacyMessage != null ? Base64.decode(legacyMessage) : null,
|
legacyMessage != null ? Base64.decode(legacyMessage) : null,
|
||||||
content != null ? Base64.decode(content) : null,
|
content != null ? Base64.decode(content) : null,
|
||||||
serverTimestamp, serverGuid);
|
serverTimestamp,
|
||||||
|
serverGuid);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,18 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class RecipientDatabase extends Database {
|
public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
|
@ -64,6 +67,7 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String PROFILE_SHARING = "profile_sharing";
|
private static final String PROFILE_SHARING = "profile_sharing";
|
||||||
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
|
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
|
||||||
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
|
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
|
||||||
|
private static final String UUID_SUPPORTED = "uuid_supported";
|
||||||
|
|
||||||
private static final String SORT_NAME = "sort_name";
|
private static final String SORT_NAME = "sort_name";
|
||||||
|
|
||||||
|
@ -73,23 +77,14 @@ public class RecipientDatabase extends Database {
|
||||||
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, 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,
|
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
|
||||||
UNIDENTIFIED_ACCESS_MODE,
|
UNIDENTIFIED_ACCESS_MODE,
|
||||||
FORCE_SMS_SELECTION,
|
FORCE_SMS_SELECTION, UUID_SUPPORTED
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String[] ID_PROJECTION = new String[] { ID };
|
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};
|
||||||
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 };
|
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||||
|
.map(columnName -> TABLE_NAME + "." + columnName)
|
||||||
private static Address addressFromCursor(Cursor cursor) {
|
.toList();
|
||||||
String phone = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
|
|
||||||
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
|
|
||||||
String groupId = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
|
|
||||||
return phone != null ? Address.fromSerialized(phone) : email != null ? Address.fromSerialized(email) : Address.fromSerialized(groupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
|
||||||
.map(columnName -> TABLE_NAME + "." + columnName)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
public enum VibrateState {
|
public enum VibrateState {
|
||||||
DEFAULT(0), ENABLED(1), DISABLED(2);
|
DEFAULT(0), ENABLED(1), DISABLED(2);
|
||||||
|
@ -173,12 +168,23 @@ public class RecipientDatabase extends Database {
|
||||||
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
|
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
|
||||||
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
|
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
|
||||||
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
|
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
|
||||||
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0);";
|
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0, " +
|
||||||
|
UUID_SUPPORTED + " INTEGER DEFAULT 0);";
|
||||||
|
|
||||||
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NonNull boolean containsPhoneOrUuid(@NonNull String id) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
String query = UUID + " = ? OR " + PHONE + " = ?";
|
||||||
|
String[] args = new String[]{id, id};
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID }, query, args, null, null, null)) {
|
||||||
|
return cursor != null && cursor.moveToFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public @NonNull Optional<RecipientId> getByE164(@NonNull String e164) {
|
public @NonNull Optional<RecipientId> getByE164(@NonNull String e164) {
|
||||||
return getByColumn(PHONE, e164);
|
return getByColumn(PHONE, e164);
|
||||||
}
|
}
|
||||||
|
@ -189,6 +195,15 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
public @NonNull Optional<RecipientId> getByGroupId(@NonNull String groupId) {
|
public @NonNull Optional<RecipientId> getByGroupId(@NonNull String groupId) {
|
||||||
return getByColumn(GROUP_ID, groupId);
|
return getByColumn(GROUP_ID, groupId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Optional<RecipientId> getByUuid(@NonNull UUID uuid) {
|
||||||
|
return getByColumn(UUID, uuid.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull RecipientId getOrInsertFromUuid(@NonNull UUID uuid) {
|
||||||
|
return getOrInsertByColumn(UUID, uuid.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull RecipientId getOrInsertFromE164(@NonNull String e164) {
|
public @NonNull RecipientId getOrInsertFromE164(@NonNull String e164) {
|
||||||
|
@ -238,7 +253,10 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
@NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
|
@NonNull RecipientSettings getRecipientSettings(@NonNull Cursor cursor) {
|
||||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
Address address = addressFromCursor(cursor);
|
UUID uuid = UuidUtil.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(UUID)));
|
||||||
|
String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
|
||||||
|
String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL));
|
||||||
|
String groupId = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
|
||||||
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
|
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1;
|
||||||
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE));
|
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE));
|
||||||
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
|
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
|
||||||
|
@ -261,6 +279,7 @@ public class RecipientDatabase extends Database {
|
||||||
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
||||||
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
|
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
|
||||||
boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1;
|
boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1;
|
||||||
|
boolean uuidSupported = cursor.getInt(cursor.getColumnIndexOrThrow(UUID_SUPPORTED)) == 1;
|
||||||
|
|
||||||
MaterialColor color;
|
MaterialColor color;
|
||||||
byte[] profileKey = null;
|
byte[] profileKey = null;
|
||||||
|
@ -281,7 +300,7 @@ public class RecipientDatabase extends Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RecipientSettings(RecipientId.from(id), address, blocked, muteUntil,
|
return new RecipientSettings(RecipientId.from(id), uuid, e164, email, groupId, blocked, muteUntil,
|
||||||
VibrateState.fromId(messageVibrateState),
|
VibrateState.fromId(messageVibrateState),
|
||||||
VibrateState.fromId(callVibrateState),
|
VibrateState.fromId(callVibrateState),
|
||||||
Util.uri(messageRingtone), Util.uri(callRingtone),
|
Util.uri(messageRingtone), Util.uri(callRingtone),
|
||||||
|
@ -292,7 +311,7 @@ public class RecipientDatabase extends Database {
|
||||||
systemPhoneLabel, systemContactUri,
|
systemPhoneLabel, systemContactUri,
|
||||||
signalProfileName, signalProfileAvatar, profileSharing,
|
signalProfileName, signalProfileAvatar, profileSharing,
|
||||||
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
|
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
|
||||||
forceSmsSelection);
|
forceSmsSelection, uuidSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BulkOperationsHandle resetAllSystemContactInfo() {
|
public BulkOperationsHandle resetAllSystemContactInfo() {
|
||||||
|
@ -394,6 +413,13 @@ public class RecipientDatabase extends Database {
|
||||||
Recipient.live(id).refresh();
|
Recipient.live(id).refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUuidSupported(@NonNull RecipientId id, boolean supported) {
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
values.put(UUID_SUPPORTED, supported ? "1" : "0");
|
||||||
|
update(id, values);
|
||||||
|
Recipient.live(id).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
public void setProfileKey(@NonNull RecipientId id, @Nullable byte[] profileKey) {
|
public void setProfileKey(@NonNull RecipientId id, @Nullable byte[] profileKey) {
|
||||||
ContentValues values = new ContentValues(1);
|
ContentValues values = new ContentValues(1);
|
||||||
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
||||||
|
@ -429,19 +455,84 @@ public class RecipientDatabase extends Database {
|
||||||
Recipient.live(id).refresh();
|
Recipient.live(id).refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Address> getAllAddresses() {
|
public void setPhoneNumber(@NonNull RecipientId id, @NonNull String e164) {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
ContentValues contentValues = new ContentValues(1);
|
||||||
Set<Address> results = new HashSet<>();
|
contentValues.put(PHONE, e164);
|
||||||
|
update(id, contentValues);
|
||||||
|
Recipient.live(id).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID, UUID, PHONE, EMAIL, GROUP_ID }, null, null, null, null, null)) {
|
public Set<String> getAllPhoneNumbers() {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
Set<String> results = new HashSet<>();
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query(TABLE_NAME, new String[] { PHONE }, null, null, null, null, null)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
results.add(addressFromCursor(cursor));
|
String number = cursor.getString(cursor.getColumnIndexOrThrow(PHONE));
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(number)) {
|
||||||
|
results.add(number);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markRegistered(@NonNull RecipientId id, @NonNull UUID uuid) {
|
||||||
|
ContentValues contentValues = new ContentValues(2);
|
||||||
|
contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
|
||||||
|
contentValues.put(UUID, uuid.toString().toLowerCase());
|
||||||
|
update(id, contentValues);
|
||||||
|
Recipient.live(id).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the user as registered without providing a UUID. This should only be used when one
|
||||||
|
* cannot be reasonably obtained. {@link #markRegistered(RecipientId, UUID)} should be strongly
|
||||||
|
* preferred.
|
||||||
|
*/
|
||||||
|
public void markRegistered(@NonNull RecipientId id) {
|
||||||
|
ContentValues contentValues = new ContentValues(2);
|
||||||
|
contentValues.put(REGISTERED, RegisteredState.REGISTERED.getId());
|
||||||
|
update(id, contentValues);
|
||||||
|
Recipient.live(id).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markUnregistered(@NonNull RecipientId id) {
|
||||||
|
ContentValues contentValues = new ContentValues(2);
|
||||||
|
contentValues.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId());
|
||||||
|
contentValues.put(UUID, (String) null);
|
||||||
|
update(id, contentValues);
|
||||||
|
Recipient.live(id).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bulkUpdatedRegisteredStatus(@NonNull Map<RecipientId, String> registered, Collection<RecipientId> unregistered) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (Map.Entry<RecipientId, String> entry : registered.entrySet()) {
|
||||||
|
ContentValues values = new ContentValues(2);
|
||||||
|
values.put(REGISTERED, RegisteredState.REGISTERED.getId());
|
||||||
|
values.put(UUID, entry.getValue().toLowerCase());
|
||||||
|
db.update(TABLE_NAME, values, ID_WHERE, new String[] { entry.getKey().serialize() });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RecipientId id : unregistered) {
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
values.put(REGISTERED, RegisteredState.NOT_REGISTERED.getId());
|
||||||
|
values.put(UUID, (String) null);
|
||||||
|
db.update(TABLE_NAME, values, ID_WHERE, new String[] { id.serialize() });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public void setRegistered(@NonNull RecipientId id, RegisteredState registeredState) {
|
public void setRegistered(@NonNull RecipientId id, RegisteredState registeredState) {
|
||||||
ContentValues contentValues = new ContentValues(1);
|
ContentValues contentValues = new ContentValues(1);
|
||||||
contentValues.put(REGISTERED, registeredState.getId());
|
contentValues.put(REGISTERED, registeredState.getId());
|
||||||
|
@ -449,8 +540,9 @@ public class RecipientDatabase extends Database {
|
||||||
Recipient.live(id).refresh();
|
Recipient.live(id).refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistered(@NonNull List<RecipientId> activeIds,
|
@Deprecated
|
||||||
@NonNull List<RecipientId> inactiveIds)
|
public void setRegistered(@NonNull Collection<RecipientId> activeIds,
|
||||||
|
@NonNull Collection<RecipientId> inactiveIds)
|
||||||
{
|
{
|
||||||
for (RecipientId activeId : activeIds) {
|
for (RecipientId activeId : activeIds) {
|
||||||
ContentValues contentValues = new ContentValues(1);
|
ContentValues contentValues = new ContentValues(1);
|
||||||
|
@ -673,7 +765,10 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
public static class RecipientSettings {
|
public static class RecipientSettings {
|
||||||
private final RecipientId id;
|
private final RecipientId id;
|
||||||
private final Address address;
|
private final UUID uuid;
|
||||||
|
private final String e164;
|
||||||
|
private final String email;
|
||||||
|
private final String groupId;
|
||||||
private final boolean blocked;
|
private final boolean blocked;
|
||||||
private final long muteUntil;
|
private final long muteUntil;
|
||||||
private final VibrateState messageVibrateState;
|
private final VibrateState messageVibrateState;
|
||||||
|
@ -696,9 +791,14 @@ public class RecipientDatabase extends Database {
|
||||||
private final String notificationChannel;
|
private final String notificationChannel;
|
||||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
private final boolean forceSmsSelection;
|
private final boolean forceSmsSelection;
|
||||||
|
private final boolean uuidSupported;
|
||||||
|
|
||||||
RecipientSettings(@NonNull RecipientId id,
|
RecipientSettings(@NonNull RecipientId id,
|
||||||
@NonNull Address address, boolean blocked, long muteUntil,
|
@Nullable UUID uuid,
|
||||||
|
@Nullable String e164,
|
||||||
|
@Nullable String email,
|
||||||
|
@Nullable String groupId,
|
||||||
|
boolean blocked, long muteUntil,
|
||||||
@NonNull VibrateState messageVibrateState,
|
@NonNull VibrateState messageVibrateState,
|
||||||
@NonNull VibrateState callVibrateState,
|
@NonNull VibrateState callVibrateState,
|
||||||
@Nullable Uri messageRingtone,
|
@Nullable Uri messageRingtone,
|
||||||
|
@ -718,10 +818,14 @@ public class RecipientDatabase extends Database {
|
||||||
boolean profileSharing,
|
boolean profileSharing,
|
||||||
@Nullable String notificationChannel,
|
@Nullable String notificationChannel,
|
||||||
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
|
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
|
||||||
boolean forceSmsSelection)
|
boolean forceSmsSelection,
|
||||||
|
boolean uuidSupported)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.address = address;
|
this.uuid = uuid;
|
||||||
|
this.e164 = e164;
|
||||||
|
this.email = email;
|
||||||
|
this.groupId = groupId;
|
||||||
this.blocked = blocked;
|
this.blocked = blocked;
|
||||||
this.muteUntil = muteUntil;
|
this.muteUntil = muteUntil;
|
||||||
this.messageVibrateState = messageVibrateState;
|
this.messageVibrateState = messageVibrateState;
|
||||||
|
@ -744,14 +848,27 @@ public class RecipientDatabase extends Database {
|
||||||
this.notificationChannel = notificationChannel;
|
this.notificationChannel = notificationChannel;
|
||||||
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
||||||
this.forceSmsSelection = forceSmsSelection;
|
this.forceSmsSelection = forceSmsSelection;
|
||||||
|
this.uuidSupported = uuidSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipientId getId() {
|
public RecipientId getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull Address getAddress() {
|
public @Nullable UUID getUuid() {
|
||||||
return address;
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getE164() {
|
||||||
|
return e164;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getGroupId() {
|
||||||
|
return groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable MaterialColor getColor() {
|
public @Nullable MaterialColor getColor() {
|
||||||
|
@ -841,6 +958,10 @@ public class RecipientDatabase extends Database {
|
||||||
public boolean isForceSmsSelection() {
|
public boolean isForceSmsSelection() {
|
||||||
return forceSmsSelection;
|
return forceSmsSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUuidSupported() {
|
||||||
|
return uuidSupported;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RecipientReader implements Closeable {
|
public static class RecipientReader implements Closeable {
|
||||||
|
|
|
@ -570,7 +570,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||||
if (message.getGroupId() == null) {
|
if (message.getGroupId() == null) {
|
||||||
groupRecipient = null;
|
groupRecipient = null;
|
||||||
} else {
|
} else {
|
||||||
RecipientId id = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(message.getGroupId().serialize());
|
RecipientId id = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(message.getGroupId());
|
||||||
groupRecipient = Recipient.resolved(id);
|
groupRecipient = Recipient.resolved(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,8 @@ import net.sqlcipher.database.SQLiteDatabase;
|
||||||
import net.sqlcipher.database.SQLiteStatement;
|
import net.sqlcipher.database.SQLiteStatement;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import androidx.annotation.NonNull;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
@ -39,8 +38,10 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
|
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.Hex;
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
|
@ -1269,10 +1270,10 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
|
||||||
if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) {
|
if (Permissions.hasAny(context, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) {
|
||||||
try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) {
|
try (Cursor cursor = db.query("recipient_preferences", null, null, null, null, null, null)) {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids")));
|
String address = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
|
||||||
|
|
||||||
if (address.isPhone() && !TextUtils.isEmpty(address.toPhoneString())) {
|
if (!TextUtils.isEmpty(address) && !GroupUtil.isEncodedGroup(address) && !NumberUtil.isValidEmail(address)) {
|
||||||
Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address.toPhoneString()));
|
Uri lookup = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address));
|
||||||
|
|
||||||
try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME,
|
try (Cursor contactCursor = context.getContentResolver().query(lookup, new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME,
|
||||||
ContactsContract.PhoneLookup.LOOKUP_KEY,
|
ContactsContract.PhoneLookup.LOOKUP_KEY,
|
||||||
|
@ -1288,7 +1289,7 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
|
||||||
contentValues.put("system_phone_label", contactCursor.getString(4));
|
contentValues.put("system_phone_label", contactCursor.getString(4));
|
||||||
contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString());
|
contentValues.put("system_contact_uri", ContactsContract.Contacts.getLookupUri(contactCursor.getLong(2), contactCursor.getString(1)).toString());
|
||||||
|
|
||||||
db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address.toPhoneString()});
|
db.update("recipient_preferences", contentValues, "recipient_ids = ?", new String[] {address});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
@ -90,12 +91,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
private static final int ATTACHMENT_TRANSFORM_PROPERTIES = 32;
|
private static final int ATTACHMENT_TRANSFORM_PROPERTIES = 32;
|
||||||
private static final int ATTACHMENT_CLEAR_HASHES = 33;
|
private static final int ATTACHMENT_CLEAR_HASHES = 33;
|
||||||
private static final int ATTACHMENT_CLEAR_HASHES_2 = 34;
|
private static final int ATTACHMENT_CLEAR_HASHES_2 = 34;
|
||||||
|
private static final int UUIDS = 35;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 34;
|
private static final int DATABASE_VERSION = 35;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
private final DatabaseSecret databaseSecret;
|
private final DatabaseSecret databaseSecret;
|
||||||
|
|
||||||
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
|
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
|
||||||
|
@ -552,15 +553,15 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
for (NotificationChannel oldChannel : channels) {
|
for (NotificationChannel oldChannel : channels) {
|
||||||
notificationManager.deleteNotificationChannel(oldChannel.getId());
|
notificationManager.deleteNotificationChannel(oldChannel.getId());
|
||||||
|
|
||||||
int startIndex = "contact_".length();
|
int startIndex = "contact_".length();
|
||||||
int endIndex = oldChannel.getId().lastIndexOf("_");
|
int endIndex = oldChannel.getId().lastIndexOf("_");
|
||||||
String address = oldChannel.getId().substring(startIndex, endIndex);
|
String address = oldChannel.getId().substring(startIndex, endIndex);
|
||||||
|
|
||||||
String recipientId;
|
String recipientId;
|
||||||
|
|
||||||
try (Cursor cursor = db.query("recipient", new String[] { "_id" }, "phone = ? OR email = ? OR group_id = ?", new String[] { address, address, address}, null, null, null)) {
|
try (Cursor cursor = db.query("recipient", new String[] { "_id" }, "phone = ? OR email = ? OR group_id = ?", new String[] { address, address, address}, null, null, null)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
recipientId = cursor.getString(0);
|
recipientId = cursor.getString(cursor.getColumnIndexOrThrow("_id"));
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Couldn't find recipient for address: " + address);
|
Log.w(TAG, "Couldn't find recipient for address: " + address);
|
||||||
continue;
|
continue;
|
||||||
|
@ -614,6 +615,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
Glide.get(context).clearDiskCache();
|
Glide.get(context).clearDiskCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < UUIDS) {
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN uuid_supported INTEGER DEFAULT 0");
|
||||||
|
db.execSQL("ALTER TABLE push ADD COLUMN source_uuid TEXT DEFAULT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.SessionDatabase;
|
import org.thoughtcrime.securesms.database.SessionDatabase;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
import org.whispersystems.libsignal.state.SessionRecord;
|
import org.whispersystems.libsignal.state.SessionRecord;
|
||||||
|
@ -41,7 +40,7 @@ class SessionStoreMigrationHelper {
|
||||||
for (File sessionFile : sessionFiles) {
|
for (File sessionFile : sessionFiles) {
|
||||||
try {
|
try {
|
||||||
String[] parts = sessionFile.getName().split("[.]");
|
String[] parts = sessionFile.getName().split("[.]");
|
||||||
Address address = Address.fromSerialized(parts[0]);
|
String address = parts[0];
|
||||||
|
|
||||||
int deviceId;
|
int deviceId;
|
||||||
|
|
||||||
|
@ -79,7 +78,7 @@ class SessionStoreMigrationHelper {
|
||||||
|
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(SessionDatabase.RECIPIENT_ID, address.serialize());
|
contentValues.put(SessionDatabase.RECIPIENT_ID, address);
|
||||||
contentValues.put(SessionDatabase.DEVICE, deviceId);
|
contentValues.put(SessionDatabase.DEVICE, deviceId);
|
||||||
contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize());
|
contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize());
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import androidx.loader.content.AsyncTaskLoader;
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
|
@ -6,10 +6,8 @@ import android.database.MatrixCursor;
|
||||||
import android.database.MergeCursor;
|
import android.database.MergeCursor;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
|
@ -4,9 +4,7 @@ package org.thoughtcrime.securesms.database.model;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
public class Quote {
|
public class Quote {
|
||||||
|
|
|
@ -34,6 +34,8 @@ import org.whispersystems.signalservice.api.util.SleepTimer;
|
||||||
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
||||||
import org.whispersystems.signalservice.api.websocket.ConnectivityListener;
|
import org.whispersystems.signalservice.api.websocket.ConnectivityListener;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
|
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
|
||||||
*/
|
*/
|
||||||
|
@ -120,7 +122,12 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUser() {
|
public UUID getUuid() {
|
||||||
|
return TextSecurePreferences.getLocalUuid(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getE164() {
|
||||||
return TextSecurePreferences.getLocalNumber(context);
|
return TextSecurePreferences.getLocalNumber(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,6 @@ public class WebRtcViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String toString() {
|
public @NonNull String toString() {
|
||||||
return "[State: " + state + ", recipient: " + recipient.requireAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
|
return "[State: " + state + ", recipient: " + recipient.getId().serialize() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.graphics.Bitmap;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
|
@ -22,6 +23,7 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
|
@ -32,6 +34,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -90,8 +93,8 @@ public class GroupMessageProcessor {
|
||||||
List<RecipientId> members = new LinkedList<>();
|
List<RecipientId> members = new LinkedList<>();
|
||||||
|
|
||||||
if (group.getMembers().isPresent()) {
|
if (group.getMembers().isPresent()) {
|
||||||
for (String member : group.getMembers().get()) {
|
for (SignalServiceAddress member : group.getMembers().get()) {
|
||||||
members.add(Recipient.external(context, member).getId());
|
members.add(Recipient.externalPush(context, member).getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,8 +117,8 @@ public class GroupMessageProcessor {
|
||||||
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
|
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
|
||||||
Set<RecipientId> messageMembers = new HashSet<>();
|
Set<RecipientId> messageMembers = new HashSet<>();
|
||||||
|
|
||||||
for (String messageMember : group.getMembers().get()) {
|
for (SignalServiceAddress messageMember : group.getMembers().get()) {
|
||||||
messageMembers.add(Recipient.external(context, messageMember).getId());
|
messageMembers.add(Recipient.externalPush(context, messageMember).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<RecipientId> addedMembers = new HashSet<>(messageMembers);
|
Set<RecipientId> addedMembers = new HashSet<>(messageMembers);
|
||||||
|
@ -135,7 +138,13 @@ public class GroupMessageProcessor {
|
||||||
builder.clearMembers();
|
builder.clearMembers();
|
||||||
|
|
||||||
for (RecipientId addedMember : addedMembers) {
|
for (RecipientId addedMember : addedMembers) {
|
||||||
builder.addMembers(Recipient.resolved(addedMember).requireAddress().serialize());
|
Recipient recipient = Recipient.resolved(addedMember);
|
||||||
|
|
||||||
|
if (recipient.getE164().isPresent()) {
|
||||||
|
builder.addMembersE164(recipient.getE164().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addMembers(createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builder.clearMembers();
|
builder.clearMembers();
|
||||||
|
@ -164,7 +173,7 @@ public class GroupMessageProcessor {
|
||||||
@NonNull SignalServiceGroup group,
|
@NonNull SignalServiceGroup group,
|
||||||
@NonNull GroupRecord record)
|
@NonNull GroupRecord record)
|
||||||
{
|
{
|
||||||
Recipient sender = Recipient.external(context, content.getSender());
|
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||||
|
|
||||||
if (record.getMembers().contains(sender.getId())) {
|
if (record.getMembers().contains(sender.getId())) {
|
||||||
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), group.getGroupId()));
|
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), group.getGroupId()));
|
||||||
|
@ -186,8 +195,8 @@ public class GroupMessageProcessor {
|
||||||
GroupContext.Builder builder = createGroupContext(group);
|
GroupContext.Builder builder = createGroupContext(group);
|
||||||
builder.setType(GroupContext.Type.QUIT);
|
builder.setType(GroupContext.Type.QUIT);
|
||||||
|
|
||||||
if (members.contains(Recipient.external(context, content.getSender()).getId())) {
|
if (members.contains(Recipient.externalPush(context, content.getSender()).getId())) {
|
||||||
database.remove(id, Recipient.external(context, content.getSender()).getId());
|
database.remove(id, Recipient.externalPush(context, content.getSender()).getId());
|
||||||
if (outgoing) database.setActive(id, false);
|
if (outgoing) database.setActive(id, false);
|
||||||
|
|
||||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||||
|
@ -223,7 +232,7 @@ public class GroupMessageProcessor {
|
||||||
} else {
|
} else {
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||||
String body = Base64.encodeBytes(storage.toByteArray());
|
String body = Base64.encodeBytes(storage.toByteArray());
|
||||||
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
|
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
|
||||||
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
|
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
|
||||||
|
|
||||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||||
|
@ -258,10 +267,29 @@ public class GroupMessageProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.getMembers().isPresent()) {
|
if (group.getMembers().isPresent()) {
|
||||||
builder.addAllMembers(group.getMembers().get());
|
builder.addAllMembersE164(Stream.of(group.getMembers().get())
|
||||||
|
.filter(a -> a.getNumber().isPresent())
|
||||||
|
.map(a -> a.getNumber().get())
|
||||||
|
.toList());
|
||||||
|
builder.addAllMembers(Stream.of(group.getMembers().get())
|
||||||
|
.map(GroupMessageProcessor::createMember)
|
||||||
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GroupContext.Member createMember(SignalServiceAddress address) {
|
||||||
|
GroupContext.Member.Builder member = GroupContext.Member.newBuilder();
|
||||||
|
|
||||||
|
if (address.getUuid().isPresent()) {
|
||||||
|
member = member.setUuid(address.getUuid().get().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.getNumber().isPresent()) {
|
||||||
|
member = member.setE164(address.getNumber().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return member.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
|
@ -48,7 +49,7 @@ final class V1GroupManager {
|
||||||
final RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
final RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||||
final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||||
|
|
||||||
memberIds.add(Recipient.external(context, TextSecurePreferences.getLocalNumber(context)).getId());
|
memberIds.add(Recipient.self().getId());
|
||||||
groupDatabase.create(groupId, name, new LinkedList<>(memberIds), null, null);
|
groupDatabase.create(groupId, name, new LinkedList<>(memberIds), null, null);
|
||||||
|
|
||||||
if (!mms) {
|
if (!mms) {
|
||||||
|
@ -71,7 +72,7 @@ final class V1GroupManager {
|
||||||
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
||||||
|
|
||||||
memberAddresses.add(Recipient.external(context, TextSecurePreferences.getLocalNumber(context)).getId());
|
memberAddresses.add(Recipient.self().getId());
|
||||||
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
|
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
|
||||||
groupDatabase.updateTitle(groupId, name);
|
groupDatabase.updateTitle(groupId, name);
|
||||||
groupDatabase.updateAvatar(groupId, avatarBytes);
|
groupDatabase.updateAvatar(groupId, avatarBytes);
|
||||||
|
@ -97,16 +98,19 @@ final class V1GroupManager {
|
||||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||||
|
|
||||||
List<String> numbers = new LinkedList<>();
|
List<GroupContext.Member> uuidMembers = new LinkedList<>();
|
||||||
|
List<String> e164Members = new LinkedList<>();
|
||||||
|
|
||||||
for (RecipientId member : members) {
|
for (RecipientId member : members) {
|
||||||
numbers.add(Recipient.resolved(member).requireAddress().serialize());
|
Recipient recipient = Recipient.resolved(member);
|
||||||
|
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
|
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
|
||||||
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
|
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
|
||||||
.setType(GroupContext.Type.UPDATE)
|
.setType(GroupContext.Type.UPDATE)
|
||||||
.addAllMembers(numbers);
|
.addAllMembersE164(e164Members)
|
||||||
|
.addAllMembers(uuidMembers);
|
||||||
if (groupName != null) groupContextBuilder.setName(groupName);
|
if (groupName != null) groupContextBuilder.setName(groupName);
|
||||||
GroupContext groupContext = groupContextBuilder.build();
|
GroupContext groupContext = groupContextBuilder.build();
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class DirectoryRefreshJob extends BaseJob {
|
||||||
if (recipient == null) {
|
if (recipient == null) {
|
||||||
DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
|
DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
|
||||||
} else {
|
} else {
|
||||||
DirectoryHelper.refreshDirectoryFor(context, recipient);
|
DirectoryHelper.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
||||||
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
||||||
|
import org.thoughtcrime.securesms.migrations.UuidMigrationJob;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -88,6 +89,7 @@ public final class JobManagerFactories {
|
||||||
put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory());
|
put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory());
|
||||||
put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory());
|
put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory());
|
||||||
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
|
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
|
||||||
|
put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory());
|
||||||
|
|
||||||
// Dead jobs
|
// Dead jobs
|
||||||
put("PushContentReceiveJob", new FailingJob.Factory());
|
put("PushContentReceiveJob", new FailingJob.Factory());
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
import com.google.android.mms.pdu_alt.CharacterSets;
|
import com.google.android.mms.pdu_alt.CharacterSets;
|
||||||
import com.google.android.mms.pdu_alt.EncodedStringValue;
|
import com.google.android.mms.pdu_alt.EncodedStringValue;
|
||||||
import com.google.android.mms.pdu_alt.PduBody;
|
import com.google.android.mms.pdu_alt.PduBody;
|
||||||
|
@ -13,8 +12,6 @@ import com.google.android.mms.pdu_alt.RetrieveConf;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||||
import org.thoughtcrime.securesms.blurhash.BlurHash;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||||
|
@ -35,10 +32,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.DuplicateMessageException;
|
|
||||||
import org.whispersystems.libsignal.InvalidMessageException;
|
|
||||||
import org.whispersystems.libsignal.LegacyMessageException;
|
|
||||||
import org.whispersystems.libsignal.NoSessionException;
|
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
@ -14,7 +14,6 @@ import com.google.android.mms.pdu_alt.PduHeaders;
|
||||||
import com.google.android.mms.pdu_alt.PduParser;
|
import com.google.android.mms.pdu_alt.PduParser;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
|
@ -24,7 +24,6 @@ import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
|
@ -221,7 +220,6 @@ public final class MmsSendJob extends SendJob {
|
||||||
{
|
{
|
||||||
SendReq req = new SendReq();
|
SendReq req = new SendReq();
|
||||||
String lineNumber = getMyNumber(context);
|
String lineNumber = getMyNumber(context);
|
||||||
Address destination = message.getRecipient().requireAddress();
|
|
||||||
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
||||||
List<Attachment> scaledAttachments = message.getAttachments();
|
List<Attachment> scaledAttachments = message.getAttachments();
|
||||||
|
|
||||||
|
@ -231,18 +229,18 @@ public final class MmsSendJob extends SendJob {
|
||||||
req.setFrom(new EncodedStringValue(TextSecurePreferences.getLocalNumber(context)));
|
req.setFrom(new EncodedStringValue(TextSecurePreferences.getLocalNumber(context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destination.isMmsGroup()) {
|
if (message.getRecipient().isMmsGroup()) {
|
||||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(destination.toGroupString(), false);
|
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), false);
|
||||||
|
|
||||||
for (Recipient member : members) {
|
for (Recipient member : members) {
|
||||||
if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
|
if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
|
||||||
req.addBcc(new EncodedStringValue(member.requireAddress().serialize()));
|
req.addBcc(new EncodedStringValue(member.requireSmsAddress()));
|
||||||
} else {
|
} else {
|
||||||
req.addTo(new EncodedStringValue(member.requireAddress().serialize()));
|
req.addTo(new EncodedStringValue(member.requireSmsAddress()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
req.addTo(new EncodedStringValue(destination.serialize()));
|
req.addTo(new EncodedStringValue(message.getRecipient().requireSmsAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
req.setDate(System.currentTimeMillis() / 1000);
|
req.setDate(System.currentTimeMillis() / 1000);
|
||||||
|
|
|
@ -12,12 +12,14 @@ import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -67,16 +69,16 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob {
|
||||||
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
|
||||||
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
|
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
|
||||||
List<String> blockedIndividuals = new LinkedList<>();
|
List<SignalServiceAddress> blockedIndividuals = new LinkedList<>();
|
||||||
List<byte[]> blockedGroups = new LinkedList<>();
|
List<byte[]> blockedGroups = new LinkedList<>();
|
||||||
|
|
||||||
Recipient recipient;
|
Recipient recipient;
|
||||||
|
|
||||||
while ((recipient = reader.getNext()) != null) {
|
while ((recipient = reader.getNext()) != null) {
|
||||||
if (recipient.isGroup()) {
|
if (recipient.isGroup()) {
|
||||||
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireAddress().toGroupString()));
|
blockedGroups.add(GroupUtil.getDecodedId(recipient.requireGroupId()));
|
||||||
} else {
|
} else {
|
||||||
blockedIndividuals.add(recipient.requireAddress().serialize());
|
blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -14,7 +13,6 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
@ -25,6 +23,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
@ -37,6 +36,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
|
||||||
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
|
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(recipient.getId());
|
||||||
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
|
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
|
||||||
|
|
||||||
out.write(new DeviceContact(recipient.requireAddress().toPhoneString(),
|
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient),
|
||||||
Optional.fromNullable(recipient.getName()),
|
Optional.fromNullable(recipient.getName()),
|
||||||
getAvatar(recipient.getContactUri()),
|
getAvatar(recipient.getContactUri()),
|
||||||
Optional.fromNullable(recipient.getColor().serialize()),
|
Optional.fromNullable(recipient.getColor().serialize()),
|
||||||
|
@ -189,12 +189,12 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
|
||||||
boolean blocked = recipient.isBlocked();
|
boolean blocked = recipient.isBlocked();
|
||||||
Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
|
Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
|
||||||
|
|
||||||
out.write(new DeviceContact(recipient.requireAddress().toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
|
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, recipient), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ProfileKeyUtil.hasProfileKey(context)) {
|
if (ProfileKeyUtil.hasProfileKey(context)) {
|
||||||
Recipient self = Recipient.self();
|
Recipient self = Recipient.self();
|
||||||
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
|
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, Recipient.self()),
|
||||||
Optional.absent(), Optional.absent(),
|
Optional.absent(), Optional.absent(),
|
||||||
Optional.of(self.getColor().serialize()), Optional.absent(),
|
Optional.of(self.getColor().serialize()), Optional.absent(),
|
||||||
Optional.of(ProfileKeyUtil.getProfileKey(context)),
|
Optional.of(ProfileKeyUtil.getProfileKey(context)),
|
||||||
|
@ -300,8 +300,8 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
|
||||||
private Optional<VerifiedMessage> getVerifiedMessage(Recipient recipient, Optional<IdentityDatabase.IdentityRecord> identity) throws InvalidNumberException {
|
private Optional<VerifiedMessage> getVerifiedMessage(Recipient recipient, Optional<IdentityDatabase.IdentityRecord> identity) throws InvalidNumberException {
|
||||||
if (!identity.isPresent()) return Optional.absent();
|
if (!identity.isPresent()) return Optional.absent();
|
||||||
|
|
||||||
String destination = recipient.requireAddress().toPhoneString();
|
SignalServiceAddress destination = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
IdentityKey identityKey = identity.get().getIdentityKey();
|
IdentityKey identityKey = identity.get().getIdentityKey();
|
||||||
|
|
||||||
VerifiedMessage.VerifiedState state;
|
VerifiedMessage.VerifiedState state;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
@ -23,6 +24,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -82,10 +84,10 @@ public class MultiDeviceGroupUpdateJob extends BaseJob {
|
||||||
|
|
||||||
while ((record = reader.getNext()) != null) {
|
while ((record = reader.getNext()) != null) {
|
||||||
if (!record.isMms()) {
|
if (!record.isMms()) {
|
||||||
List<String> members = new LinkedList<>();
|
List<SignalServiceAddress> members = new LinkedList<>();
|
||||||
|
|
||||||
for (RecipientId member : record.getMembers()) {
|
for (RecipientId member : record.getMembers()) {
|
||||||
members.add(Recipient.resolved(member).requireAddress().serialize());
|
members.add(RecipientUtil.toSignalServiceAddress(context, Recipient.resolved(member)));
|
||||||
}
|
}
|
||||||
|
|
||||||
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(record.getId(), record.isMms()));
|
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(record.getId(), record.isMms()));
|
||||||
|
|
|
@ -11,6 +11,8 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
|
@ -68,7 +70,7 @@ public class MultiDeviceProfileKeyUpdateJob extends BaseJob {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos);
|
DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos);
|
||||||
|
|
||||||
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
|
out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, Recipient.self()),
|
||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
|
|
|
@ -15,12 +15,14 @@ import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -89,7 +91,7 @@ public class MultiDeviceReadUpdateJob extends BaseJob {
|
||||||
|
|
||||||
for (SerializableSyncMessageId messageId : messageIds) {
|
for (SerializableSyncMessageId messageId : messageIds) {
|
||||||
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
|
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
|
||||||
readMessages.add(new ReadMessage(recipient.requireAddress().serialize(), messageId.timestamp));
|
readMessages.add(new ReadMessage(RecipientUtil.toSignalServiceAddress(context, recipient), messageId.timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
|
|
|
@ -10,9 +10,9 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
@ -22,6 +22,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -99,9 +100,9 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob {
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
Recipient recipient = Recipient.resolved(destination);
|
Recipient recipient = Recipient.resolved(destination);
|
||||||
Address canonicalDestination = recipient.requireAddress();
|
|
||||||
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
|
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
|
||||||
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
|
SignalServiceAddress verifiedAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
|
VerifiedMessage verifiedMessage = new VerifiedMessage(verifiedAddress, new IdentityKey(identityKey, 0), verifiedState, timestamp);
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
|
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
|
||||||
UnidentifiedAccessUtil.getAccessFor(context, recipient));
|
UnidentifiedAccessUtil.getAccessFor(context, recipient));
|
||||||
|
|
|
@ -13,12 +13,15 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -76,7 +79,7 @@ public class MultiDeviceViewOnceOpenJob extends BaseJob {
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
|
Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
|
||||||
ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(recipient.requireAddress().serialize(), messageId.timestamp);
|
ViewOnceOpenMessage openMessage = new ViewOnceOpenMessage(RecipientUtil.toSignalServiceAddress(context, recipient), messageId.timestamp);
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forViewOnceOpen(openMessage), UnidentifiedAccessUtil.getAccessForSync(context));
|
messageSender.sendMessage(SignalServiceSyncMessage.forViewOnceOpen(openMessage), UnidentifiedAccessUtil.getAccessForSync(context));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import android.util.Pair;
|
||||||
|
|
||||||
import com.annimon.stream.Collectors;
|
import com.annimon.stream.Collectors;
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.google.android.mms.APN;
|
|
||||||
|
|
||||||
import org.signal.libsignal.metadata.InvalidMetadataMessageException;
|
import org.signal.libsignal.metadata.InvalidMetadataMessageException;
|
||||||
import org.signal.libsignal.metadata.InvalidMetadataVersionException;
|
import org.signal.libsignal.metadata.InvalidMetadataVersionException;
|
||||||
|
@ -35,7 +34,6 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||||
import org.thoughtcrime.securesms.attachments.PointerAttachment;
|
import org.thoughtcrime.securesms.attachments.PointerAttachment;
|
||||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
|
import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
|
||||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||||
import org.thoughtcrime.securesms.blurhash.BlurHash;
|
|
||||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
|
@ -226,7 +224,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
try {
|
try {
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
SignalServiceAddress localAddress = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context)));
|
||||||
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, UnidentifiedAccessUtil.getCertificateValidator());
|
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, UnidentifiedAccessUtil.getCertificateValidator());
|
||||||
|
|
||||||
SignalServiceContent content = cipher.decrypt(envelope);
|
SignalServiceContent content = cipher.decrypt(envelope);
|
||||||
|
@ -290,7 +288,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Log.w(TAG, "Got unrecognized message...");
|
Log.w(TAG, "Got unrecognized message...");
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRecipientToPush(Recipient.external(context, content.getSender()));
|
resetRecipientToPush(Recipient.externalPush(context, content.getSender()));
|
||||||
|
|
||||||
if (envelope.isPreKeySignalMessage()) {
|
if (envelope.isPreKeySignalMessage()) {
|
||||||
ApplicationDependencies.getJobManager().add(new RefreshPreKeysJob());
|
ApplicationDependencies.getJobManager().add(new RefreshPreKeysJob());
|
||||||
|
@ -301,8 +299,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
} catch (ProtocolInvalidMessageException e) {
|
} catch (ProtocolInvalidMessageException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||||
}
|
} catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
||||||
catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||||
} catch (StorageFailedException e) {
|
} catch (StorageFailedException e) {
|
||||||
|
@ -340,7 +337,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||||
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
|
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp());
|
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp());
|
||||||
|
|
||||||
|
@ -356,7 +353,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||||
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
|
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
|
||||||
|
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
|
@ -370,7 +367,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||||
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
|
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
|
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
|
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
|
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
|
||||||
|
@ -390,7 +387,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||||
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
|
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
|
||||||
|
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
@ -402,7 +399,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||||
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
|
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.external(context, content.getSender()).getId());
|
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, Recipient.externalPush(context, content.getSender()).getId());
|
||||||
|
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
@ -411,7 +408,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
@NonNull Optional<Long> smsMessageId)
|
@NonNull Optional<Long> smsMessageId)
|
||||||
{
|
{
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(),
|
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
||||||
content.getSenderDevice(),
|
content.getSenderDevice(),
|
||||||
content.getTimestamp(),
|
content.getTimestamp(),
|
||||||
"", Optional.absent(), 0,
|
"", Optional.absent(), 0,
|
||||||
|
@ -432,7 +429,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
|
|
||||||
if (threadId != null) {
|
if (threadId != null) {
|
||||||
SessionStore sessionStore = new TextSecureSessionStore(context);
|
SessionStore sessionStore = new TextSecureSessionStore(context);
|
||||||
sessionStore.deleteAllSessions(content.getSender());
|
sessionStore.deleteAllSessions(content.getSender().getIdentifier());
|
||||||
|
|
||||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||||
MessageNotifier.updateNotification(context, threadId);
|
MessageNotifier.updateNotification(context, threadId);
|
||||||
|
@ -450,7 +447,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
|
|
||||||
if (!recipient.isGroup()) {
|
if (!recipient.isGroup()) {
|
||||||
SessionStore sessionStore = new TextSecureSessionStore(context);
|
SessionStore sessionStore = new TextSecureSessionStore(context);
|
||||||
sessionStore.deleteAllSessions(recipient.requireAddress().toPhoneString());
|
sessionStore.deleteAllSessions(recipient.requireServiceId());
|
||||||
|
|
||||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||||
|
|
||||||
|
@ -482,7 +479,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
|
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceGroup group)
|
@NonNull SignalServiceGroup group)
|
||||||
{
|
{
|
||||||
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(Recipient.external(context, content.getSender()).getId(), group.getGroupId()));
|
ApplicationDependencies.getJobManager().add(new RequestGroupInfoJob(Recipient.externalPush(context, content.getSender()).getId(), group.getGroupId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
|
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
|
||||||
|
@ -514,7 +511,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||||
}
|
}
|
||||||
} catch (MmsException e) {
|
} catch (MmsException e) {
|
||||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,11 +570,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.getMessage().getProfileKey().isPresent()) {
|
if (message.getMessage().getProfileKey().isPresent()) {
|
||||||
Recipient recipient = null;
|
Recipient recipient = getSyncMessageDestination(message);
|
||||||
|
|
||||||
if (message.getDestination().isPresent()) recipient = Recipient.external(context, message.getDestination().get());
|
|
||||||
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
|
|
||||||
|
|
||||||
|
|
||||||
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
|
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
|
||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
|
||||||
|
@ -591,7 +584,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
|
|
||||||
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
|
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
|
||||||
} catch (MmsException e) {
|
} catch (MmsException e) {
|
||||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,8 +615,8 @@ public class PushDecryptJob extends BaseJob {
|
||||||
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
|
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
|
||||||
{
|
{
|
||||||
for (ReadMessage readMessage : readMessages) {
|
for (ReadMessage readMessage : readMessages) {
|
||||||
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.external(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
|
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
|
||||||
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.external(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
|
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp);
|
||||||
|
|
||||||
for (Pair<Long, Long> expiringMessage : expiringText) {
|
for (Pair<Long, Long> expiringMessage : expiringText) {
|
||||||
ApplicationContext.getInstance(context)
|
ApplicationContext.getInstance(context)
|
||||||
|
@ -644,7 +637,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) {
|
private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) {
|
||||||
RecipientId author = Recipient.external(context, openMessage.getSender()).getId();
|
RecipientId author = Recipient.externalPush(context, openMessage.getSender()).getId();
|
||||||
long timestamp = openMessage.getTimestamp();
|
long timestamp = openMessage.getTimestamp();
|
||||||
MessageRecord record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(timestamp, author);
|
MessageRecord record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(timestamp, author);
|
||||||
|
|
||||||
|
@ -674,7 +667,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
||||||
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
|
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
|
||||||
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.external(context, content.getSender()).getId(),
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
||||||
message.getTimestamp(), -1,
|
message.getTimestamp(), -1,
|
||||||
message.getExpiresInSeconds() * 1000L, false,
|
message.getExpiresInSeconds() * 1000L, false,
|
||||||
message.isViewOnce(),
|
message.isViewOnce(),
|
||||||
|
@ -707,7 +700,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
database.setTransactionSuccessful();
|
database.setTransactionSuccessful();
|
||||||
}
|
}
|
||||||
} catch (MmsException e) {
|
} catch (MmsException e) {
|
||||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
|
||||||
} finally {
|
} finally {
|
||||||
database.endTransaction();
|
database.endTransaction();
|
||||||
}
|
}
|
||||||
|
@ -778,12 +771,13 @@ public class PushDecryptJob extends BaseJob {
|
||||||
try {
|
try {
|
||||||
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
|
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
|
||||||
|
|
||||||
if (recipients.requireAddress().isGroup()) {
|
if (recipients.isGroup()) {
|
||||||
updateGroupReceiptStatus(message, messageId, recipients.requireAddress().toGroupString());
|
updateGroupReceiptStatus(message, messageId, recipients.requireGroupId());
|
||||||
|
} else {
|
||||||
|
database.markUnidentified(messageId, message.isUnidentified(recipients.requireServiceId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
database.markAsSent(messageId, true);
|
||||||
database.markUnidentified(messageId, message.isUnidentified(recipients.requireAddress().serialize()));
|
|
||||||
|
|
||||||
List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId);
|
List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId);
|
||||||
List<DatabaseAttachment> stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList();
|
List<DatabaseAttachment> stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList();
|
||||||
|
@ -838,12 +832,12 @@ public class PushDecryptJob extends BaseJob {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGroupReceiptStatus(message, record.getId(), recipient.requireAddress().toGroupString());
|
updateGroupReceiptStatus(message, record.getId(), recipient.requireGroupId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGroupReceiptStatus(@NonNull SentTranscriptMessage message, long messageId, @NonNull String groupString) {
|
private void updateGroupReceiptStatus(@NonNull SentTranscriptMessage message, long messageId, @NonNull String groupString) {
|
||||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
||||||
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.external(context, address)).toList();
|
List<Recipient> messageRecipients = Stream.of(message.getRecipients()).map(address -> Recipient.externalPush(context, address)).toList();
|
||||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false);
|
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupString, false);
|
||||||
Map<RecipientId, Integer> localReceipts = Stream.of(receiptDatabase.getGroupReceiptInfo(messageId))
|
Map<RecipientId, Integer> localReceipts = Stream.of(receiptDatabase.getGroupReceiptInfo(messageId))
|
||||||
.collect(Collectors.toMap(GroupReceiptInfo::getRecipientId, GroupReceiptInfo::getStatus));
|
.collect(Collectors.toMap(GroupReceiptInfo::getRecipientId, GroupReceiptInfo::getStatus));
|
||||||
|
@ -858,7 +852,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Recipient member : members) {
|
for (Recipient member : members) {
|
||||||
receiptDatabase.setUnidentified(member.getId(), messageId, message.isUnidentified(member.requireAddress().serialize()));
|
receiptDatabase.setUnidentified(member.getId(), messageId, message.isUnidentified(member.requireServiceId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,7 +876,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
} else {
|
} else {
|
||||||
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
|
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
|
||||||
|
|
||||||
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, content.getSender()).getId(),
|
IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(),
|
||||||
content.getSenderDevice(),
|
content.getSenderDevice(),
|
||||||
message.getTimestamp(), body,
|
message.getTimestamp(), body,
|
||||||
message.getGroupInfo(),
|
message.getGroupInfo(),
|
||||||
|
@ -915,7 +909,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||||
boolean isGroup = recipient.requireAddress().isGroup();
|
boolean isGroup = recipient.isGroup();
|
||||||
|
|
||||||
MessagingDatabase database;
|
MessagingDatabase database;
|
||||||
long messageId;
|
long messageId;
|
||||||
|
@ -927,13 +921,13 @@ public class PushDecryptJob extends BaseJob {
|
||||||
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
|
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null);
|
||||||
database = DatabaseFactory.getMmsDatabase(context);
|
database = DatabaseFactory.getMmsDatabase(context);
|
||||||
|
|
||||||
updateGroupReceiptStatus(message, messageId, recipient.requireAddress().toGroupString());
|
updateGroupReceiptStatus(message, messageId, recipient.requireGroupId());
|
||||||
} else {
|
} else {
|
||||||
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
|
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
|
||||||
|
|
||||||
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
|
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
|
||||||
database = DatabaseFactory.getSmsDatabase(context);
|
database = DatabaseFactory.getSmsDatabase(context);
|
||||||
database.markUnidentified(messageId, message.isUnidentified(recipient.requireAddress().serialize()));
|
database.markUnidentified(messageId, message.isUnidentified(recipient.requireServiceId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
database.markAsSent(messageId, true);
|
||||||
|
@ -1025,7 +1019,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInvalidMessage(@NonNull String sender,
|
private void handleInvalidMessage(@NonNull SignalServiceAddress sender,
|
||||||
int senderDevice,
|
int senderDevice,
|
||||||
@NonNull Optional<SignalServiceGroup> group,
|
@NonNull Optional<SignalServiceGroup> group,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
|
@ -1034,7 +1028,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||||
|
|
||||||
if (!smsMessageId.isPresent()) {
|
if (!smsMessageId.isPresent()) {
|
||||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp, group);
|
Optional<InsertResult> insertResult = insertPlaceholder(sender.getIdentifier(), senderDevice, timestamp, group);
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
smsDatabase.markAsInvalidMessage(insertResult.get().getMessageId());
|
smsDatabase.markAsInvalidMessage(insertResult.get().getMessageId());
|
||||||
|
@ -1082,7 +1076,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
@NonNull SignalServiceDataMessage message)
|
@NonNull SignalServiceDataMessage message)
|
||||||
{
|
{
|
||||||
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
||||||
Recipient recipient = Recipient.external(context, content.getSender());
|
Recipient recipient = Recipient.externalPush(context, content.getSender());
|
||||||
|
|
||||||
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
|
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
|
||||||
database.setProfileKey(recipient.getId(), message.getProfileKey().get());
|
database.setProfileKey(recipient.getId(), message.getProfileKey().get());
|
||||||
|
@ -1094,7 +1088,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
|
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceDataMessage message)
|
@NonNull SignalServiceDataMessage message)
|
||||||
{
|
{
|
||||||
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(Recipient.external(context, content.getSender()).getId(), message.getTimestamp()));
|
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(Recipient.externalPush(context, content.getSender()).getId(), message.getTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
|
@ -1104,7 +1098,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
for (long timestamp : message.getTimestamps()) {
|
for (long timestamp : message.getTimestamps()) {
|
||||||
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
|
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
|
||||||
DatabaseFactory.getMmsSmsDatabase(context)
|
DatabaseFactory.getMmsSmsDatabase(context)
|
||||||
.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.external(context, content.getSender()).getId(), timestamp), System.currentTimeMillis());
|
.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.externalPush(context, content.getSender()).getId(), timestamp), System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,7 +1111,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
|
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
|
||||||
|
|
||||||
DatabaseFactory.getMmsSmsDatabase(context)
|
DatabaseFactory.getMmsSmsDatabase(context)
|
||||||
.incrementReadReceiptCount(new SyncMessageId(Recipient.external(context, content.getSender()).getId(), timestamp), content.getTimestamp());
|
.incrementReadReceiptCount(new SyncMessageId(Recipient.externalPush(context, content.getSender()).getId(), timestamp), content.getTimestamp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1129,7 +1123,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient author = Recipient.external(context, content.getSender());
|
Recipient author = Recipient.externalPush(context, content.getSender());
|
||||||
|
|
||||||
long threadId;
|
long threadId;
|
||||||
|
|
||||||
|
@ -1184,7 +1178,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
RecipientId author = Recipient.external(context, quote.get().getAuthor().getNumber()).getId();
|
RecipientId author = Recipient.externalPush(context, quote.get().getAuthor()).getId();
|
||||||
MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author);
|
MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author);
|
||||||
|
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
|
@ -1313,7 +1307,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
if (message.getMessage().getGroupInfo().isPresent()) {
|
if (message.getMessage().getGroupInfo().isPresent()) {
|
||||||
return Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
|
return Recipient.external(context, GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false));
|
||||||
} else {
|
} else {
|
||||||
return Recipient.external(context, message.getDestination().get());
|
return Recipient.externalPush(context, message.getDestination().get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1321,12 +1315,12 @@ public class PushDecryptJob extends BaseJob {
|
||||||
if (message.getGroupInfo().isPresent()) {
|
if (message.getGroupInfo().isPresent()) {
|
||||||
return Recipient.external(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false));
|
return Recipient.external(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false));
|
||||||
} else {
|
} else {
|
||||||
return Recipient.external(context, content.getSender());
|
return Recipient.externalPush(context, content.getSender());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
|
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull SignalServiceAddress sender, int device) {
|
||||||
Recipient author = Recipient.external(context, sender);
|
Recipient author = Recipient.externalPush(context, sender);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(conversationRecipient);
|
||||||
|
|
||||||
if (threadId > 0) {
|
if (threadId > 0) {
|
||||||
|
@ -1341,7 +1335,7 @@ public class PushDecryptJob extends BaseJob {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient sender = Recipient.external(context, content.getSender());
|
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||||
|
|
||||||
if (content.getDataMessage().isPresent()) {
|
if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
|
|
|
@ -11,7 +11,6 @@ import com.annimon.stream.Stream;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
|
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
|
||||||
|
@ -45,6 +44,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Qu
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -96,7 +96,7 @@ public class PushGroupSendJob extends PushSendJob {
|
||||||
throw new AssertionError("Not a group!");
|
throw new AssertionError("Not a group!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DatabaseFactory.getGroupDatabase(context).isActive(group.requireAddress().toGroupString())) {
|
if (!DatabaseFactory.getGroupDatabase(context).isActive(group.requireGroupId())) {
|
||||||
throw new MmsException("Inactive group!");
|
throw new MmsException("Inactive group!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,12 +152,12 @@ public class PushGroupSendJob extends PushSendJob {
|
||||||
|
|
||||||
if (filterRecipient != null) target = Collections.singletonList(Recipient.resolved(filterRecipient).getId());
|
if (filterRecipient != null) target = Collections.singletonList(Recipient.resolved(filterRecipient).getId());
|
||||||
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).toList();
|
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).toList();
|
||||||
else target = getGroupMessageRecipients(message.getRecipient().requireAddress().toGroupString(), messageId);
|
else target = getGroupMessageRecipients(message.getRecipient().requireGroupId(), messageId);
|
||||||
|
|
||||||
List<SendMessageResult> results = deliver(message, target);
|
List<SendMessageResult> results = deliver(message, target);
|
||||||
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Recipient.external(context, result.getAddress().getNumber()).getId())).toList();
|
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Recipient.externalPush(context, result.getAddress()).getId())).toList();
|
||||||
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Recipient.external(context, result.getAddress().getNumber()).getId(), result.getIdentityFailure().getIdentityKey())).toList();
|
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Recipient.externalPush(context, result.getAddress()).getId(), result.getIdentityFailure().getIdentityKey())).toList();
|
||||||
Set<RecipientId> successIds = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> result.getAddress().getNumber()).map(a -> Recipient.external(context, a).getId()).collect(Collectors.toSet());
|
Set<RecipientId> successIds = Stream.of(results).filter(result -> result.getSuccess() != null).map(SendMessageResult::getAddress).map(a -> Recipient.externalPush(context, a).getId()).collect(Collectors.toSet());
|
||||||
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
||||||
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
||||||
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
|
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
|
||||||
|
@ -181,7 +181,7 @@ public class PushGroupSendJob extends PushSendJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SendMessageResult success : successes) {
|
for (SendMessageResult success : successes) {
|
||||||
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Recipient.external(context, success.getAddress().getNumber()).getId(),
|
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Recipient.externalPush(context, success.getAddress()).getId(),
|
||||||
messageId,
|
messageId,
|
||||||
success.getSuccess().isUnidentified());
|
success.getSuccess().isUnidentified());
|
||||||
}
|
}
|
||||||
|
@ -231,33 +231,36 @@ public class PushGroupSendJob extends PushSendJob {
|
||||||
rotateSenderCertificateIfNecessary();
|
rotateSenderCertificateIfNecessary();
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
String groupId = message.getRecipient().requireAddress().toGroupString();
|
String groupId = message.getRecipient().requireGroupId();
|
||||||
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
|
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
|
||||||
Optional<Quote> quote = getQuoteFor(message);
|
Optional<Quote> quote = getQuoteFor(message);
|
||||||
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
|
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
|
||||||
List<SharedContact> sharedContacts = getSharedContactsFor(message);
|
List<SharedContact> sharedContacts = getSharedContactsFor(message);
|
||||||
List<Preview> previews = getPreviewsFor(message);
|
List<Preview> previews = getPreviewsFor(message);
|
||||||
List<SignalServiceAddress> addresses = Stream.of(destinations).map(Recipient::resolved).map(Recipient::requireAddress).map(this::getPushAddress).toList();
|
List<SignalServiceAddress> addresses = Stream.of(destinations).map(Recipient::resolved).map(this::getPushAddress).toList();
|
||||||
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
|
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
|
||||||
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
|
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
|
||||||
boolean isRecipientUpdate = destinations.size() != DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId).size();
|
boolean isRecipientUpdate = destinations.size() != DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId).size();
|
||||||
|
|
||||||
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
|
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations)
|
||||||
.map(address -> Recipient.external(context, address.getNumber()))
|
.map(Recipient::resolved)
|
||||||
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
|
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (message.isGroup()) {
|
if (message.isGroup()) {
|
||||||
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
|
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
|
||||||
GroupContext groupContext = groupMessage.getGroupContext();
|
GroupContext groupContext = groupMessage.getGroupContext();
|
||||||
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
|
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
|
||||||
SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
|
SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
|
||||||
SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), groupContext.getMembersList(), avatar);
|
List<SignalServiceAddress> members = Stream.of(groupContext.getMembersList())
|
||||||
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
|
.map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164()))
|
||||||
.withTimestamp(message.getSentTimeMillis())
|
.toList();
|
||||||
.withExpiration(message.getRecipient().getExpireMessages())
|
SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), members, avatar);
|
||||||
.asGroupMessage(group)
|
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
|
||||||
.build();
|
.withTimestamp(message.getSentTimeMillis())
|
||||||
|
.withExpiration(message.getRecipient().getExpireMessages())
|
||||||
|
.asGroupMessage(group)
|
||||||
|
.build();
|
||||||
|
|
||||||
return messageSender.sendMessage(addresses, unidentifiedAccess, isRecipientUpdate, groupDataMessage);
|
return messageSender.sendMessage(addresses, unidentifiedAccess, isRecipientUpdate, groupDataMessage);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
|
@ -25,6 +26,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||||
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -92,10 +95,11 @@ public class PushGroupUpdateJob extends BaseJob {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> members = new LinkedList<>();
|
List<SignalServiceAddress> members = new LinkedList<>();
|
||||||
|
|
||||||
for (RecipientId member : record.get().getMembers()) {
|
for (RecipientId member : record.get().getMembers()) {
|
||||||
members.add(Recipient.resolved(member).requireAddress().serialize());
|
Recipient recipient = Recipient.resolved(member);
|
||||||
|
members.add(RecipientUtil.toSignalServiceAddress(context, recipient));
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
|
SignalServiceGroup groupContext = SignalServiceGroup.newBuilder(Type.UPDATE)
|
||||||
|
@ -117,7 +121,7 @@ public class PushGroupUpdateJob extends BaseJob {
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
Recipient recipient = Recipient.resolved(source);
|
Recipient recipient = Recipient.resolved(source);
|
||||||
|
|
||||||
messageSender.sendMessage(new SignalServiceAddress(recipient.requireAddress().serialize()),
|
messageSender.sendMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
|
||||||
UnidentifiedAccessUtil.getAccessFor(context, recipient),
|
UnidentifiedAccessUtil.getAccessFor(context, recipient),
|
||||||
message);
|
message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import com.annimon.stream.Stream;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
|
@ -29,6 +28,7 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
|
@ -67,7 +67,7 @@ public class PushMediaSendJob extends PushSendJob {
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Recipient recipient) {
|
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Recipient recipient) {
|
||||||
try {
|
try {
|
||||||
if (!recipient.requireAddress().isPhone()) {
|
if (!recipient.hasServiceIdentifier()) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ public class PushMediaSendJob extends PushSendJob {
|
||||||
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
|
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
|
||||||
} catch (UntrustedIdentityException uie) {
|
} catch (UntrustedIdentityException uie) {
|
||||||
warn(TAG, "Failure", uie);
|
warn(TAG, "Failure", uie);
|
||||||
database.addMismatchedIdentity(messageId, Recipient.external(context, uie.getE164Number()).getId(), uie.getIdentityKey());
|
database.addMismatchedIdentity(messageId, Recipient.external(context, uie.getIdentifier()).getId(), uie.getIdentityKey());
|
||||||
database.markAsSentFailed(messageId);
|
database.markAsSentFailed(messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,19 +189,11 @@ public class PushMediaSendJob extends PushSendJob {
|
||||||
throw new UndeliverableMessageException("No destination address.");
|
throw new UndeliverableMessageException("No destination address.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final Address destination = message.getRecipient().requireAddress();
|
|
||||||
|
|
||||||
if (!destination.isPhone()) {
|
|
||||||
if (destination.isEmail()) throw new UndeliverableMessageException("Not e164, is email");
|
|
||||||
if (destination.isGroup()) throw new UndeliverableMessageException("Not e164, is group");
|
|
||||||
throw new UndeliverableMessageException("Not e164, unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rotateSenderCertificateIfNecessary();
|
rotateSenderCertificateIfNecessary();
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
SignalServiceAddress address = getPushAddress(destination);
|
SignalServiceAddress address = getPushAddress(message.getRecipient());
|
||||||
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
|
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
|
||||||
List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments);
|
List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments);
|
||||||
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
|
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
|
||||||
|
@ -223,7 +215,7 @@ public class PushMediaSendJob extends PushSendJob {
|
||||||
.asExpirationUpdate(message.isExpirationUpdate())
|
.asExpirationUpdate(message.isExpirationUpdate())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if (address.getNumber().equals(TextSecurePreferences.getLocalNumber(context))) {
|
if (Util.equals(TextSecurePreferences.getLocalUuid(context), address.getUuid().orNull())) {
|
||||||
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
|
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
|
||||||
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess);
|
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess);
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,6 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
|
||||||
|
|
||||||
public abstract class PushReceivedJob extends BaseJob {
|
public abstract class PushReceivedJob extends BaseJob {
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.blurhash.BlurHash;
|
||||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||||
|
@ -33,6 +32,7 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
@ -106,9 +106,8 @@ public abstract class PushSendJob extends SendJob {
|
||||||
return Optional.of(ProfileKeyUtil.getProfileKey(context));
|
return Optional.of(ProfileKeyUtil.getProfileKey(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SignalServiceAddress getPushAddress(Address address) {
|
protected SignalServiceAddress getPushAddress(@NonNull Recipient recipient) {
|
||||||
String relay = null;
|
return RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<SignalServiceAttachment> getAttachmentsFor(List<Attachment> parts) {
|
protected List<SignalServiceAttachment> getAttachmentsFor(List<Attachment> parts) {
|
||||||
|
@ -256,7 +255,9 @@ public abstract class PushSendJob extends SendJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.of(new SignalServiceDataMessage.Quote(quoteId, new SignalServiceAddress(Recipient.resolved(quoteAuthor).requireAddress().serialize()), quoteBody, quoteAttachments));
|
Recipient quoteAuthorRecipient = Recipient.resolved(quoteAuthor);
|
||||||
|
SignalServiceAddress quoteAddress = RecipientUtil.toSignalServiceAddress(context, quoteAuthorRecipient);
|
||||||
|
return Optional.of(new SignalServiceDataMessage.Quote(quoteId, quoteAddress, quoteBody, quoteAttachments));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Optional<SignalServiceDataMessage.Sticker> getStickerFor(OutgoingMediaMessage message) {
|
protected Optional<SignalServiceDataMessage.Sticker> getStickerFor(OutgoingMediaMessage message) {
|
||||||
|
@ -329,13 +330,13 @@ public abstract class PushSendJob extends SendJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional<UnidentifiedAccessPair> syncAccess) {
|
protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional<UnidentifiedAccessPair> syncAccess) {
|
||||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalUuid(context), TextSecurePreferences.getLocalNumber(context));
|
||||||
SentTranscriptMessage transcript = new SentTranscriptMessage(localNumber,
|
SentTranscriptMessage transcript = new SentTranscriptMessage(localAddress,
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
message,
|
message,
|
||||||
message.getExpiresInSeconds(),
|
message.getExpiresInSeconds(),
|
||||||
Collections.singletonMap(localNumber, syncAccess.isPresent()),
|
Collections.singletonMap(localAddress, syncAccess.isPresent()),
|
||||||
false);
|
false);
|
||||||
return SignalServiceSyncMessage.forSentTranscript(transcript);
|
return SignalServiceSyncMessage.forSentTranscript(transcript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessM
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
|
@ -21,6 +20,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
|
@ -122,7 +122,7 @@ public class PushTextSendJob extends PushSendJob {
|
||||||
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
|
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
|
||||||
} catch (UntrustedIdentityException e) {
|
} catch (UntrustedIdentityException e) {
|
||||||
warn(TAG, "Failure", e);
|
warn(TAG, "Failure", e);
|
||||||
database.addMismatchedIdentity(record.getId(), Recipient.external(context, e.getE164Number()).getId(), e.getIdentityKey());
|
database.addMismatchedIdentity(record.getId(), Recipient.external(context, e.getIdentifier()).getId(), e.getIdentityKey());
|
||||||
database.markAsSentFailed(record.getId());
|
database.markAsSentFailed(record.getId());
|
||||||
database.markAsPush(record.getId());
|
database.markAsPush(record.getId());
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ public class PushTextSendJob extends PushSendJob {
|
||||||
rotateSenderCertificateIfNecessary();
|
rotateSenderCertificateIfNecessary();
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().requireAddress());
|
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient());
|
||||||
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
|
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
|
||||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, message.getIndividualRecipient());
|
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, message.getIndividualRecipient());
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ public class PushTextSendJob extends PushSendJob {
|
||||||
.asEndSessionMessage(message.isEndSession())
|
.asEndSessionMessage(message.isEndSession())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if (address.getNumber().equals(TextSecurePreferences.getLocalNumber(context))) {
|
if (Util.equals(TextSecurePreferences.getLocalUuid(context), address.getUuid().orNull())) {
|
||||||
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
|
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
|
||||||
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);
|
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,8 @@ public class RefreshUnidentifiedDeliveryAbilityJob extends BaseJob {
|
||||||
@Override
|
@Override
|
||||||
public void onRun() throws Exception {
|
public void onRun() throws Exception {
|
||||||
byte[] profileKey = ProfileKeyUtil.getProfileKey(context);
|
byte[] profileKey = ProfileKeyUtil.getProfileKey(context);
|
||||||
SignalServiceProfile profile = retrieveProfile(TextSecurePreferences.getLocalNumber(context));
|
SignalServiceAddress address = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context)));
|
||||||
|
SignalServiceProfile profile = retrieveProfile(address);
|
||||||
|
|
||||||
boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess());
|
boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess());
|
||||||
|
|
||||||
|
@ -68,19 +69,19 @@ public class RefreshUnidentifiedDeliveryAbilityJob extends BaseJob {
|
||||||
return exception instanceof PushNetworkException;
|
return exception instanceof PushNetworkException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
|
private SignalServiceProfile retrieveProfile(@NonNull SignalServiceAddress address) throws IOException {
|
||||||
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
||||||
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
|
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
|
||||||
|
|
||||||
if (pipe != null) {
|
if (pipe != null) {
|
||||||
try {
|
try {
|
||||||
return pipe.getProfile(new SignalServiceAddress(number), Optional.absent());
|
return pipe.getProfile(address, Optional.absent());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return receiver.retrieveProfile(new SignalServiceAddress(number), Optional.absent());
|
return receiver.retrieveProfile(address, Optional.absent());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) {
|
private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) {
|
||||||
|
|
|
@ -3,14 +3,15 @@ package org.thoughtcrime.securesms.jobs;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
|
@ -79,7 +80,7 @@ public class RequestGroupInfoJob extends BaseJob {
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
Recipient recipient = Recipient.resolved(source);
|
Recipient recipient = Recipient.resolved(source);
|
||||||
|
|
||||||
messageSender.sendMessage(new SignalServiceAddress(recipient.requireAddress().serialize()),
|
messageSender.sendMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
|
||||||
UnidentifiedAccessUtil.getAccessFor(context, recipient),
|
UnidentifiedAccessUtil.getAccessFor(context, recipient),
|
||||||
message);
|
message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(profileAvatar)) {
|
if (TextUtils.isEmpty(profileAvatar)) {
|
||||||
Log.w(TAG, "Removing profile avatar (no url) for: " + recipient.requireAddress().serialize());
|
Log.w(TAG, "Removing profile avatar (no url) for: " + recipient.getId().serialize());
|
||||||
AvatarHelper.delete(context, recipient.getId());
|
AvatarHelper.delete(context, recipient.getId());
|
||||||
database.setProfileAvatar(recipient.getId(), profileAvatar);
|
database.setProfileAvatar(recipient.getId(), profileAvatar);
|
||||||
return;
|
return;
|
||||||
|
@ -107,7 +107,7 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
||||||
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getId()));
|
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getId()));
|
||||||
} catch (PushNetworkException e) {
|
} catch (PushNetworkException e) {
|
||||||
if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
|
if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
|
||||||
Log.w(TAG, "Removing profile avatar (no image available) for: " + recipient.requireAddress().serialize());
|
Log.w(TAG, "Removing profile avatar (no image available) for: " + recipient.getId().serialize());
|
||||||
AvatarHelper.delete(context, recipient.getId());
|
AvatarHelper.delete(context, recipient.getId());
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
|
@ -16,6 +18,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
|
@ -86,21 +89,21 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
public void onCanceled() {}
|
public void onCanceled() {}
|
||||||
|
|
||||||
private void handleIndividualRecipient(Recipient recipient) throws IOException {
|
private void handleIndividualRecipient(Recipient recipient) throws IOException {
|
||||||
if (recipient.requireAddress().isPhone()) handlePhoneNumberRecipient(recipient);
|
if (recipient.hasServiceIdentifier()) handlePhoneNumberRecipient(recipient);
|
||||||
else Log.w(TAG, "Skipping fetching profile of non-phone recipient");
|
else Log.w(TAG, "Skipping fetching profile of non-Signal recipient");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePhoneNumberRecipient(Recipient recipient) throws IOException {
|
private void handlePhoneNumberRecipient(Recipient recipient) throws IOException {
|
||||||
String number = recipient.requireAddress().toPhoneString();
|
SignalServiceAddress address = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(recipient);
|
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(recipient);
|
||||||
|
|
||||||
SignalServiceProfile profile;
|
SignalServiceProfile profile;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
profile = retrieveProfile(number, unidentifiedAccess);
|
profile = retrieveProfile(address, unidentifiedAccess);
|
||||||
} catch (NonSuccessfulResponseCodeException e) {
|
} catch (NonSuccessfulResponseCodeException e) {
|
||||||
if (unidentifiedAccess.isPresent()) {
|
if (unidentifiedAccess.isPresent()) {
|
||||||
profile = retrieveProfile(number, Optional.absent());
|
profile = retrieveProfile(address, Optional.absent());
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -109,18 +112,19 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
setIdentityKey(recipient, profile.getIdentityKey());
|
setIdentityKey(recipient, profile.getIdentityKey());
|
||||||
setProfileName(recipient, profile.getName());
|
setProfileName(recipient, profile.getName());
|
||||||
setProfileAvatar(recipient, profile.getAvatar());
|
setProfileAvatar(recipient, profile.getAvatar());
|
||||||
|
setProfileCapabilities(recipient, profile.getCapabilities());
|
||||||
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
|
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGroupRecipient(Recipient group) throws IOException {
|
private void handleGroupRecipient(Recipient group) throws IOException {
|
||||||
List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.requireAddress().toGroupString(), false);
|
List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.requireGroupId(), false);
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
for (Recipient recipient : recipients) {
|
||||||
handleIndividualRecipient(recipient);
|
handleIndividualRecipient(recipient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
|
private SignalServiceProfile retrieveProfile(@NonNull SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe();
|
SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe();
|
||||||
|
@ -130,14 +134,14 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
|
|
||||||
if (pipe != null) {
|
if (pipe != null) {
|
||||||
try {
|
try {
|
||||||
return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess);
|
return pipe.getProfile(address, unidentifiedAccess);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
||||||
return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess);
|
return receiver.retrieveProfile(address, unidentifiedAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
|
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
|
||||||
|
@ -157,7 +161,7 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityUtil.saveIdentity(context, recipient.requireAddress().toPhoneString(), identityKey);
|
IdentityUtil.saveIdentity(context, recipient.requireServiceId(), identityKey);
|
||||||
} catch (InvalidKeyException | IOException e) {
|
} catch (InvalidKeyException | IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
|
@ -225,6 +229,14 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setProfileCapabilities(@NonNull Recipient recipient, @Nullable SignalServiceProfile.Capabilities capabilities) {
|
||||||
|
if (capabilities == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).setUuidSupported(recipient.getId(), capabilities.isUuid());
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<UnidentifiedAccess> getUnidentifiedAccess(@NonNull Recipient recipient) {
|
private Optional<UnidentifiedAccess> getUnidentifiedAccess(@NonNull Recipient recipient) {
|
||||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
||||||
|
|
||||||
|
|
|
@ -52,10 +52,12 @@ public class RotateCertificateJob extends BaseJob {
|
||||||
@Override
|
@Override
|
||||||
public void onRun() throws IOException {
|
public void onRun() throws IOException {
|
||||||
synchronized (RotateCertificateJob.class) {
|
synchronized (RotateCertificateJob.class) {
|
||||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||||
byte[] certificate = accountManager.getSenderCertificate();
|
byte[] certificate = accountManager.getSenderCertificate();
|
||||||
|
byte[] legacyCertificate = accountManager.getSenderCertificateLegacy();
|
||||||
|
|
||||||
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
|
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
|
||||||
|
TextSecurePreferences.setUnidentifiedAccessCertificateLegacy(context, legacyCertificate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
|
|
@ -4,7 +4,6 @@ package org.thoughtcrime.securesms.jobs;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
@ -12,6 +11,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||||
|
@ -76,7 +76,7 @@ public class SendDeliveryReceiptJob extends BaseJob {
|
||||||
public void onRun() throws IOException, UntrustedIdentityException {
|
public void onRun() throws IOException, UntrustedIdentityException {
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
SignalServiceAddress remoteAddress = new SignalServiceAddress(recipient.requireAddress().serialize());
|
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
|
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
|
||||||
Collections.singletonList(messageId),
|
Collections.singletonList(messageId),
|
||||||
timestamp);
|
timestamp);
|
||||||
|
|
|
@ -13,7 +13,9 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||||
|
@ -87,7 +89,7 @@ public class SendReadReceiptJob extends BaseJob {
|
||||||
|
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
SignalServiceAddress remoteAddress = new SignalServiceAddress(recipient.requireAddress().serialize());
|
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
|
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
|
||||||
|
|
||||||
messageSender.sendReceipt(remoteAddress,
|
messageSender.sendReceipt(remoteAddress,
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.annotation.NonNull;
|
||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.telephony.SmsManager;
|
import android.telephony.SmsManager;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
||||||
|
@ -118,7 +117,7 @@ public class SmsSendJob extends SendJob {
|
||||||
throw new UndeliverableMessageException("Trying to send a secure SMS?");
|
throw new UndeliverableMessageException("Trying to send a secure SMS?");
|
||||||
}
|
}
|
||||||
|
|
||||||
String recipient = message.getIndividualRecipient().requireAddress().serialize();
|
String recipient = message.getIndividualRecipient().requireSmsAddress();
|
||||||
|
|
||||||
// See issue #1516 for bug report, and discussion on commits related to #4833 for problems
|
// See issue #1516 for bug report, and discussion on commits related to #4833 for problems
|
||||||
// related to the original fix to #1516. This still may not be a correct fix if networks allow
|
// related to the original fix to #1516. This still may not be a correct fix if networks allow
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
@ -85,12 +86,14 @@ public class TypingSendJob extends BaseJob {
|
||||||
Optional<byte[]> groupId = Optional.absent();
|
Optional<byte[]> groupId = Optional.absent();
|
||||||
|
|
||||||
if (recipient.isGroup()) {
|
if (recipient.isGroup()) {
|
||||||
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireAddress().toGroupString(), false);
|
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), false);
|
||||||
groupId = Optional.of(GroupUtil.getDecodedId(recipient.requireAddress().toGroupString()));
|
groupId = Optional.of(GroupUtil.getDecodedId(recipient.requireGroupId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recipients = Stream.of(recipients).map(Recipient::resolve).toList();
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
List<SignalServiceAddress> addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.requireAddress().serialize())).toList();
|
List<SignalServiceAddress> addresses = Stream.of(recipients).map(r -> RecipientUtil.toSignalServiceAddress(context, r)).toList();
|
||||||
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList();
|
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList();
|
||||||
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId);
|
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId);
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity {
|
||||||
getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_your_message));
|
getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_your_message));
|
||||||
} else {
|
} else {
|
||||||
Recipient recipient = message.get().getMessageRecord().getRecipient();
|
Recipient recipient = message.get().getMessageRecord().getRecipient();
|
||||||
String name = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.requireAddress().serialize()) ;
|
String name = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.getE164().orNull(), recipient.getEmail().orNull()) ;
|
||||||
getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_message_from_s, name));
|
getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_message_from_s, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -704,9 +704,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
|
||||||
} else if (recipient.isLocalNumber()) {
|
} else if (recipient.isLocalNumber()) {
|
||||||
composeText.setHint(getString(R.string.note_to_self), null);
|
composeText.setHint(getString(R.string.note_to_self), null);
|
||||||
} else {
|
} else {
|
||||||
String displayName = Optional.fromNullable(recipient.getName())
|
String displayName = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.getE164().orNull(), recipient.getEmail().orNull());
|
||||||
.or(Optional.fromNullable(recipient.getProfileName())
|
|
||||||
.or(recipient.requireAddress().serialize()));
|
|
||||||
composeText.setHint(getString(R.string.MediaSendActivity_message_to_s, displayName), null);
|
composeText.setHint(getString(R.string.MediaSendActivity_message_to_s, displayName), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class ApplicationMigrations {
|
||||||
|
|
||||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
private static final int LEGACY_CANONICAL_VERSION = 455;
|
||||||
|
|
||||||
public static final int CURRENT_VERSION = 5;
|
public static final int CURRENT_VERSION = 6;
|
||||||
|
|
||||||
private static final class Version {
|
private static final class Version {
|
||||||
static final int LEGACY = 1;
|
static final int LEGACY = 1;
|
||||||
|
@ -46,6 +46,7 @@ public class ApplicationMigrations {
|
||||||
static final int RECIPIENT_SEARCH = 3;
|
static final int RECIPIENT_SEARCH = 3;
|
||||||
static final int RECIPIENT_CLEANUP = 4;
|
static final int RECIPIENT_CLEANUP = 4;
|
||||||
static final int AVATAR_MIGRATION = 5;
|
static final int AVATAR_MIGRATION = 5;
|
||||||
|
static final int UUIDS = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,6 +179,10 @@ public class ApplicationMigrations {
|
||||||
jobs.put(Version.AVATAR_MIGRATION, new AvatarMigrationJob());
|
jobs.put(Version.AVATAR_MIGRATION, new AvatarMigrationJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSeenVersion < Version.UUIDS) {
|
||||||
|
jobs.put(Version.UUIDS, new UuidMigrationJob());
|
||||||
|
}
|
||||||
|
|
||||||
return jobs;
|
return jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package org.thoughtcrime.securesms.migrations;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Couple migrations steps need to happen after we move to UUIDS.
|
||||||
|
* - We need to get our own UUID.
|
||||||
|
* - We need to fetch the new UUID sealed sender cert.
|
||||||
|
* - We need to do a directory sync so we can guarantee that all active users have UUIDs.
|
||||||
|
*/
|
||||||
|
public class UuidMigrationJob extends MigrationJob {
|
||||||
|
|
||||||
|
public static final String KEY = "UuidMigrationJob";
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(UuidMigrationJob.class);
|
||||||
|
|
||||||
|
UuidMigrationJob() {
|
||||||
|
this(new Parameters.Builder().addConstraint(NetworkConstraint.KEY).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UuidMigrationJob(@NonNull Parameters parameters) {
|
||||||
|
super(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String getFactoryKey() {
|
||||||
|
return KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isUiBlocking() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void performMigration() throws Exception {
|
||||||
|
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||||
|
Log.w(TAG, "Not registered! Skipping migration, as it wouldn't do anything.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchOwnUuid(context);
|
||||||
|
rotateSealedSenderCerts(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean shouldRetry(@NonNull Exception e) {
|
||||||
|
return e instanceof IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fetchOwnUuid(@NonNull Context context) throws IOException {
|
||||||
|
RecipientId self = Recipient.self().getId();
|
||||||
|
UUID localUuid = ApplicationDependencies.getSignalServiceAccountManager().getOwnUuid();
|
||||||
|
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).markRegistered(self, localUuid);
|
||||||
|
TextSecurePreferences.setLocalUuid(context, localUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void rotateSealedSenderCerts(@NonNull Context context) throws IOException {
|
||||||
|
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||||
|
byte[] certificate = accountManager.getSenderCertificate();
|
||||||
|
byte[] legacyCertificate = accountManager.getSenderCertificateLegacy();
|
||||||
|
|
||||||
|
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
|
||||||
|
TextSecurePreferences.setUnidentifiedAccessCertificateLegacy(context, legacyCertificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Factory implements Job.Factory<UuidMigrationJob> {
|
||||||
|
@Override
|
||||||
|
public @NonNull UuidMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||||
|
return new UuidMigrationJob(parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.attachments.PointerAttachment;
|
import org.thoughtcrime.securesms.attachments.PointerAttachment;
|
||||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
|
|
|
@ -7,7 +7,6 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -25,7 +25,6 @@ import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.core.app.RemoteInput;
|
import androidx.core.app.RemoteInput;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import com.annimon.stream.Collectors;
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||||
|
|
|
@ -41,8 +41,8 @@ public class NotificationChannels {
|
||||||
private static final String TAG = NotificationChannels.class.getSimpleName();
|
private static final String TAG = NotificationChannels.class.getSimpleName();
|
||||||
|
|
||||||
private static class Version {
|
private static class Version {
|
||||||
static final int MESSAGES_CATEGORY = 2;
|
static final int MESSAGES_CATEGORY = 2;
|
||||||
static final int CALLS_PRIORITY_BUMP = 3;
|
static final int CALLS_PRIORITY_BUMP = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int VERSION = 3;
|
private static final int VERSION = 3;
|
||||||
|
@ -147,7 +147,7 @@ public class NotificationChannels {
|
||||||
VibrateState vibrateState = recipient.getMessageVibrate();
|
VibrateState vibrateState = recipient.getMessageVibrate();
|
||||||
boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED;
|
boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED;
|
||||||
Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context);
|
Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context);
|
||||||
String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.requireAddress().serialize());
|
String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getSmsAddress().or(""));
|
||||||
|
|
||||||
return createChannelFor(context, generateChannelIdFor(recipient), displayName, messageRingtone, vibrationEnabled);
|
return createChannelFor(context, generateChannelIdFor(recipient), displayName, messageRingtone, vibrationEnabled);
|
||||||
}
|
}
|
||||||
|
@ -384,7 +384,7 @@ public class NotificationChannels {
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(),
|
NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(),
|
||||||
getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.requireAddress().serialize()),
|
getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getSmsAddress().or("")),
|
||||||
NotificationManager.IMPORTANCE_HIGH);
|
NotificationManager.IMPORTANCE_HIGH);
|
||||||
channel.setGroup(CATEGORY_MESSAGES);
|
channel.setGroup(CATEGORY_MESSAGES);
|
||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
|
|
@ -17,6 +17,9 @@ import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.preference.CheckBoxPreference;
|
import androidx.preference.CheckBoxPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
import com.google.firebase.iid.FirebaseInstanceId;
|
import com.google.firebase.iid.FirebaseInstanceId;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||||
|
@ -24,8 +27,6 @@ import org.thoughtcrime.securesms.LogSubmitActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
|
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
|
||||||
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||||
|
@ -185,7 +186,7 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
|
||||||
protected Integer doInBackground(Void... params) {
|
protected Integer doInBackground(Void... params) {
|
||||||
try {
|
try {
|
||||||
Context context = getActivity();
|
Context context = getActivity();
|
||||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
|
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
accountManager.setGcmId(Optional.<String>absent());
|
accountManager.setGcmId(Optional.<String>absent());
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue