mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
WIP cleanup of verify identity functionality.
This commit is contained in:
parent
73c4e44711
commit
112f77f6ed
@ -316,9 +316,6 @@
|
|||||||
android:name="org.thoughtcrime.securesms.PassphraseChangeActivity"
|
android:name="org.thoughtcrime.securesms.PassphraseChangeActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:label="@string/AndroidManifest__change_passphrase" />
|
android:label="@string/AndroidManifest__change_passphrase" />
|
||||||
<activity
|
|
||||||
android:name="org.thoughtcrime.securesms.VerifyIdentityActivity"
|
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.stickers.StickerManagementActivity"
|
android:name="org.thoughtcrime.securesms.stickers.StickerManagementActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
|
@ -1,201 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import android.text.SpannableString;
|
|
||||||
import android.text.Spanned;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
|
||||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
|
||||||
import org.session.libsignal.libsignal.SignalProtocolAddress;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceEnvelope;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
public class ConfirmIdentityDialog extends AlertDialog {
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
|
|
||||||
|
|
||||||
private OnClickListener callback;
|
|
||||||
|
|
||||||
public ConfirmIdentityDialog(Context context,
|
|
||||||
MessageRecord messageRecord,
|
|
||||||
IdentityKeyMismatch mismatch)
|
|
||||||
{
|
|
||||||
super(context);
|
|
||||||
|
|
||||||
Recipient recipient = Recipient.from(context, mismatch.getAddress(), false);
|
|
||||||
String name = recipient.toShortString();
|
|
||||||
String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name);
|
|
||||||
SpannableString spannableString = new SpannableString(introduction + " " +
|
|
||||||
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
|
|
||||||
|
|
||||||
spannableString.setSpan(new VerifySpan(context, mismatch),
|
|
||||||
introduction.length()+1, spannableString.length(),
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
|
|
||||||
setTitle(name);
|
|
||||||
setMessage(spannableString);
|
|
||||||
|
|
||||||
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress()));
|
|
||||||
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void show() {
|
|
||||||
super.show();
|
|
||||||
((TextView)this.findViewById(android.R.id.message))
|
|
||||||
.setMovementMethod(LinkMovementMethod.getInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCallback(OnClickListener callback) {
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AcceptListener implements OnClickListener {
|
|
||||||
|
|
||||||
private final MessageRecord messageRecord;
|
|
||||||
private final IdentityKeyMismatch mismatch;
|
|
||||||
private final Address address;
|
|
||||||
|
|
||||||
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
|
|
||||||
this.messageRecord = messageRecord;
|
|
||||||
this.mismatch = mismatch;
|
|
||||||
this.address = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
new AsyncTask<Void, Void, Void>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params) {
|
|
||||||
synchronized (SESSION_LOCK) {
|
|
||||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1);
|
|
||||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
|
||||||
|
|
||||||
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
processMessageRecord(messageRecord);
|
|
||||||
processPendingMessageRecords(messageRecord.getThreadId(), mismatch);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processMessageRecord(MessageRecord messageRecord) {
|
|
||||||
if (messageRecord.isOutgoing()) processOutgoingMessageRecord(messageRecord);
|
|
||||||
else processIncomingMessageRecord(messageRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) {
|
|
||||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext());
|
|
||||||
Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId);
|
|
||||||
MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor);
|
|
||||||
MessageRecord record;
|
|
||||||
|
|
||||||
try {
|
|
||||||
while ((record = reader.getNext()) != null) {
|
|
||||||
for (IdentityKeyMismatch recordMismatch : record.getIdentityKeyMismatches()) {
|
|
||||||
if (mismatch.equals(recordMismatch)) {
|
|
||||||
processMessageRecord(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (reader != null)
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processOutgoingMessageRecord(MessageRecord messageRecord) {
|
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
|
||||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
|
|
||||||
|
|
||||||
if (messageRecord.isMms()) {
|
|
||||||
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
|
||||||
mismatch.getAddress(),
|
|
||||||
mismatch.getIdentityKey());
|
|
||||||
|
|
||||||
if (messageRecord.getRecipient().isPushGroupRecipient()) {
|
|
||||||
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
|
|
||||||
} else {
|
|
||||||
MessageSender.resend(getContext(), messageRecord);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
|
||||||
mismatch.getAddress(),
|
|
||||||
mismatch.getIdentityKey());
|
|
||||||
|
|
||||||
MessageSender.resend(getContext(), messageRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processIncomingMessageRecord(MessageRecord messageRecord) {
|
|
||||||
try {
|
|
||||||
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext());
|
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
|
||||||
|
|
||||||
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
|
||||||
mismatch.getAddress(),
|
|
||||||
mismatch.getIdentityKey());
|
|
||||||
|
|
||||||
boolean legacy = !messageRecord.isContentBundleKeyExchange();
|
|
||||||
|
|
||||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
|
||||||
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
|
|
||||||
messageRecord.getRecipientDeviceId(),
|
|
||||||
messageRecord.getDateSent(),
|
|
||||||
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
|
||||||
!legacy ? Base64.decode(messageRecord.getBody()) : null,
|
|
||||||
0, null);
|
|
||||||
|
|
||||||
long pushId = pushDatabase.insert(envelope);
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(getContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new PushDecryptJob(getContext(), pushId, messageRecord.getId()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
|
|
||||||
if (callback != null) callback.onClick(null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CancelListener implements OnClickListener {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if (callback != null) callback.onClick(null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,660 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2017 Open 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;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.animation.TypeEvaluator;
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Vibrator;
|
|
||||||
import androidx.annotation.DrawableRes;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
|
||||||
import androidx.appcompat.widget.SwitchCompat;
|
|
||||||
import android.text.Html;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
|
||||||
import android.view.ContextMenu;
|
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.animation.Animation;
|
|
||||||
import android.view.animation.AnticipateInterpolator;
|
|
||||||
import android.view.animation.OvershootInterpolator;
|
|
||||||
import android.view.animation.ScaleAnimation;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
|
||||||
import org.thoughtcrime.securesms.components.camera.CameraView;
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
|
||||||
import org.thoughtcrime.securesms.qr.QrCode;
|
|
||||||
import org.thoughtcrime.securesms.qr.ScanListener;
|
|
||||||
import org.thoughtcrime.securesms.qr.ScanningThread;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
|
||||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.libsignal.fingerprint.Fingerprint;
|
|
||||||
import org.session.libsignal.libsignal.fingerprint.FingerprintParsingException;
|
|
||||||
import org.session.libsignal.libsignal.fingerprint.FingerprintVersionMismatchException;
|
|
||||||
import org.session.libsignal.libsignal.fingerprint.NumericFingerprintGenerator;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
import static org.session.libsignal.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activity for verifying identity keys.
|
|
||||||
*
|
|
||||||
* @author Moxie Marlinspike
|
|
||||||
*/
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, ScanListener, View.OnClickListener {
|
|
||||||
|
|
||||||
private static final String TAG = VerifyIdentityActivity.class.getSimpleName();
|
|
||||||
|
|
||||||
public static final String ADDRESS_EXTRA = "address";
|
|
||||||
public static final String IDENTITY_EXTRA = "recipient_identity";
|
|
||||||
public static final String VERIFIED_EXTRA = "verified_state";
|
|
||||||
|
|
||||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
|
||||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
|
||||||
|
|
||||||
private VerifyDisplayFragment displayFragment = new VerifyDisplayFragment();
|
|
||||||
private VerifyScanFragment scanFragment = new VerifyScanFragment();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPreCreate() {
|
|
||||||
dynamicTheme.onCreate(this);
|
|
||||||
dynamicLanguage.onCreate(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle state, boolean ready) {
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
getSupportActionBar().setTitle(R.string.AndroidManifest__verify_safety_number);
|
|
||||||
|
|
||||||
Recipient recipient = Recipient.from(this, (Address)getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
|
|
||||||
recipient.addListener(this);
|
|
||||||
|
|
||||||
setActionBarNotificationBarColor(recipient.getColor());
|
|
||||||
|
|
||||||
Bundle extras = new Bundle();
|
|
||||||
extras.putParcelable(VerifyDisplayFragment.REMOTE_ADDRESS, getIntent().getParcelableExtra(ADDRESS_EXTRA));
|
|
||||||
extras.putParcelable(VerifyDisplayFragment.REMOTE_IDENTITY, getIntent().getParcelableExtra(IDENTITY_EXTRA));
|
|
||||||
extras.putString(VerifyDisplayFragment.REMOTE_NUMBER, recipient.getAddress().toPhoneString());
|
|
||||||
extras.putParcelable(VerifyDisplayFragment.LOCAL_IDENTITY, new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this)));
|
|
||||||
extras.putString(VerifyDisplayFragment.LOCAL_NUMBER, TextSecurePreferences.getLocalNumber(this));
|
|
||||||
extras.putBoolean(VerifyDisplayFragment.VERIFIED_STATE, getIntent().getBooleanExtra(VERIFIED_EXTRA, false));
|
|
||||||
|
|
||||||
scanFragment.setScanListener(this);
|
|
||||||
displayFragment.setClickListener(this);
|
|
||||||
|
|
||||||
initFragment(android.R.id.content, displayFragment, dynamicLanguage.getCurrentLocale(), extras);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case android.R.id.home: finish(); return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onModified(final Recipient recipient) {
|
|
||||||
Util.runOnMain(() -> setActionBarNotificationBarColor(recipient.getColor()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onQrDataFound(final String data) {
|
|
||||||
Util.runOnMain(() -> {
|
|
||||||
((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
|
|
||||||
|
|
||||||
getSupportFragmentManager().popBackStack();
|
|
||||||
displayFragment.setScannedFingerprint(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Permissions.with(this)
|
|
||||||
.request(Manifest.permission.CAMERA)
|
|
||||||
.withPermanentDenialDialog(getString(R.string.VerifyIdentityActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code_but_it_has_been_permanently_denied))
|
|
||||||
.onAllGranted(() -> {
|
|
||||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
|
||||||
transaction.setCustomAnimations(R.anim.slide_from_top, R.anim.slide_to_bottom,
|
|
||||||
R.anim.slide_from_bottom, R.anim.slide_to_top);
|
|
||||||
|
|
||||||
transaction.replace(android.R.id.content, scanFragment)
|
|
||||||
.addToBackStack(null)
|
|
||||||
.commitAllowingStateLoss();
|
|
||||||
})
|
|
||||||
.onAnyDenied(() -> Toast.makeText(this, R.string.VerifyIdentityActivity_unable_to_scan_qr_code_without_camera_permission, Toast.LENGTH_LONG).show())
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setActionBarNotificationBarColor(MaterialColor color) {
|
|
||||||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
getWindow().setStatusBarColor(color.toStatusBarColor(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class VerifyDisplayFragment extends Fragment implements RecipientModifiedListener, CompoundButton.OnCheckedChangeListener {
|
|
||||||
|
|
||||||
public static final String REMOTE_ADDRESS = "remote_address";
|
|
||||||
public static final String REMOTE_NUMBER = "remote_number";
|
|
||||||
public static final String REMOTE_IDENTITY = "remote_identity";
|
|
||||||
public static final String LOCAL_IDENTITY = "local_identity";
|
|
||||||
public static final String LOCAL_NUMBER = "local_number";
|
|
||||||
public static final String VERIFIED_STATE = "verified_state";
|
|
||||||
|
|
||||||
private Recipient recipient;
|
|
||||||
private String localNumber;
|
|
||||||
private String remoteNumber;
|
|
||||||
|
|
||||||
private IdentityKey localIdentity;
|
|
||||||
private IdentityKey remoteIdentity;
|
|
||||||
|
|
||||||
private Fingerprint fingerprint;
|
|
||||||
|
|
||||||
private View container;
|
|
||||||
private View numbersContainer;
|
|
||||||
private ImageView qrCode;
|
|
||||||
private ImageView qrVerified;
|
|
||||||
private TextView tapLabel;
|
|
||||||
private TextView description;
|
|
||||||
private View.OnClickListener clickListener;
|
|
||||||
private SwitchCompat verified;
|
|
||||||
|
|
||||||
private TextView[] codes = new TextView[12];
|
|
||||||
private boolean animateSuccessOnDraw = false;
|
|
||||||
private boolean animateFailureOnDraw = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
|
|
||||||
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_display_fragment);
|
|
||||||
this.numbersContainer = ViewUtil.findById(container, R.id.number_table);
|
|
||||||
this.qrCode = ViewUtil.findById(container, R.id.qr_code);
|
|
||||||
this.verified = ViewUtil.findById(container, R.id.verified_switch);
|
|
||||||
this.qrVerified = ViewUtil.findById(container, R.id.qr_verified);
|
|
||||||
this.description = ViewUtil.findById(container, R.id.description);
|
|
||||||
this.tapLabel = ViewUtil.findById(container, R.id.tap_label);
|
|
||||||
this.codes[0] = ViewUtil.findById(container, R.id.code_first);
|
|
||||||
this.codes[1] = ViewUtil.findById(container, R.id.code_second);
|
|
||||||
this.codes[2] = ViewUtil.findById(container, R.id.code_third);
|
|
||||||
this.codes[3] = ViewUtil.findById(container, R.id.code_fourth);
|
|
||||||
this.codes[4] = ViewUtil.findById(container, R.id.code_fifth);
|
|
||||||
this.codes[5] = ViewUtil.findById(container, R.id.code_sixth);
|
|
||||||
this.codes[6] = ViewUtil.findById(container, R.id.code_seventh);
|
|
||||||
this.codes[7] = ViewUtil.findById(container, R.id.code_eighth);
|
|
||||||
this.codes[8] = ViewUtil.findById(container, R.id.code_ninth);
|
|
||||||
this.codes[9] = ViewUtil.findById(container, R.id.code_tenth);
|
|
||||||
this.codes[10] = ViewUtil.findById(container, R.id.code_eleventh);
|
|
||||||
this.codes[11] = ViewUtil.findById(container, R.id.code_twelth);
|
|
||||||
|
|
||||||
this.qrCode.setOnClickListener(clickListener);
|
|
||||||
this.registerForContextMenu(numbersContainer);
|
|
||||||
|
|
||||||
this.verified.setChecked(getArguments().getBoolean(VERIFIED_STATE, false));
|
|
||||||
this.verified.setOnCheckedChangeListener(this);
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle bundle) {
|
|
||||||
super.onCreate(bundle);
|
|
||||||
|
|
||||||
Address address = getArguments().getParcelable(REMOTE_ADDRESS);
|
|
||||||
IdentityKeyParcelable localIdentityParcelable = getArguments().getParcelable(LOCAL_IDENTITY);
|
|
||||||
IdentityKeyParcelable remoteIdentityParcelable = getArguments().getParcelable(REMOTE_IDENTITY);
|
|
||||||
|
|
||||||
if (address == null) throw new AssertionError("Address required");
|
|
||||||
if (localIdentityParcelable == null) throw new AssertionError("local identity required");
|
|
||||||
if (remoteIdentityParcelable == null) throw new AssertionError("remote identity required");
|
|
||||||
|
|
||||||
this.localNumber = getArguments().getString(LOCAL_NUMBER);
|
|
||||||
this.localIdentity = localIdentityParcelable.get();
|
|
||||||
this.remoteNumber = getArguments().getString(REMOTE_NUMBER);
|
|
||||||
this.recipient = Recipient.from(getActivity(), address, true);
|
|
||||||
this.remoteIdentity = remoteIdentityParcelable.get();
|
|
||||||
|
|
||||||
this.recipient.addListener(this);
|
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Fingerprint>() {
|
|
||||||
@Override
|
|
||||||
protected Fingerprint doInBackground(Void... params) {
|
|
||||||
return new NumericFingerprintGenerator(5200).createFor(localNumber, localIdentity,
|
|
||||||
remoteNumber, remoteIdentity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Fingerprint fingerprint) {
|
|
||||||
VerifyDisplayFragment.this.fingerprint = fingerprint;
|
|
||||||
setFingerprintViews(fingerprint, true);
|
|
||||||
getActivity().supportInvalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onModified(final Recipient recipient) {
|
|
||||||
Util.runOnMain(() -> setRecipientText(recipient));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
setRecipientText(recipient);
|
|
||||||
|
|
||||||
if (fingerprint != null) {
|
|
||||||
setFingerprintViews(fingerprint, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animateSuccessOnDraw) {
|
|
||||||
animateSuccessOnDraw = false;
|
|
||||||
animateVerifiedSuccess();
|
|
||||||
} else if (animateFailureOnDraw) {
|
|
||||||
animateFailureOnDraw = false;
|
|
||||||
animateVerifiedFailure();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
recipient.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateContextMenu(ContextMenu menu, View view,
|
|
||||||
ContextMenuInfo menuInfo)
|
|
||||||
{
|
|
||||||
super.onCreateContextMenu(menu, view, menuInfo);
|
|
||||||
|
|
||||||
if (fingerprint != null) {
|
|
||||||
MenuInflater inflater = getActivity().getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.verify_display_fragment_context_menu, menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onContextItemSelected(MenuItem item) {
|
|
||||||
if (fingerprint == null) return super.onContextItemSelected(item);
|
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_copy: handleCopyToClipboard(fingerprint, codes.length); return true;
|
|
||||||
case R.id.menu_compare: handleCompareWithClipboard(fingerprint); return true;
|
|
||||||
default: return super.onContextItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
|
|
||||||
if (fingerprint != null) {
|
|
||||||
inflater.inflate(R.menu.verify_identity, menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.verify_identity__share: handleShare(fingerprint, codes.length); return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScannedFingerprint(String scanned) {
|
|
||||||
try {
|
|
||||||
if (fingerprint.getScannableFingerprint().compareTo(scanned.getBytes("ISO-8859-1"))) {
|
|
||||||
this.animateSuccessOnDraw = true;
|
|
||||||
} else {
|
|
||||||
this.animateFailureOnDraw = true;
|
|
||||||
}
|
|
||||||
} catch (FingerprintVersionMismatchException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
if (e.getOurVersion() < e.getTheirVersion()) {
|
|
||||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_a_newer_version_of_Signal, Toast.LENGTH_LONG).show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_an_old_version_of_signal, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
} catch (FingerprintParsingException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_the_scanned_qr_code_is_not_a_correctly_formatted_safety_number, Toast.LENGTH_LONG).show();
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClickListener(View.OnClickListener listener) {
|
|
||||||
this.clickListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
private @NonNull String getFormattedSafetyNumbers(@NonNull Fingerprint fingerprint, int segmentCount) {
|
|
||||||
String[] segments = getSegments(fingerprint, segmentCount);
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
|
|
||||||
for (int i = 0; i < segments.length; i++) {
|
|
||||||
result.append(segments[i]);
|
|
||||||
|
|
||||||
if (i != segments.length - 1) {
|
|
||||||
if (((i+1) % 4) == 0) result.append('\n');
|
|
||||||
else result.append(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleCopyToClipboard(Fingerprint fingerprint, int segmentCount) {
|
|
||||||
Util.writeTextToClipboard(getActivity(), getFormattedSafetyNumbers(fingerprint, segmentCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleCompareWithClipboard(Fingerprint fingerprint) {
|
|
||||||
String clipboardData = Util.readTextFromClipboard(getActivity());
|
|
||||||
|
|
||||||
if (clipboardData == null) {
|
|
||||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String numericClipboardData = clipboardData.replaceAll("\\D", "");
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(numericClipboardData) || numericClipboardData.length() != 60) {
|
|
||||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_safety_number_to_compare_was_found_in_the_clipboard, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fingerprint.getDisplayableFingerprint().getDisplayText().equals(numericClipboardData)) {
|
|
||||||
animateVerifiedSuccess();
|
|
||||||
} else {
|
|
||||||
animateVerifiedFailure();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleShare(@NonNull Fingerprint fingerprint, int segmentCount) {
|
|
||||||
String shareString =
|
|
||||||
getString(R.string.VerifyIdentityActivity_our_signal_safety_number) + "\n" +
|
|
||||||
getFormattedSafetyNumbers(fingerprint, segmentCount) + "\n";
|
|
||||||
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, shareString);
|
|
||||||
intent.setType("text/plain");
|
|
||||||
|
|
||||||
try {
|
|
||||||
startActivity(Intent.createChooser(intent, getString(R.string.VerifyIdentityActivity_share_safety_number_via)));
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_no_app_to_share_to, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRecipientText(Recipient recipient) {
|
|
||||||
description.setText(Html.fromHtml(String.format(getActivity().getString(R.string.verify_display_fragment__if_you_wish_to_verify_the_security_of_your_end_to_end_encryption_with_s), recipient.toShortString())));
|
|
||||||
description.setMovementMethod(LinkMovementMethod.getInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFingerprintViews(Fingerprint fingerprint, boolean animate) {
|
|
||||||
String[] segments = getSegments(fingerprint, codes.length);
|
|
||||||
|
|
||||||
for (int i=0;i<codes.length;i++) {
|
|
||||||
if (animate) setCodeSegment(codes[i], segments[i]);
|
|
||||||
else codes[i].setText(segments[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] qrCodeData = fingerprint.getScannableFingerprint().getSerialized();
|
|
||||||
String qrCodeString = new String(qrCodeData, Charset.forName("ISO-8859-1"));
|
|
||||||
Bitmap qrCodeBitmap = QrCode.create(qrCodeString);
|
|
||||||
|
|
||||||
qrCode.setImageBitmap(qrCodeBitmap);
|
|
||||||
|
|
||||||
if (animate) {
|
|
||||||
ViewUtil.fadeIn(qrCode, 1000);
|
|
||||||
ViewUtil.fadeIn(tapLabel, 1000);
|
|
||||||
} else {
|
|
||||||
qrCode.setVisibility(View.VISIBLE);
|
|
||||||
tapLabel.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCodeSegment(final TextView codeView, String segment) {
|
|
||||||
ValueAnimator valueAnimator = new ValueAnimator();
|
|
||||||
valueAnimator.setObjectValues(0, Integer.parseInt(segment));
|
|
||||||
|
|
||||||
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
@Override
|
|
||||||
public void onAnimationUpdate(ValueAnimator animation) {
|
|
||||||
int value = (int) animation.getAnimatedValue();
|
|
||||||
codeView.setText(String.format("%05d", value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
valueAnimator.setEvaluator(new TypeEvaluator<Integer>() {
|
|
||||||
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
|
|
||||||
return Math.round(startValue + (endValue - startValue) * fraction);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
valueAnimator.setDuration(1000);
|
|
||||||
valueAnimator.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getSegments(Fingerprint fingerprint, int segmentCount) {
|
|
||||||
String[] segments = new String[segmentCount];
|
|
||||||
String digits = fingerprint.getDisplayableFingerprint().getDisplayText();
|
|
||||||
int partSize = digits.length() / segmentCount;
|
|
||||||
|
|
||||||
for (int i=0;i<segmentCount;i++) {
|
|
||||||
segments[i] = digits.substring(i * partSize, (i * partSize) + partSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bitmap createVerifiedBitmap(int width, int height, @DrawableRes int id) {
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(bitmap);
|
|
||||||
Bitmap check = BitmapFactory.decodeResource(getResources(), id);
|
|
||||||
float offset = (width - check.getWidth()) / 2;
|
|
||||||
|
|
||||||
canvas.drawBitmap(check, offset, offset, null);
|
|
||||||
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateVerifiedSuccess() {
|
|
||||||
Bitmap qrBitmap = ((BitmapDrawable)qrCode.getDrawable()).getBitmap();
|
|
||||||
Bitmap qrSuccess = createVerifiedBitmap(qrBitmap.getWidth(), qrBitmap.getHeight(), R.drawable.ic_check_white_48dp);
|
|
||||||
|
|
||||||
qrVerified.setImageBitmap(qrSuccess);
|
|
||||||
qrVerified.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.MULTIPLY);
|
|
||||||
|
|
||||||
animateVerified();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateVerifiedFailure() {
|
|
||||||
Bitmap qrBitmap = ((BitmapDrawable)qrCode.getDrawable()).getBitmap();
|
|
||||||
Bitmap qrSuccess = createVerifiedBitmap(qrBitmap.getWidth(), qrBitmap.getHeight(), R.drawable.ic_close_white_48dp);
|
|
||||||
|
|
||||||
qrVerified.setImageBitmap(qrSuccess);
|
|
||||||
qrVerified.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.MULTIPLY);
|
|
||||||
|
|
||||||
animateVerified();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateVerified() {
|
|
||||||
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1,
|
|
||||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
|
|
||||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
|
|
||||||
scaleAnimation.setInterpolator(new OvershootInterpolator());
|
|
||||||
scaleAnimation.setDuration(800);
|
|
||||||
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animation animation) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animation animation) {
|
|
||||||
qrVerified.postDelayed(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0, 1, 0,
|
|
||||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
|
|
||||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
|
|
||||||
|
|
||||||
scaleAnimation.setInterpolator(new AnticipateInterpolator());
|
|
||||||
scaleAnimation.setDuration(500);
|
|
||||||
ViewUtil.animateOut(qrVerified, scaleAnimation, View.GONE);
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationRepeat(Animation animation) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
ViewUtil.animateIn(qrVerified, scaleAnimation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
|
|
||||||
new AsyncTask<Recipient, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Recipient... params) {
|
|
||||||
synchronized (SESSION_LOCK) {
|
|
||||||
if (isChecked) {
|
|
||||||
Log.i(TAG, "Saving identity: " + params[0].getAddress());
|
|
||||||
DatabaseFactory.getIdentityDatabase(getActivity())
|
|
||||||
.saveIdentity(params[0].getAddress(),
|
|
||||||
remoteIdentity,
|
|
||||||
VerifiedStatus.VERIFIED, false,
|
|
||||||
System.currentTimeMillis(), true);
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getIdentityDatabase(getActivity())
|
|
||||||
.setVerified(params[0].getAddress(),
|
|
||||||
remoteIdentity,
|
|
||||||
VerifiedStatus.DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class VerifyScanFragment extends Fragment {
|
|
||||||
|
|
||||||
private View container;
|
|
||||||
private CameraView cameraView;
|
|
||||||
private ScanningThread scanningThread;
|
|
||||||
private ScanListener scanListener;
|
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
|
|
||||||
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_scan_fragment);
|
|
||||||
this.cameraView = ViewUtil.findById(container, R.id.scanner);
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
this.scanningThread = new ScanningThread();
|
|
||||||
this.scanningThread.setScanListener(scanListener);
|
|
||||||
this.scanningThread.setCharacterSet("ISO-8859-1");
|
|
||||||
this.cameraView.onResume();
|
|
||||||
this.cameraView.setPreviewCallback(scanningThread);
|
|
||||||
this.scanningThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
this.cameraView.onPause();
|
|
||||||
this.scanningThread.stopScanning();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfiguration) {
|
|
||||||
super.onConfigurationChanged(newConfiguration);
|
|
||||||
this.cameraView.onPause();
|
|
||||||
this.cameraView.onResume();
|
|
||||||
this.cameraView.setPreviewCallback(scanningThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScanListener(ScanListener listener) {
|
|
||||||
if (this.scanningThread != null) scanningThread.setScanListener(listener);
|
|
||||||
this.scanListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +1,52 @@
|
|||||||
package org.thoughtcrime.securesms.attachments
|
//package org.thoughtcrime.securesms.attachments
|
||||||
|
//
|
||||||
import android.content.Context
|
//import android.content.Context
|
||||||
import com.google.protobuf.ByteString
|
//import com.google.protobuf.ByteString
|
||||||
import org.session.libsession.database.dto.DatabaseAttachmentDTO
|
//import org.session.libsession.database.dto.DatabaseAttachmentDTO
|
||||||
import org.session.libsession.database.MessageDataProvider
|
//import org.session.libsession.database.MessageDataProvider
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
//import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
import org.thoughtcrime.securesms.database.Database
|
//import org.thoughtcrime.securesms.database.Database
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
//import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
//import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil
|
//import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
|
//
|
||||||
class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), MessageDataProvider {
|
//class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), MessageDataProvider {
|
||||||
override fun getAttachment(uniqueID: String): DatabaseAttachmentDTO? {
|
// override fun getAttachment(uniqueID: String): DatabaseAttachmentDTO? {
|
||||||
|
//
|
||||||
val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
|
// val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
|
||||||
val uniqueID = uniqueID.toLongOrNull() ?: return null
|
// val uniqueID = uniqueID.toLongOrNull() ?: return null
|
||||||
val attachmentID = AttachmentId(0, uniqueID)
|
// val attachmentID = AttachmentId(0, uniqueID)
|
||||||
val databaseAttachment = attachmentDatabase.getAttachment(attachmentID) ?: return null
|
// val databaseAttachment = attachmentDatabase.getAttachment(attachmentID) ?: return null
|
||||||
|
//
|
||||||
return databaseAttachment.toDTO()
|
// return databaseAttachment.toDTO()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// Extension to DatabaseAttachment class
|
//// Extension to DatabaseAttachment class
|
||||||
|
//
|
||||||
fun DatabaseAttachment.toDTO(): DatabaseAttachmentDTO {
|
//fun DatabaseAttachment.toDTO(): DatabaseAttachmentDTO {
|
||||||
var databaseAttachmentDTO = DatabaseAttachmentDTO()
|
// var databaseAttachmentDTO = DatabaseAttachmentDTO()
|
||||||
databaseAttachmentDTO.contentType = this.contentType
|
// databaseAttachmentDTO.contentType = this.contentType
|
||||||
databaseAttachmentDTO.fileName = this.fileName
|
// databaseAttachmentDTO.fileName = this.fileName
|
||||||
databaseAttachmentDTO.caption = this.caption
|
// databaseAttachmentDTO.caption = this.caption
|
||||||
|
//
|
||||||
databaseAttachmentDTO.size = this.size.toInt()
|
// databaseAttachmentDTO.size = this.size.toInt()
|
||||||
databaseAttachmentDTO.key = ByteString.copyFrom(this.key?.toByteArray())
|
// databaseAttachmentDTO.key = ByteString.copyFrom(this.key?.toByteArray())
|
||||||
databaseAttachmentDTO.digest = ByteString.copyFrom(this.digest)
|
// databaseAttachmentDTO.digest = ByteString.copyFrom(this.digest)
|
||||||
databaseAttachmentDTO.flags = if (this.isVoiceNote) SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE.number else 0
|
// databaseAttachmentDTO.flags = if (this.isVoiceNote) SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE.number else 0
|
||||||
|
//
|
||||||
databaseAttachmentDTO.url = this.url
|
// databaseAttachmentDTO.url = this.url
|
||||||
|
//
|
||||||
if (this.shouldHaveImageSize()) {
|
// if (this.shouldHaveImageSize()) {
|
||||||
databaseAttachmentDTO.shouldHaveImageSize = true
|
// databaseAttachmentDTO.shouldHaveImageSize = true
|
||||||
databaseAttachmentDTO.width = this.width
|
// databaseAttachmentDTO.width = this.width
|
||||||
databaseAttachmentDTO.height = this.height
|
// databaseAttachmentDTO.height = this.height
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return databaseAttachmentDTO
|
// return databaseAttachmentDTO
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
fun DatabaseAttachment.shouldHaveImageSize(): Boolean {
|
//fun DatabaseAttachment.shouldHaveImageSize(): Boolean {
|
||||||
return (MediaUtil.isVideo(this) || MediaUtil.isImage(this) || MediaUtil.isGif(this));
|
// return (MediaUtil.isVideo(this) || MediaUtil.isImage(this) || MediaUtil.isGif(this));
|
||||||
}
|
//}
|
@ -16,7 +16,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.BindableConversationItem;
|
import org.thoughtcrime.securesms.BindableConversationItem;
|
||||||
import org.thoughtcrime.securesms.VerifyIdentityActivity;
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||||
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;
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.text.style.ClickableSpan;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.VerifyIdentityActivity;
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
|
|
||||||
public class VerifySpan extends ClickableSpan {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Address address;
|
|
||||||
private final IdentityKey identityKey;
|
|
||||||
|
|
||||||
public VerifySpan(@NonNull Context context, @NonNull IdentityKeyMismatch mismatch) {
|
|
||||||
this.context = context;
|
|
||||||
this.address = mismatch.getAddress();
|
|
||||||
this.identityKey = mismatch.getIdentityKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
public VerifySpan(@NonNull Context context, @NonNull Address address, @NonNull IdentityKey identityKey) {
|
|
||||||
this.context = context;
|
|
||||||
this.address = address;
|
|
||||||
this.identityKey = identityKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(@NonNull View widget) {
|
|
||||||
Intent intent = new Intent(context, VerifyIdentityActivity.class);
|
|
||||||
intent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, address);
|
|
||||||
intent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey));
|
|
||||||
intent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, false);
|
|
||||||
context.startActivity(intent);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:fillViewport="true">
|
|
||||||
|
|
||||||
<LinearLayout android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="20dp"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:background="?verification_background"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<FrameLayout android:layout_width="250dp"
|
|
||||||
android:layout_height="250dp">
|
|
||||||
|
|
||||||
<TextView android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:text="@string/verify_display_fragment__loading"/>
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.SquareImageView
|
|
||||||
android:id="@+id/qr_code"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_margin="20dp"
|
|
||||||
android:padding="20dp"
|
|
||||||
android:background="@drawable/qr_code_background"
|
|
||||||
android:visibility="invisible"
|
|
||||||
tools:src="@drawable/splash_logo"
|
|
||||||
tools:visibility="invisible"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/tap_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|center_horizontal"
|
|
||||||
android:layout_marginBottom="35dp"
|
|
||||||
android:textColor="@color/gray50"
|
|
||||||
android:textSize="11sp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:text="@string/verify_display_fragment__tap_to_scan"/>
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.SquareImageView
|
|
||||||
android:id="@+id/qr_verified"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_margin="20dp"
|
|
||||||
android:padding="20dp"
|
|
||||||
android:src="@drawable/ic_check_white_48dp"
|
|
||||||
android:background="@drawable/qr_code_background"
|
|
||||||
android:backgroundTint="@color/green_500"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<TableLayout android:id="@+id/number_table"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<TableRow android:gravity="center_horizontal"
|
|
||||||
android:clickable="false"
|
|
||||||
android:focusable="false">
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_first"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="22934"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_second"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="56944"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_third"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="42738"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_fourth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="20038"/>
|
|
||||||
</TableRow>
|
|
||||||
|
|
||||||
<TableRow android:gravity="center_horizontal">
|
|
||||||
<TextView android:id="@+id/code_fifth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="34431"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_sixth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="24922"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_seventh"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="58594"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_eighth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="24109"/>
|
|
||||||
</TableRow>
|
|
||||||
|
|
||||||
<TableRow android:gravity="center_horizontal">
|
|
||||||
<TextView android:id="@+id/code_ninth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="00257"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_tenth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="34956"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_eleventh"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="32440"/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/code_twelth"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
style="@style/IdentityKey"
|
|
||||||
tools:text="15774"/>
|
|
||||||
</TableRow>
|
|
||||||
</TableLayout>
|
|
||||||
|
|
||||||
<LinearLayout android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:paddingStart="20dp">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/verified_switch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:textSize="17dp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/verify_display_fragment__verified"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/description"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="15dp"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:lineSpacingExtra="3sp"
|
|
||||||
android:text="@string/verify_display_fragment__if_you_wish_to_verify_the_security_of_your_end_to_end_encryption_with_s"/>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
Loading…
Reference in New Issue
Block a user