mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Identity functionality and data structure are completely removed.
This commit is contained in:
parent
5046d1dce6
commit
2aa179585f
@ -1,66 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.identity;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK;
|
||||
|
||||
public class UntrustedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
|
||||
|
||||
private final List<IdentityRecord> untrustedRecords;
|
||||
private final ResendListener resendListener;
|
||||
|
||||
public UntrustedSendDialog(@NonNull Context context,
|
||||
@NonNull String message,
|
||||
@NonNull List<IdentityRecord> untrustedRecords,
|
||||
@NonNull ResendListener resendListener)
|
||||
{
|
||||
super(context);
|
||||
this.untrustedRecords = untrustedRecords;
|
||||
this.resendListener = resendListener;
|
||||
|
||||
setTitle(R.string.UntrustedSendDialog_send_message);
|
||||
setIconAttribute(R.attr.dialog_alert_icon);
|
||||
setMessage(message);
|
||||
setPositiveButton(R.string.UntrustedSendDialog_send, this);
|
||||
setNegativeButton(android.R.string.cancel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext());
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
for (IdentityRecord identityRecord : untrustedRecords) {
|
||||
identityDatabase.setApproval(identityRecord.getAddress(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
resendListener.onResendMessage();
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
public interface ResendListener {
|
||||
public void onResendMessage();
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.identity;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import android.util.AttributeSet;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class UnverifiedBannerView extends LinearLayout {
|
||||
|
||||
private static final String TAG = UnverifiedBannerView.class.getSimpleName();
|
||||
|
||||
private View container;
|
||||
private TextView text;
|
||||
private ImageView closeButton;
|
||||
|
||||
public UnverifiedBannerView(Context context) {
|
||||
super(context);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public UnverifiedBannerView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
|
||||
public UnverifiedBannerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public UnverifiedBannerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.unverified_banner_view, this, true);
|
||||
this.container = ViewUtil.findById(this, R.id.container);
|
||||
this.text = ViewUtil.findById(this, R.id.unverified_text);
|
||||
this.closeButton = ViewUtil.findById(this, R.id.cancel);
|
||||
}
|
||||
|
||||
public void display(@NonNull final String text,
|
||||
@NonNull final List<IdentityRecord> unverifiedIdentities,
|
||||
@NonNull final ClickListener clickListener,
|
||||
@NonNull final DismissListener dismissListener)
|
||||
{
|
||||
this.text.setText(text);
|
||||
setVisibility(View.VISIBLE);
|
||||
|
||||
this.container.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.i(TAG, "onClick()");
|
||||
clickListener.onClicked(unverifiedIdentities);
|
||||
}
|
||||
});
|
||||
|
||||
this.closeButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hide();
|
||||
dismissListener.onDismissed(unverifiedIdentities);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public interface DismissListener {
|
||||
public void onDismissed(List<IdentityRecord> unverifiedIdentities);
|
||||
}
|
||||
|
||||
public interface ClickListener {
|
||||
public void onClicked(List<IdentityRecord> unverifiedIdentities);
|
||||
}
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components.identity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK;
|
||||
|
||||
public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
|
||||
|
||||
private final List<IdentityRecord> untrustedRecords;
|
||||
private final ResendListener resendListener;
|
||||
|
||||
public UnverifiedSendDialog(@NonNull Context context,
|
||||
@NonNull String message,
|
||||
@NonNull List<IdentityRecord> untrustedRecords,
|
||||
@NonNull ResendListener resendListener)
|
||||
{
|
||||
super(context);
|
||||
this.untrustedRecords = untrustedRecords;
|
||||
this.resendListener = resendListener;
|
||||
|
||||
setTitle(R.string.UnverifiedSendDialog_send_message);
|
||||
setIconAttribute(R.attr.dialog_alert_icon);
|
||||
setMessage(message);
|
||||
setPositiveButton(R.string.UnverifiedSendDialog_send, this);
|
||||
setNegativeButton(android.R.string.cancel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext());
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
for (IdentityRecord identityRecord : untrustedRecords) {
|
||||
identityDatabase.setVerified(identityRecord.getAddress(),
|
||||
identityRecord.getIdentityKey(),
|
||||
IdentityDatabase.VerifiedStatus.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
resendListener.onResendMessage();
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
public interface ResendListener {
|
||||
public void onResendMessage();
|
||||
}
|
||||
}
|
@ -112,29 +112,21 @@ import org.thoughtcrime.securesms.components.TooltipPopup;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
|
||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
||||
import org.thoughtcrime.securesms.components.identity.UntrustedSendDialog;
|
||||
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView;
|
||||
import org.thoughtcrime.securesms.components.identity.UnverifiedSendDialog;
|
||||
import org.thoughtcrime.securesms.components.location.SignalPlace;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||
@ -200,7 +192,6 @@ import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.PushCharacterCalculator;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
@ -218,7 +209,6 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@ -229,7 +219,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import kotlin.Unit;
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK;
|
||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
|
||||
/**
|
||||
@ -290,7 +279,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private Button unblockButton;
|
||||
private Button makeDefaultSmsButton;
|
||||
private InputAwareLayout container;
|
||||
private Stub<UnverifiedBannerView> unverifiedBannerView;
|
||||
private Stub<GroupShareProfileView> groupShareProfileView;
|
||||
private TypingStatusTextWatcher typingTextWatcher;
|
||||
private MentionTextWatcher mentionTextWatcher;
|
||||
@ -327,8 +315,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private int collapsedKeyboardHeight = Integer.MAX_VALUE;
|
||||
private int keyboardHeight = 0;
|
||||
|
||||
private final IdentityRecordList identityRecords = new IdentityRecordList();
|
||||
|
||||
// Message status bar
|
||||
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
|
||||
private String messageStatus = null;
|
||||
@ -505,7 +491,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
EventBus.getDefault().register(this);
|
||||
initializeEnabledCheck();
|
||||
initializeMmsEnabledCheck();
|
||||
initializeIdentityRecords();
|
||||
composeText.setTransport();
|
||||
|
||||
updateTitleTextView(recipient);
|
||||
@ -513,7 +498,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
updateSubtitleTextView();
|
||||
updateInputUI(recipient);
|
||||
setGroupShareProfileReminder(recipient);
|
||||
calculateCharactersRemaining();
|
||||
|
||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
|
||||
markThreadAsRead();
|
||||
@ -1178,52 +1162,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnverifiedRecipients() {
|
||||
List<Recipient> unverifiedRecipients = identityRecords.getUnverifiedRecipients(this);
|
||||
List<IdentityRecord> unverifiedRecords = identityRecords.getUnverifiedRecords();
|
||||
String message = IdentityUtil.getUnverifiedSendDialogDescription(this, unverifiedRecipients);
|
||||
|
||||
if (message == null) return;
|
||||
|
||||
//noinspection CodeBlock2Expr
|
||||
new UnverifiedSendDialog(this, message, unverifiedRecords, () -> {
|
||||
initializeIdentityRecords().addListener(new ListenableFuture.Listener<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
sendMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
});
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleUntrustedRecipients() {
|
||||
List<Recipient> untrustedRecipients = identityRecords.getUntrustedRecipients(this);
|
||||
List<IdentityRecord> untrustedRecords = identityRecords.getUntrustedRecords();
|
||||
String untrustedMessage = IdentityUtil.getUntrustedSendDialogDescription(this, untrustedRecipients);
|
||||
|
||||
if (untrustedMessage == null) return;
|
||||
|
||||
//noinspection CodeBlock2Expr
|
||||
new UntrustedSendDialog(this, untrustedMessage, untrustedRecords, () -> {
|
||||
initializeIdentityRecords().addListener(new ListenableFuture.Listener<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
sendMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
});
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleSecurityChange(boolean isSecureText, boolean isDefaultSms) {
|
||||
Log.i(TAG, "handleSecurityChange(" + isSecureText + ", " + isDefaultSms + ")");
|
||||
if (isSecurityInitialized && isSecureText == true && isDefaultSms == this.isDefaultSms) {
|
||||
@ -1249,7 +1187,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
*/
|
||||
|
||||
calculateCharactersRemaining();
|
||||
supportInvalidateOptionsMenu();
|
||||
updateInputUI(recipient);
|
||||
}
|
||||
@ -1429,63 +1366,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private ListenableFuture<Boolean> initializeIdentityRecords() {
|
||||
final SettableFuture<Boolean> future = new SettableFuture<>();
|
||||
|
||||
new AsyncTask<Recipient, Void, Pair<IdentityRecordList, String>>() {
|
||||
@Override
|
||||
protected @NonNull Pair<IdentityRecordList, String> doInBackground(Recipient... params) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this);
|
||||
IdentityRecordList identityRecordList = new IdentityRecordList();
|
||||
List<Recipient> recipients = new LinkedList<>();
|
||||
|
||||
if (params[0].isGroupRecipient()) {
|
||||
recipients.addAll(DatabaseFactory.getGroupDatabase(ConversationActivity.this)
|
||||
.getGroupMembers(params[0].getAddress().toGroupString(), false));
|
||||
} else {
|
||||
recipients.add(params[0]);
|
||||
}
|
||||
|
||||
for (Recipient recipient : recipients) {
|
||||
Log.i(TAG, "Loading identity for: " + recipient.getAddress());
|
||||
identityRecordList.add(identityDatabase.getIdentity(recipient.getAddress()));
|
||||
}
|
||||
|
||||
String message = null;
|
||||
|
||||
if (identityRecordList.isUnverified()) {
|
||||
message = IdentityUtil.getUnverifiedBannerDescription(ConversationActivity.this, identityRecordList.getUnverifiedRecipients(ConversationActivity.this));
|
||||
}
|
||||
|
||||
return new Pair<>(identityRecordList, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(@NonNull Pair<IdentityRecordList, String> result) {
|
||||
Log.i(TAG, "Got identity records: " + result.first.isUnverified());
|
||||
identityRecords.replaceWith(result.first);
|
||||
|
||||
//TODO Remove it.
|
||||
// if (result.second != null) {
|
||||
// Log.d(TAG, "Replacing banner...");
|
||||
// unverifiedBannerView.get().display(result.second, result.first.getUnverifiedRecords(),
|
||||
// new UnverifiedClickedListener(),
|
||||
// new UnverifiedDismissedListener());
|
||||
// } else if (unverifiedBannerView.resolved()) {
|
||||
// Log.d(TAG, "Clearing banner...");
|
||||
// unverifiedBannerView.get().hide();
|
||||
// }
|
||||
|
||||
// titleView.setVerified(isSecureText && identityRecords.isVerified());
|
||||
|
||||
future.set(true);
|
||||
}
|
||||
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
private void initializeViews() {
|
||||
profilePictureView = findViewById(R.id.profilePictureView);
|
||||
titleTextView = findViewById(R.id.titleTextView);
|
||||
@ -1498,7 +1378,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
unblockButton = ViewUtil.findById(this, R.id.unblock_button);
|
||||
makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
|
||||
container = ViewUtil.findById(this, R.id.layout_container);
|
||||
unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
|
||||
groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub);
|
||||
quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
|
||||
inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
|
||||
@ -1700,11 +1579,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
});
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onIdentityRecordUpdate(final IdentityRecord event) {
|
||||
initializeIdentityRecords();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
public void onStickerPackInstalled(final StickerPackInstallEvent event) {
|
||||
if (!TextSecurePreferences.hasSeenStickerIntroTooltip(this)) return;
|
||||
@ -1731,7 +1605,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
initializeSecurity(true, isDefaultSms);
|
||||
calculateCharactersRemaining();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1931,25 +1804,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateCharactersRemaining() {
|
||||
/*
|
||||
String messageBody = composeText.getTextTrimmed();
|
||||
TransportOption transportOption = sendButton.getSelectedTransport();
|
||||
CharacterState characterState = transportOption.calculateCharacters(messageBody);
|
||||
|
||||
if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) {
|
||||
charactersLeft.setText(String.format(dynamicLanguage.getCurrentLocale(),
|
||||
"%d/%d (%d)",
|
||||
characterState.charactersRemaining,
|
||||
characterState.maxTotalMessageSize,
|
||||
characterState.messagesSpent));
|
||||
charactersLeft.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
charactersLeft.setVisibility(View.GONE);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private void initializeMediaKeyboardProviders(@NonNull MediaKeyboard mediaKeyboard, boolean stickersAvailable) {
|
||||
boolean isSystemEmojiPreferred = TextSecurePreferences.isSystemEmojiPreferred(this);
|
||||
|
||||
@ -2127,9 +1981,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages
|
||||
needsSplit;
|
||||
|
||||
if (identityRecords.isUnverified()) {
|
||||
handleUnverifiedRecipients();
|
||||
} else if (isMediaMessage) {
|
||||
if (isMediaMessage) {
|
||||
sendMediaMessage(expiresIn, subscriptionId, initiating);
|
||||
} else {
|
||||
sendTextMessage(expiresIn, subscriptionId, initiating);
|
||||
@ -2552,7 +2404,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
calculateCharactersRemaining();
|
||||
|
||||
if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) {
|
||||
composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50);
|
||||
@ -2719,33 +2570,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
updateLinkPreviewState();
|
||||
}
|
||||
|
||||
private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener {
|
||||
@Override
|
||||
public void onDismissed(final List<IdentityRecord> unverifiedIdentities) {
|
||||
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ConversationActivity.this);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
for (IdentityRecord identityRecord : unverifiedIdentities) {
|
||||
identityDatabase.setVerified(identityRecord.getAddress(),
|
||||
identityRecord.getIdentityKey(),
|
||||
VerifiedStatus.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
initializeIdentityRecords();
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private class QuoteRestorationTask extends AsyncTask<Void, Void, MessageRecord> {
|
||||
|
||||
private final String serialized;
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
@ -16,11 +15,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.BindableConversationItem;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@ -28,14 +23,11 @@ import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
|
@ -21,63 +21,68 @@ public class SignalProtocolStoreImpl implements SignalProtocolStore {
|
||||
|
||||
// private final PreKeyStore preKeyStore;
|
||||
// private final SignedPreKeyStore signedPreKeyStore;
|
||||
private final IdentityKeyStore identityKeyStore;
|
||||
// private final IdentityKeyStore identityKeyStore;
|
||||
private final SessionStore sessionStore;
|
||||
|
||||
public SignalProtocolStoreImpl(Context context) {
|
||||
// this.preKeyStore = new TextSecurePreKeyStore(context);
|
||||
// this.signedPreKeyStore = new TextSecurePreKeyStore(context);
|
||||
this.identityKeyStore = new TextSecureIdentityKeyStore(context);
|
||||
// this.identityKeyStore = new TextSecureIdentityKeyStore(context);
|
||||
this.sessionStore = new TextSecureSessionStore(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKeyPair getIdentityKeyPair() {
|
||||
return identityKeyStore.getIdentityKeyPair();
|
||||
// return identityKeyStore.getIdentityKeyPair();
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalRegistrationId() {
|
||||
return identityKeyStore.getLocalRegistrationId();
|
||||
// return identityKeyStore.getLocalRegistrationId();
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
|
||||
return identityKeyStore.saveIdentity(address, identityKey);
|
||||
// return identityKeyStore.saveIdentity(address, identityKey);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
|
||||
return identityKeyStore.isTrustedIdentity(address, identityKey, direction);
|
||||
// return identityKeyStore.isTrustedIdentity(address, identityKey, direction);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getIdentity(SignalProtocolAddress address) {
|
||||
return identityKeyStore.getIdentity(address);
|
||||
// return identityKeyStore.getIdentity(address);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
// return preKeyStore.loadPreKey(preKeyId);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
// preKeyStore.storePreKey(preKeyId, record);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsPreKey(int preKeyId) {
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
// return preKeyStore.containsPreKey(preKeyId);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePreKey(int preKeyId) {
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
// preKeyStore.removePreKey(preKeyId);
|
||||
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,151 +0,0 @@
|
||||
package org.thoughtcrime.securesms.crypto.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.session.libsignal.libsignal.IdentityKey;
|
||||
import org.session.libsignal.libsignal.IdentityKeyPair;
|
||||
import org.session.libsignal.libsignal.SignalProtocolAddress;
|
||||
import org.session.libsignal.libsignal.state.IdentityKeyStore;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TextSecureIdentityKeyStore implements IdentityKeyStore {
|
||||
|
||||
private static final int TIMESTAMP_THRESHOLD_SECONDS = 5;
|
||||
|
||||
private static final String TAG = TextSecureIdentityKeyStore.class.getSimpleName();
|
||||
private static final Object LOCK = new Object();
|
||||
|
||||
private final Context context;
|
||||
|
||||
public TextSecureIdentityKeyStore(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKeyPair getIdentityKeyPair() {
|
||||
return IdentityKeyUtil.getIdentityKeyPair(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalRegistrationId() {
|
||||
return TextSecurePreferences.getLocalRegistrationId(context);
|
||||
}
|
||||
|
||||
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
|
||||
synchronized (LOCK) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||
Address signalAddress = Address.fromSerialized(address.getName());
|
||||
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(signalAddress);
|
||||
|
||||
if (!identityRecord.isPresent()) {
|
||||
Log.i(TAG, "Saving new identity...");
|
||||
identityDatabase.saveIdentity(signalAddress, identityKey, VerifiedStatus.DEFAULT, true, System.currentTimeMillis(), nonBlockingApproval);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!identityRecord.get().getIdentityKey().equals(identityKey)) {
|
||||
Log.i(TAG, "Replacing existing identity...");
|
||||
VerifiedStatus verifiedStatus;
|
||||
|
||||
if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.VERIFIED ||
|
||||
identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED)
|
||||
{
|
||||
verifiedStatus = VerifiedStatus.UNVERIFIED;
|
||||
} else {
|
||||
verifiedStatus = VerifiedStatus.DEFAULT;
|
||||
}
|
||||
|
||||
identityDatabase.saveIdentity(signalAddress, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
|
||||
IdentityUtil.markIdentityUpdate(context, Recipient.from(context, signalAddress, true));
|
||||
SessionUtil.archiveSiblingSessions(context, address);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isNonBlockingApprovalRequired(identityRecord.get())) {
|
||||
Log.i(TAG, "Setting approval status...");
|
||||
identityDatabase.setApproval(signalAddress, nonBlockingApproval);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
|
||||
return saveIdentity(address, identityKey, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
|
||||
synchronized (LOCK) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||
String ourNumber = TextSecurePreferences.getLocalNumber(context);
|
||||
Address theirAddress = Address.fromSerialized(address.getName());
|
||||
|
||||
if (ourNumber.equals(address.getName()) || Address.fromSerialized(ourNumber).equals(theirAddress)) {
|
||||
return identityKey.equals(IdentityKeyUtil.getIdentityKey(context));
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case SENDING: return isTrustedForSending(identityKey, identityDatabase.getIdentity(theirAddress));
|
||||
case RECEIVING: return true;
|
||||
default: throw new AssertionError("Unknown direction: " + direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getIdentity(SignalProtocolAddress address) {
|
||||
Optional<IdentityRecord> record = DatabaseFactory.getIdentityDatabase(context).getIdentity(Address.fromSerialized(address.getName()));
|
||||
|
||||
if (record.isPresent()) {
|
||||
return record.get().getIdentityKey();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTrustedForSending(IdentityKey identityKey, Optional<IdentityRecord> identityRecord) {
|
||||
if (!identityRecord.isPresent()) {
|
||||
Log.w(TAG, "Nothing here, returning true...");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!identityKey.equals(identityRecord.get().getIdentityKey())) {
|
||||
Log.w(TAG, "Identity keys don't match...");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (identityRecord.get().getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
|
||||
Log.w(TAG, "Needs unverified approval!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNonBlockingApprovalRequired(identityRecord.get())) {
|
||||
Log.w(TAG, "Needs non-blocking approval!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isNonBlockingApprovalRequired(IdentityRecord identityRecord) {
|
||||
return !identityRecord.isFirstUse() &&
|
||||
System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(TIMESTAMP_THRESHOLD_SECONDS) &&
|
||||
!identityRecord.isApprovedNonBlocking();
|
||||
}
|
||||
}
|
@ -51,7 +51,6 @@ public class DatabaseFactory {
|
||||
private final MediaDatabase media;
|
||||
private final ThreadDatabase thread;
|
||||
private final MmsSmsDatabase mmsSmsDatabase;
|
||||
private final IdentityDatabase identityDatabase;
|
||||
private final DraftDatabase draftDatabase;
|
||||
private final PushDatabase pushDatabase;
|
||||
private final GroupDatabase groupDatabase;
|
||||
@ -107,10 +106,6 @@ public class DatabaseFactory {
|
||||
return getInstance(context).media;
|
||||
}
|
||||
|
||||
public static IdentityDatabase getIdentityDatabase(Context context) {
|
||||
return getInstance(context).identityDatabase;
|
||||
}
|
||||
|
||||
public static DraftDatabase getDraftDatabase(Context context) {
|
||||
return getInstance(context).draftDatabase;
|
||||
}
|
||||
@ -211,7 +206,6 @@ public class DatabaseFactory {
|
||||
this.media = new MediaDatabase(context, databaseHelper);
|
||||
this.thread = new ThreadDatabase(context, databaseHelper);
|
||||
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
||||
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
||||
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
||||
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
||||
this.groupDatabase = new GroupDatabase(context, databaseHelper);
|
||||
|
@ -1,243 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.session.libsignal.libsignal.IdentityKey;
|
||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class IdentityDatabase extends Database {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = IdentityDatabase.class.getSimpleName();
|
||||
|
||||
private static final String TABLE_NAME = "identities";
|
||||
private static final String ID = "_id";
|
||||
private static final String ADDRESS = "address";
|
||||
private static final String IDENTITY_KEY = "key";
|
||||
private static final String TIMESTAMP = "timestamp";
|
||||
private static final String FIRST_USE = "first_use";
|
||||
private static final String NONBLOCKING_APPROVAL = "nonblocking_approval";
|
||||
private static final String VERIFIED = "verified";
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
|
||||
" (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
ADDRESS + " TEXT UNIQUE, " +
|
||||
IDENTITY_KEY + " TEXT, " +
|
||||
FIRST_USE + " INTEGER DEFAULT 0, " +
|
||||
TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||
VERIFIED + " INTEGER DEFAULT 0, " +
|
||||
NONBLOCKING_APPROVAL + " INTEGER DEFAULT 0);";
|
||||
|
||||
public enum VerifiedStatus {
|
||||
DEFAULT, VERIFIED, UNVERIFIED;
|
||||
|
||||
public int toInt() {
|
||||
if (this == DEFAULT) return 0;
|
||||
else if (this == VERIFIED) return 1;
|
||||
else if (this == UNVERIFIED) return 2;
|
||||
else throw new AssertionError();
|
||||
}
|
||||
|
||||
public static VerifiedStatus forState(int state) {
|
||||
if (state == 0) return DEFAULT;
|
||||
else if (state == 1) return VERIFIED;
|
||||
else if (state == 2) return UNVERIFIED;
|
||||
else throw new AssertionError("No such state: " + state);
|
||||
}
|
||||
}
|
||||
|
||||
IdentityDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
}
|
||||
|
||||
public Cursor getIdentities() {
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
return database.query(TABLE_NAME, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public @Nullable IdentityReader readerFor(@Nullable Cursor cursor) {
|
||||
if (cursor == null) return null;
|
||||
return new IdentityReader(cursor);
|
||||
}
|
||||
|
||||
public Optional<IdentityRecord> getIdentity(Address address) {
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?",
|
||||
new String[] {address.serialize()}, null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return Optional.of(getIdentityRecord(cursor));
|
||||
}
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
throw new AssertionError(e);
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
public void saveIdentity(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus,
|
||||
boolean firstUse, long timestamp, boolean nonBlockingApproval)
|
||||
{
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
String identityKeyString = Base64.encodeBytes(identityKey.serialize());
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(ADDRESS, address.serialize());
|
||||
contentValues.put(IDENTITY_KEY, identityKeyString);
|
||||
contentValues.put(TIMESTAMP, timestamp);
|
||||
contentValues.put(VERIFIED, verifiedStatus.toInt());
|
||||
contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval ? 1 : 0);
|
||||
contentValues.put(FIRST_USE, firstUse ? 1 : 0);
|
||||
|
||||
database.replace(TABLE_NAME, null, contentValues);
|
||||
|
||||
EventBus.getDefault().post(new IdentityRecord(address, identityKey, verifiedStatus,
|
||||
firstUse, timestamp, nonBlockingApproval));
|
||||
}
|
||||
|
||||
public void setApproval(Address address, boolean nonBlockingApproval) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
||||
ContentValues contentValues = new ContentValues(2);
|
||||
contentValues.put(NONBLOCKING_APPROVAL, nonBlockingApproval);
|
||||
|
||||
database.update(TABLE_NAME, contentValues, ADDRESS + " = ?", new String[] {address.serialize()});
|
||||
}
|
||||
|
||||
public void setVerified(Address address, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(VERIFIED, verifiedStatus.toInt());
|
||||
|
||||
int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ? AND " + IDENTITY_KEY + " = ?",
|
||||
new String[] {address.serialize(), Base64.encodeBytes(identityKey.serialize())});
|
||||
|
||||
if (updated > 0) {
|
||||
Optional<IdentityRecord> record = getIdentity(address);
|
||||
if (record.isPresent()) EventBus.getDefault().post(record.get());
|
||||
}
|
||||
}
|
||||
|
||||
private IdentityRecord getIdentityRecord(@NonNull Cursor cursor) throws IOException, InvalidKeyException {
|
||||
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
|
||||
String serializedIdentity = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY));
|
||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||
int verifiedStatus = cursor.getInt(cursor.getColumnIndexOrThrow(VERIFIED));
|
||||
boolean nonblockingApproval = cursor.getInt(cursor.getColumnIndexOrThrow(NONBLOCKING_APPROVAL)) == 1;
|
||||
boolean firstUse = cursor.getInt(cursor.getColumnIndexOrThrow(FIRST_USE)) == 1;
|
||||
IdentityKey identity = new IdentityKey(Base64.decode(serializedIdentity), 0);
|
||||
|
||||
return new IdentityRecord(Address.fromSerialized(address), identity, VerifiedStatus.forState(verifiedStatus), firstUse, timestamp, nonblockingApproval);
|
||||
}
|
||||
|
||||
public static class IdentityRecord {
|
||||
|
||||
private final Address address;
|
||||
private final IdentityKey identitykey;
|
||||
private final VerifiedStatus verifiedStatus;
|
||||
private final boolean firstUse;
|
||||
private final long timestamp;
|
||||
private final boolean nonblockingApproval;
|
||||
|
||||
private IdentityRecord(Address address,
|
||||
IdentityKey identitykey, VerifiedStatus verifiedStatus,
|
||||
boolean firstUse, long timestamp, boolean nonblockingApproval)
|
||||
{
|
||||
this.address = address;
|
||||
this.identitykey = identitykey;
|
||||
this.verifiedStatus = verifiedStatus;
|
||||
this.firstUse = firstUse;
|
||||
this.timestamp = timestamp;
|
||||
this.nonblockingApproval = nonblockingApproval;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public IdentityKey getIdentityKey() {
|
||||
return identitykey;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public VerifiedStatus getVerifiedStatus() {
|
||||
return verifiedStatus;
|
||||
}
|
||||
|
||||
public boolean isApprovedNonBlocking() {
|
||||
return nonblockingApproval;
|
||||
}
|
||||
|
||||
public boolean isFirstUse() {
|
||||
return firstUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String toString() {
|
||||
return "{address: " + address + ", identityKey: " + identitykey + ", verifiedStatus: " + verifiedStatus + ", firstUse: " + firstUse + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class IdentityReader {
|
||||
private final Cursor cursor;
|
||||
|
||||
IdentityReader(@NonNull Cursor cursor) {
|
||||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
public @Nullable IdentityRecord getNext() {
|
||||
if (cursor.moveToNext()) {
|
||||
try {
|
||||
return getIdentityRecord(cursor);
|
||||
} catch (IOException | InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
@ -120,7 +119,6 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(MmsDatabase.CREATE_TABLE);
|
||||
db.execSQL(AttachmentDatabase.CREATE_TABLE);
|
||||
db.execSQL(ThreadDatabase.CREATE_TABLE);
|
||||
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
||||
db.execSQL(DraftDatabase.CREATE_TABLE);
|
||||
db.execSQL(PushDatabase.CREATE_TABLE);
|
||||
db.execSQL(GroupDatabase.CREATE_TABLE);
|
||||
|
@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.JobDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
|
||||
@ -91,9 +90,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
private static final int lokiV16 = 37;
|
||||
private static final int lokiV17 = 38;
|
||||
private static final int lokiV18_CLEAR_BG_POLL_JOBS = 39;
|
||||
private static final int lokiV19_OLD_CODE_CLEANUP = 40; //TODO Change back to 40 when the refactoring is over.
|
||||
//TODO Merge all "refactor" migrations to one before pushing to the main repo.
|
||||
private static final int lokiV19_REFACTOR0 = 40;
|
||||
private static final int lokiV19_REFACTOR1 = 41;
|
||||
|
||||
private static final int DATABASE_VERSION = lokiV19_OLD_CODE_CLEANUP; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final int DATABASE_VERSION = lokiV19_REFACTOR1;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
@ -124,7 +126,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(MmsDatabase.CREATE_TABLE);
|
||||
db.execSQL(AttachmentDatabase.CREATE_TABLE);
|
||||
db.execSQL(ThreadDatabase.CREATE_TABLE);
|
||||
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
||||
db.execSQL(DraftDatabase.CREATE_TABLE);
|
||||
db.execSQL(PushDatabase.CREATE_TABLE);
|
||||
db.execSQL(GroupDatabase.CREATE_TABLE);
|
||||
@ -637,16 +638,14 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL("DELETE FROM constraint_spec WHERE factory_key = 'BackgroundPollJob'");
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV19_OLD_CODE_CLEANUP) {
|
||||
// Many classes were removed. We need to update D structure and data to match the code changes.
|
||||
|
||||
String[] deletedJobKeys = {
|
||||
"ServiceOutageDetectionJob",
|
||||
};
|
||||
for (String jobKey : deletedJobKeys) {
|
||||
db.execSQL("DELETE FROM job_spec WHERE factory_key = ?", new String[]{jobKey});
|
||||
db.execSQL("DELETE FROM constraint_spec WHERE factory_key = ?", new String[]{jobKey});
|
||||
}
|
||||
// Many classes were removed. We need to update DB structure and data to match the code changes.
|
||||
//TODO Merge "refactor" changes in one migration.
|
||||
if (oldVersion < lokiV19_REFACTOR0) {
|
||||
deleteJobRecords(db, "ServiceOutageDetectionJob");
|
||||
}
|
||||
if (oldVersion < lokiV19_REFACTOR1) {
|
||||
db.execSQL("DROP TABLE identities");
|
||||
deleteJobRecords(db, "RetrieveProfileJob");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
@ -691,4 +690,15 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up all the records related to the job keys specified.
|
||||
* This method should be called once the Signal job class is deleted from the project.
|
||||
*/
|
||||
private static void deleteJobRecords(SQLiteDatabase db, String... jobKeys) {
|
||||
for (String jobKey : jobKeys) {
|
||||
db.execSQL("DELETE FROM job_spec WHERE factory_key = ?", new String[]{jobKey});
|
||||
db.execSQL("DELETE FROM constraint_spec WHERE factory_key = ?", new String[]{jobKey});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,115 +0,0 @@
|
||||
package org.thoughtcrime.securesms.database.identity;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class IdentityRecordList {
|
||||
|
||||
private static final String TAG = IdentityRecordList.class.getSimpleName();
|
||||
|
||||
private final List<IdentityRecord> identityRecords = new LinkedList<>();
|
||||
|
||||
public void add(Optional<IdentityRecord> identityRecord) {
|
||||
if (identityRecord.isPresent()) {
|
||||
identityRecords.add(identityRecord.get());
|
||||
}
|
||||
}
|
||||
|
||||
public void replaceWith(IdentityRecordList identityRecordList) {
|
||||
identityRecords.clear();
|
||||
identityRecords.addAll(identityRecordList.identityRecords);
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (identityRecord.getVerifiedStatus() != VerifiedStatus.VERIFIED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return identityRecords.size() > 0;
|
||||
}
|
||||
|
||||
public boolean isUnverified() {
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isUntrusted() {
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (isUntrusted(identityRecord)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<IdentityRecord> getUntrustedRecords() {
|
||||
List<IdentityRecord> results = new LinkedList<>();
|
||||
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (isUntrusted(identityRecord)) {
|
||||
results.add(identityRecord);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<Recipient> getUntrustedRecipients(Context context) {
|
||||
List<Recipient> untrusted = new LinkedList<>();
|
||||
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (isUntrusted(identityRecord)) {
|
||||
untrusted.add(Recipient.from(context, identityRecord.getAddress(), false));
|
||||
}
|
||||
}
|
||||
|
||||
return untrusted;
|
||||
}
|
||||
|
||||
public List<IdentityRecord> getUnverifiedRecords() {
|
||||
List<IdentityRecord> results = new LinkedList<>();
|
||||
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
|
||||
results.add(identityRecord);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<Recipient> getUnverifiedRecipients(Context context) {
|
||||
List<Recipient> unverified = new LinkedList<>();
|
||||
|
||||
for (IdentityRecord identityRecord : identityRecords) {
|
||||
if (identityRecord.getVerifiedStatus() == VerifiedStatus.UNVERIFIED) {
|
||||
unverified.add(Recipient.from(context, identityRecord.getAddress(), false));
|
||||
}
|
||||
}
|
||||
|
||||
return unverified;
|
||||
}
|
||||
|
||||
private boolean isUntrusted(IdentityRecord identityRecord) {
|
||||
return !identityRecord.isApprovedNonBlocking() &&
|
||||
System.currentTimeMillis() - identityRecord.getTimestamp() < TimeUnit.SECONDS.toMillis(5);
|
||||
}
|
||||
|
||||
}
|
@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
@ -60,7 +59,6 @@ import network.loki.messenger.BuildConfig;
|
||||
RequestGroupInfoJob.class,
|
||||
PushGroupUpdateJob.class,
|
||||
AvatarDownloadJob.class,
|
||||
RetrieveProfileJob.class,
|
||||
RetrieveProfileAvatarJob.class,
|
||||
SendReadReceiptJob.class,
|
||||
AppProtectionPreferenceFragment.class,
|
||||
|
@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
@ -64,7 +63,6 @@ public class WorkManagerFactoryMappings {
|
||||
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
|
||||
put(RequestGroupInfoJob.class.getName(), RequestGroupInfoJob.KEY);
|
||||
put(RetrieveProfileAvatarJob.class.getName(), RetrieveProfileAvatarJob.KEY);
|
||||
put(RetrieveProfileJob.class.getName(), RetrieveProfileJob.KEY);
|
||||
put(RotateCertificateJob.class.getName(), RotateCertificateJob.KEY);
|
||||
put(RotateProfileKeyJob.class.getName(), RotateProfileKeyJob.KEY);
|
||||
put(SendDeliveryReceiptJob.class.getName(), SendDeliveryReceiptJob.KEY);
|
||||
|
@ -48,7 +48,6 @@ public final class JobManagerFactories {
|
||||
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
|
||||
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
|
||||
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
|
||||
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory(application));
|
||||
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
||||
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
|
||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||
|
@ -5,7 +5,6 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -92,7 +91,6 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
||||
@ -107,10 +105,7 @@ import org.session.libsignal.service.api.messages.SignalServiceEnvelope;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceTypingMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.ReadMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.RequestMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage;
|
||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||
@ -479,10 +474,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSynchronizeVerifiedMessage(@NonNull VerifiedMessage verifiedMessage) {
|
||||
IdentityUtil.processVerifiedMessage(context, verifiedMessage);
|
||||
}
|
||||
|
||||
private void handleSynchronizeStickerPackOperation(@NonNull List<StickerPackOperationMessage> stickerPackOperations) {
|
||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||
|
||||
|
@ -1,255 +0,0 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
|
||||
import android.app.Application;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
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.service.IncomingMessageObserver;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.session.libsignal.libsignal.IdentityKey;
|
||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.api.SignalServiceMessagePipe;
|
||||
import org.session.libsignal.service.api.SignalServiceMessageReceiver;
|
||||
import org.session.libsignal.service.api.crypto.InvalidCiphertextException;
|
||||
import org.session.libsignal.service.api.crypto.ProfileCipher;
|
||||
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
|
||||
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
|
||||
import org.session.libsignal.service.api.profiles.SignalServiceProfile;
|
||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.session.libsignal.service.api.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class RetrieveProfileJob extends BaseJob implements InjectableType {
|
||||
|
||||
public static final String KEY = "RetrieveProfileJob";
|
||||
|
||||
private static final String TAG = RetrieveProfileJob.class.getSimpleName();
|
||||
|
||||
private static final String KEY_ADDRESS = "address";
|
||||
|
||||
@Inject SignalServiceMessageReceiver receiver;
|
||||
|
||||
private Recipient recipient;
|
||||
|
||||
public RetrieveProfileJob(@NonNull Recipient recipient) {
|
||||
this(new Job.Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxAttempts(3)
|
||||
.build(),
|
||||
recipient);
|
||||
}
|
||||
|
||||
private RetrieveProfileJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient) {
|
||||
super(parameters);
|
||||
this.recipient = recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putString(KEY_ADDRESS, recipient.getAddress().serialize()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException, InvalidKeyException {
|
||||
// Loki - Do nothing
|
||||
/*
|
||||
try {
|
||||
if (recipient.isGroupRecipient()) handleGroupRecipient(recipient);
|
||||
else handleIndividualRecipient(recipient);
|
||||
} catch (InvalidNumberException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {}
|
||||
|
||||
private void handleIndividualRecipient(Recipient recipient)
|
||||
throws IOException, InvalidKeyException, InvalidNumberException
|
||||
{
|
||||
String number = recipient.getAddress().toPhoneString();
|
||||
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(recipient);
|
||||
|
||||
SignalServiceProfile profile;
|
||||
|
||||
try {
|
||||
profile = retrieveProfile(number, unidentifiedAccess);
|
||||
} catch (NonSuccessfulResponseCodeException e) {
|
||||
if (unidentifiedAccess.isPresent()) {
|
||||
profile = retrieveProfile(number, Optional.absent());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
setIdentityKey(recipient, profile.getIdentityKey());
|
||||
setProfileName(recipient, profile.getName());
|
||||
setProfileAvatar(recipient, profile.getAvatar());
|
||||
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
|
||||
}
|
||||
|
||||
private void handleGroupRecipient(Recipient group)
|
||||
throws IOException, InvalidKeyException, InvalidNumberException
|
||||
{
|
||||
List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(group.getAddress().toGroupString(), false);
|
||||
|
||||
for (Recipient recipient : recipients) {
|
||||
handleIndividualRecipient(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
|
||||
throws IOException
|
||||
{
|
||||
SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe();
|
||||
SignalServiceMessagePipe unidentifiedPipe = IncomingMessageObserver.getUnidentifiedPipe();
|
||||
SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe
|
||||
: authPipe;
|
||||
|
||||
if (pipe != null) {
|
||||
try {
|
||||
return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess);
|
||||
}
|
||||
|
||||
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
|
||||
try {
|
||||
if (TextUtils.isEmpty(identityKeyValue)) {
|
||||
Log.w(TAG, "Identity key is missing on profile!");
|
||||
return;
|
||||
}
|
||||
|
||||
IdentityKey identityKey = new IdentityKey(Base64.decode(identityKeyValue), 0);
|
||||
|
||||
if (!DatabaseFactory.getIdentityDatabase(context)
|
||||
.getIdentity(recipient.getAddress())
|
||||
.isPresent())
|
||||
{
|
||||
Log.w(TAG, "Still first use...");
|
||||
return;
|
||||
}
|
||||
|
||||
IdentityUtil.saveIdentity(context, recipient.getAddress().toPhoneString(), identityKey);
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUnidentifiedAccessMode(Recipient recipient, String unidentifiedAccessVerifier, boolean unrestrictedUnidentifiedAccess) {
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
byte[] profileKey = recipient.getProfileKey();
|
||||
|
||||
if (unrestrictedUnidentifiedAccess && unidentifiedAccessVerifier != null) {
|
||||
Log.i(TAG, "Marking recipient UD status as unrestricted.");
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
|
||||
} else if (profileKey == null || unidentifiedAccessVerifier == null) {
|
||||
Log.i(TAG, "Marking recipient UD status as disabled.");
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
|
||||
} else {
|
||||
ProfileCipher profileCipher = new ProfileCipher(profileKey);
|
||||
boolean verifiedUnidentifiedAccess;
|
||||
|
||||
try {
|
||||
verifiedUnidentifiedAccess = profileCipher.verifyUnidentifiedAccess(Base64.decode(unidentifiedAccessVerifier));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
verifiedUnidentifiedAccess = false;
|
||||
}
|
||||
|
||||
UnidentifiedAccessMode mode = verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED;
|
||||
Log.i(TAG, "Marking recipient UD status as " + mode.name() + " after verification.");
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, mode);
|
||||
}
|
||||
}
|
||||
|
||||
private void setProfileName(Recipient recipient, String profileName) {
|
||||
try {
|
||||
byte[] profileKey = recipient.getProfileKey();
|
||||
if (profileKey == null) return;
|
||||
|
||||
String plaintextProfileName = null;
|
||||
|
||||
if (profileName != null) {
|
||||
ProfileCipher profileCipher = new ProfileCipher(profileKey);
|
||||
plaintextProfileName = new String(profileCipher.decryptName(Base64.decode(profileName)));
|
||||
}
|
||||
|
||||
if (!Util.equals(plaintextProfileName, recipient.getProfileName())) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileName(recipient, plaintextProfileName);
|
||||
}
|
||||
} catch (InvalidCiphertextException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setProfileAvatar(Recipient recipient, String profileAvatar) {
|
||||
if (recipient.getProfileKey() == null) return;
|
||||
|
||||
if (!Util.equals(profileAvatar, recipient.getProfileAvatar())) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RetrieveProfileAvatarJob(recipient, profileAvatar));
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<UnidentifiedAccess> getUnidentifiedAccess(@NonNull Recipient recipient) {
|
||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
||||
|
||||
if (unidentifiedAccess.isPresent()) {
|
||||
return unidentifiedAccess.get().getTargetUnidentifiedAccess();
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<RetrieveProfileJob> {
|
||||
|
||||
private final Application application;
|
||||
|
||||
public Factory(Application application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull RetrieveProfileJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new RetrieveProfileJob(parameters, Recipient.from(application, Address.fromSerialized(data.getString(KEY_ADDRESS)), true));
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase
|
||||
import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities
|
||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
||||
import org.thoughtcrime.securesms.loki.utilities.push
|
||||
@ -72,9 +71,6 @@ class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
|
||||
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
|
||||
val registrationID = KeyHelper.generateRegistrationId(false)
|
||||
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
|
||||
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
|
||||
IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
|
||||
true, System.currentTimeMillis(), true)
|
||||
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
|
||||
TextSecurePreferences.setRestorationTime(this, System.currentTimeMillis())
|
||||
TextSecurePreferences.setHasViewedSeed(this, true)
|
||||
|
@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase
|
||||
import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities
|
||||
import org.thoughtcrime.securesms.loki.utilities.push
|
||||
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
||||
@ -109,9 +108,6 @@ class RegisterActivity : BaseActionBarActivity() {
|
||||
val userHexEncodedPublicKey = x25519KeyPair!!.hexEncodedPublicKey
|
||||
val registrationID = KeyHelper.generateRegistrationId(false)
|
||||
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
|
||||
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
|
||||
IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
|
||||
true, System.currentTimeMillis(), true)
|
||||
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
|
||||
TextSecurePreferences.setRestorationTime(this, 0)
|
||||
TextSecurePreferences.setHasViewedSeed(this, false)
|
||||
|
@ -1,252 +0,0 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.IncomingIdentityDefaultMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingIdentityVerifiedMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingIdentityDefaultMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingIdentityVerifiedMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
||||
import org.session.libsignal.libsignal.IdentityKey;
|
||||
import org.session.libsignal.libsignal.SignalProtocolAddress;
|
||||
import org.session.libsignal.libsignal.state.IdentityKeyStore;
|
||||
import org.session.libsignal.libsignal.state.SessionRecord;
|
||||
import org.session.libsignal.libsignal.state.SessionStore;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup;
|
||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK;
|
||||
|
||||
public class IdentityUtil {
|
||||
|
||||
private static final String TAG = IdentityUtil.class.getSimpleName();
|
||||
|
||||
public static ListenableFuture<Optional<IdentityRecord>> getRemoteIdentityKey(final Context context, final Recipient recipient) {
|
||||
final SettableFuture<Optional<IdentityRecord>> future = new SettableFuture<>();
|
||||
|
||||
new AsyncTask<Recipient, Void, Optional<IdentityRecord>>() {
|
||||
@Override
|
||||
protected Optional<IdentityRecord> doInBackground(Recipient... recipient) {
|
||||
return DatabaseFactory.getIdentityDatabase(context)
|
||||
.getIdentity(recipient[0].getAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Optional<IdentityRecord> result) {
|
||||
future.set(result);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
public static void markIdentityVerified(Context context, Recipient recipient, boolean verified, boolean remote)
|
||||
{
|
||||
long time = System.currentTimeMillis();
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
GroupDatabase.Reader reader = groupDatabase.getGroups();
|
||||
|
||||
GroupDatabase.GroupRecord groupRecord;
|
||||
|
||||
while ((groupRecord = reader.getNext()) != null) {
|
||||
if (groupRecord.isRSSFeed() || groupRecord.isOpenGroup()) { continue; }
|
||||
if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive() && !groupRecord.isMms()) {
|
||||
SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId(), SignalServiceGroup.GroupType.SIGNAL);
|
||||
|
||||
if (remote) {
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false);
|
||||
|
||||
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
|
||||
else incoming = new IncomingIdentityDefaultMessage(incoming);
|
||||
|
||||
smsDatabase.insertMessageInbox(incoming);
|
||||
} else {
|
||||
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(group.getGroupId(), false)), true);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient);
|
||||
OutgoingTextMessage outgoing ;
|
||||
|
||||
if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient);
|
||||
else outgoing = new OutgoingIdentityDefaultMessage(recipient);
|
||||
|
||||
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remote) {
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false);
|
||||
|
||||
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
|
||||
else incoming = new IncomingIdentityDefaultMessage(incoming);
|
||||
|
||||
smsDatabase.insertMessageInbox(incoming);
|
||||
} else {
|
||||
OutgoingTextMessage outgoing;
|
||||
|
||||
if (verified) outgoing = new OutgoingIdentityVerifiedMessage(recipient);
|
||||
else outgoing = new OutgoingIdentityDefaultMessage(recipient);
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
||||
|
||||
Log.i(TAG, "Inserting verified outbox...");
|
||||
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void markIdentityUpdate(Context context, Recipient recipient) {
|
||||
long time = System.currentTimeMillis();
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
GroupDatabase.Reader reader = groupDatabase.getGroups();
|
||||
|
||||
GroupDatabase.GroupRecord groupRecord;
|
||||
|
||||
while ((groupRecord = reader.getNext()) != null) {
|
||||
if (groupRecord.isRSSFeed() || groupRecord.isOpenGroup()) { continue; }
|
||||
if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) {
|
||||
SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId(), SignalServiceGroup.GroupType.SIGNAL);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false);
|
||||
IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming);
|
||||
|
||||
smsDatabase.insertMessageInbox(groupUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false);
|
||||
IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming);
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveIdentity(Context context, String number, IdentityKey identityKey) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context);
|
||||
SessionStore sessionStore = new TextSecureSessionStore(context);
|
||||
SignalProtocolAddress address = new SignalProtocolAddress(number, 1);
|
||||
|
||||
if (identityKeyStore.saveIdentity(address, identityKey)) {
|
||||
if (sessionStore.containsSession(address)) {
|
||||
SessionRecord sessionRecord = sessionStore.loadSession(address);
|
||||
sessionRecord.archiveCurrentState();
|
||||
|
||||
sessionStore.storeSession(address, sessionRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||
Recipient recipient = Recipient.from(context, Address.fromExternal(context, verifiedMessage.getDestination()), true);
|
||||
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getAddress());
|
||||
|
||||
if (!identityRecord.isPresent() && verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT) {
|
||||
Log.w(TAG, "No existing record for default status");
|
||||
return;
|
||||
}
|
||||
|
||||
if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT &&
|
||||
identityRecord.isPresent() &&
|
||||
identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey()) &&
|
||||
identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.DEFAULT)
|
||||
{
|
||||
identityDatabase.setVerified(recipient.getAddress(), identityRecord.get().getIdentityKey(), IdentityDatabase.VerifiedStatus.DEFAULT);
|
||||
markIdentityVerified(context, recipient, false, true);
|
||||
}
|
||||
|
||||
if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.VERIFIED &&
|
||||
(!identityRecord.isPresent() ||
|
||||
(identityRecord.isPresent() && !identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey())) ||
|
||||
(identityRecord.isPresent() && identityRecord.get().getVerifiedStatus() != IdentityDatabase.VerifiedStatus.VERIFIED)))
|
||||
{
|
||||
saveIdentity(context, verifiedMessage.getDestination(), verifiedMessage.getIdentityKey());
|
||||
identityDatabase.setVerified(recipient.getAddress(), verifiedMessage.getIdentityKey(), IdentityDatabase.VerifiedStatus.VERIFIED);
|
||||
markIdentityVerified(context, recipient, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static @Nullable String getUnverifiedBannerDescription(@NonNull Context context,
|
||||
@NonNull List<Recipient> unverified)
|
||||
{
|
||||
return getPluralizedIdentityDescription(context, unverified,
|
||||
R.string.IdentityUtil_unverified_banner_one,
|
||||
R.string.IdentityUtil_unverified_banner_two,
|
||||
R.string.IdentityUtil_unverified_banner_many);
|
||||
}
|
||||
|
||||
public static @Nullable String getUnverifiedSendDialogDescription(@NonNull Context context,
|
||||
@NonNull List<Recipient> unverified)
|
||||
{
|
||||
return getPluralizedIdentityDescription(context, unverified,
|
||||
R.string.IdentityUtil_unverified_dialog_one,
|
||||
R.string.IdentityUtil_unverified_dialog_two,
|
||||
R.string.IdentityUtil_unverified_dialog_many);
|
||||
}
|
||||
|
||||
public static @Nullable String getUntrustedSendDialogDescription(@NonNull Context context,
|
||||
@NonNull List<Recipient> untrusted)
|
||||
{
|
||||
return getPluralizedIdentityDescription(context, untrusted,
|
||||
R.string.IdentityUtil_untrusted_dialog_one,
|
||||
R.string.IdentityUtil_untrusted_dialog_two,
|
||||
R.string.IdentityUtil_untrusted_dialog_many);
|
||||
}
|
||||
|
||||
private static @Nullable String getPluralizedIdentityDescription(@NonNull Context context,
|
||||
@NonNull List<Recipient> recipients,
|
||||
@StringRes int resourceOne,
|
||||
@StringRes int resourceTwo,
|
||||
@StringRes int resourceMany)
|
||||
{
|
||||
if (recipients.isEmpty()) return null;
|
||||
|
||||
if (recipients.size() == 1) {
|
||||
String name = recipients.get(0).toShortString();
|
||||
return context.getString(resourceOne, name);
|
||||
} else {
|
||||
String firstName = recipients.get(0).toShortString();
|
||||
String secondName = recipients.get(1).toShortString();
|
||||
|
||||
if (recipients.size() == 2) {
|
||||
return context.getString(resourceTwo, firstName, secondName);
|
||||
} else {
|
||||
int othersCount = recipients.size() - 2;
|
||||
String nMore = context.getResources().getQuantityString(R.plurals.identity_others, othersCount, othersCount);
|
||||
|
||||
return context.getString(resourceMany, firstName, secondName, nMore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -146,13 +146,6 @@
|
||||
android:inflatedId="@+id/group_share_profile_view"
|
||||
android:layout="@layout/conversation_activity_group_share_profile_stub" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/unverified_banner_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inflatedId="@+id/unverified_banner"
|
||||
android:layout="@layout/conversation_activity_unverified_banner_stub" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_content"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.identity.UnverifiedBannerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/unverified_banner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
@ -117,7 +117,7 @@ public class SignalServiceMessageReceiver {
|
||||
public InputStream retrieveProfileAvatar(String path, File destination, byte[] profileKey, int maxSizeBytes)
|
||||
throws IOException
|
||||
{
|
||||
DownloadUtilities.INSTANCE.downloadFile(destination, path, maxSizeBytes, null);
|
||||
DownloadUtilities.downloadFile(destination, path, maxSizeBytes, null);
|
||||
return new ProfileCipherInputStream(new FileInputStream(destination), profileKey);
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ public class SignalServiceMessageReceiver {
|
||||
{
|
||||
// Loki - Fetch attachment
|
||||
if (pointer.getUrl().isEmpty()) throw new InvalidMessageException("Missing attachment URL.");
|
||||
DownloadUtilities.INSTANCE.downloadFile(destination, pointer.getUrl(), maxSizeBytes, listener);
|
||||
DownloadUtilities.downloadFile(destination, pointer.getUrl(), maxSizeBytes, listener);
|
||||
|
||||
// Loki - Assume we're retrieving an attachment for an open group server if the digest is not set
|
||||
if (!pointer.getDigest().isPresent()) { return new FileInputStream(destination); }
|
||||
|
@ -16,6 +16,7 @@ object DownloadUtilities {
|
||||
/**
|
||||
* Blocks the calling thread.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun downloadFile(destination: File, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) {
|
||||
val outputStream = FileOutputStream(destination) // Throws
|
||||
var remainingAttempts = 4
|
||||
@ -36,6 +37,7 @@ object DownloadUtilities {
|
||||
/**
|
||||
* Blocks the calling thread.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun downloadFile(outputStream: OutputStream, url: String, maxSize: Int, listener: SignalServiceAttachment.ProgressListener?) {
|
||||
// We need to throw a PushNetworkException or NonSuccessfulResponseCodeException
|
||||
// because the underlying Signal logic requires these to work correctly
|
||||
|
Loading…
Reference in New Issue
Block a user