refactor omemo fingerprint UI code

This commit is contained in:
Daniel Gultsch 2016-11-15 20:00:52 +01:00
parent e84af51272
commit 48afeb571b
51 changed files with 371 additions and 231 deletions

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_verified_fingerprint.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1916"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-3.3559322"
inkscape:cy="24"
inkscape:window-x="0"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 2L6 10v12c0 11.11 7.67 21.47 18 24 10.33-2.53 18-12.89 18-24V10L24 2zm-4 32l-8-8 2.83-2.83L20 28.34l13.17-13.17L36 18 20 34z"
id="path4"
style="fill:#259b24;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -50,6 +50,7 @@ images = {
'ic_notifications_off_white80.svg' => ['ic_notifications_off_white80', 24], 'ic_notifications_off_white80.svg' => ['ic_notifications_off_white80', 24],
'ic_notifications_paused_white80.svg' => ['ic_notifications_paused_white80', 24], 'ic_notifications_paused_white80.svg' => ['ic_notifications_paused_white80', 24],
'ic_notifications_white80.svg' => ['ic_notifications_white80', 24], 'ic_notifications_white80.svg' => ['ic_notifications_white80', 24],
'ic_verified_fingerprint.svg' => ['ic_verified_fingerprint', 36],
'md_switch_thumb_disable.svg' => ['switch_thumb_disable', 48], 'md_switch_thumb_disable.svg' => ['switch_thumb_disable', 48],
'md_switch_thumb_off_normal.svg' => ['switch_thumb_off_normal', 48], 'md_switch_thumb_off_normal.svg' => ['switch_thumb_off_normal', 48],
'md_switch_thumb_off_pressed.svg' => ['switch_thumb_off_pressed', 48], 'md_switch_thumb_off_pressed.svg' => ['switch_thumb_off_pressed', 48],

View file

@ -0,0 +1,237 @@
package eu.siacs.conversations;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.security.cert.X509Certificate;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.widget.Switch;
import eu.siacs.conversations.utils.CryptoHelper;
public abstract class OmemoActivity extends XmppActivity {
private Account mSelectedAccount;
private String mSelectedFingerprint;
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu,v,menuInfo);
Object account = v.getTag(R.id.TAG_ACCOUNT);
Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
if (account != null && fingerprint != null && account instanceof Account && fingerprint instanceof String) {
getMenuInflater().inflate(R.menu.omemo_key_context, menu);
this.mSelectedAccount = (Account) account;
this.mSelectedFingerprint = (String) fingerprint;
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.purge_omemo_key:
showPurgeKeyDialog(mSelectedAccount,mSelectedFingerprint);
break;
case R.id.copy_omemo_key:
copyOmemoFingerprint(mSelectedFingerprint);
break;
}
return true;
}
protected void copyOmemoFingerprint(String fingerprint) {
if (copyTextToClipboard(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)), R.string.omemo_fingerprint)) {
Toast.makeText(
this,
R.string.toast_message_omemo_fingerprint,
Toast.LENGTH_SHORT).show();
}
}
protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight) {
final FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
return status != null && addFingerprintRowWithListeners(keys, account, fingerprint, highlight, status, true, true, new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(isChecked));
}
});
}
protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account,
final String fingerprint,
boolean highlight,
FingerprintStatus status,
boolean showTag,
boolean undecidedNeedEnablement,
CompoundButton.OnCheckedChangeListener
onCheckedChangeListener) {
if (status.isCompromised()) {
return false;
}
View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false);
TextView key = (TextView) view.findViewById(R.id.key);
TextView keyType = (TextView) view.findViewById(R.id.key_type);
if (Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
showX509Certificate(account,fingerprint);
}
};
key.setOnClickListener(listener);
keyType.setOnClickListener(listener);
}
Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust);
ImageView verifiedFingerprintSymbol = (ImageView) view.findViewById(R.id.verified_fingerprint);
trustToggle.setVisibility(View.VISIBLE);
registerForContextMenu(view);
view.setTag(R.id.TAG_ACCOUNT,account);
view.setTag(R.id.TAG_FINGERPRINT,fingerprint);
boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
final View.OnClickListener toast;
trustToggle.setChecked(status.isTrusted(), false);
if (status.isActive()){
key.setTextColor(getPrimaryTextColor());
keyType.setTextColor(getSecondaryTextColor());
if (status.isVerified()) {
verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
trustToggle.setVisibility(View.GONE);
verifiedFingerprintSymbol.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
replaceToast(getString(R.string.this_device_has_been_verified), false);
}
});
toast = null;
} else {
verifiedFingerprintSymbol.setVisibility(View.GONE);
trustToggle.setVisibility(View.VISIBLE);
trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED && undecidedNeedEnablement) {
trustToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
v.setEnabled(true);
v.setOnClickListener(null);
}
});
trustToggle.setEnabled(false);
} else {
trustToggle.setOnClickListener(null);
trustToggle.setEnabled(true);
}
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
hideToast();
}
};
}
} else {
key.setTextColor(getTertiaryTextColor());
keyType.setTextColor(getTertiaryTextColor());
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
}
};
if (status.isVerified()) {
trustToggle.setVisibility(View.GONE);
verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
verifiedFingerprintSymbol.setOnClickListener(toast);
} else {
trustToggle.setVisibility(View.VISIBLE);
verifiedFingerprintSymbol.setVisibility(View.GONE);
trustToggle.setOnClickListener(null);
trustToggle.setEnabled(false);
trustToggle.setOnClickListener(toast);
}
}
view.setOnClickListener(toast);
key.setOnClickListener(toast);
keyType.setOnClickListener(toast);
if (showTag) {
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
} else {
keyType.setVisibility(View.GONE);
}
if (highlight) {
keyType.setTextColor(getResources().getColor(R.color.accent));
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
} else {
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
}
key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
keys.addView(view);
return true;
}
public void showPurgeKeyDialog(final Account account, final String fingerprint) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.purge_key));
builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setMessage(getString(R.string.purge_key_desc_part1)
+ "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2))
+ "\n\n" + getString(R.string.purge_key_desc_part2));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.purge_key),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
account.getAxolotlService().purgeKey(fingerprint);
refreshUi();
}
});
builder.create().show();
}
private void showX509Certificate(Account account, String fingerprint) {
X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint);
if (x509Certificate != null) {
showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
} else {
Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
}
}
private void showCertificateInformationDialog(Bundle bundle) {
View view = getLayoutInflater().inflate(R.layout.certificate_information, null);
final String not_available = getString(R.string.certicate_info_not_available);
TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn);
TextView subject_o = (TextView) view.findViewById(R.id.subject_o);
TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn);
TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o);
TextView sha1 = (TextView) view.findViewById(R.id.sha1);
subject_cn.setText(bundle.getString("subject_cn", not_available));
subject_o.setText(bundle.getString("subject_o", not_available));
issuer_cn.setText(bundle.getString("issuer_cn", not_available));
issuer_o.setText(bundle.getString("issuer_o", not_available));
sha1.setText(bundle.getString("sha1", not_available));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.certificate_information);
builder.setView(view);
builder.setPositiveButton(R.string.ok, null);
builder.create().show();
}
}

View file

@ -36,15 +36,14 @@ import java.security.cert.X509Certificate;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.OmemoActivity;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
@ -55,7 +54,7 @@ import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated { public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated {
public static final String ACTION_VIEW_CONTACT = "view_contact"; public static final String ACTION_VIEW_CONTACT = "view_contact";
private Contact contact; private Contact contact;
@ -448,12 +447,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
if (Config.supportOmemo()) { if (Config.supportOmemo()) {
for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) { for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) {
boolean highlight = fingerprint.equals(messageFingerprint); boolean highlight = fingerprint.equals(messageFingerprint);
hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight, new OnClickListener() { hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight);
@Override
public void onClick(View v) {
onOmemoKeyClicked(contact.getAccount(), fingerprint);
}
});
} }
} }
if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) { if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) {
@ -509,40 +503,6 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} }
} }
private void onOmemoKeyClicked(Account account, String fingerprint) {
FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
if (Config.X509_VERIFICATION && status != null && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint);
if (x509Certificate != null) {
showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
} else {
Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
}
}
}
private void showCertificateInformationDialog(Bundle bundle) {
View view = getLayoutInflater().inflate(R.layout.certificate_information, null);
final String not_available = getString(R.string.certicate_info_not_available);
TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn);
TextView subject_o = (TextView) view.findViewById(R.id.subject_o);
TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn);
TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o);
TextView sha1 = (TextView) view.findViewById(R.id.sha1);
subject_cn.setText(bundle.getString("subject_cn", not_available));
subject_o.setText(bundle.getString("subject_o", not_available));
issuer_cn.setText(bundle.getString("issuer_cn", not_available));
issuer_o.setText(bundle.getString("issuer_o", not_available));
sha1.setText(bundle.getString("sha1", not_available));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.certificate_information);
builder.setView(view);
builder.setPositiveButton(R.string.ok, null);
builder.create().show();
}
protected void confirmToDeleteFingerprint(final String fingerprint) { protected void confirmToDeleteFingerprint(final String fingerprint) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.delete_fingerprint); builder.setTitle(R.string.delete_fingerprint);
@ -563,7 +523,6 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
builder.create().show(); builder.create().show();
} }
@Override
public void onBackendConnected() { public void onBackendConnected() {
if ((accountJid != null) && (contactJid != null)) { if ((accountJid != null) && (contactJid != null)) {
Account account = xmppConnectionService Account account = xmppConnectionService

View file

@ -41,6 +41,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.OmemoActivity;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -59,7 +60,7 @@ import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.pep.Avatar;
public class EditAccountActivity extends XmppActivity implements OnAccountUpdate, public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate,
OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched { OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
private static final int REQUEST_DATA_SAVER = 0x37af244; private static final int REQUEST_DATA_SAVER = 0x37af244;
@ -635,7 +636,6 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
super.onSaveInstanceState(savedInstanceState); super.onSaveInstanceState(savedInstanceState);
} }
@Override
protected void onBackendConnected() { protected void onBackendConnected() {
boolean init = true; boolean init = true;
if (mSavedInstanceAccount != null) { if (mSavedInstanceAccount != null) {
@ -884,13 +884,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
copyOmemoFingerprint(ownAxolotlFingerprint);
if (copyTextToClipboard(ownAxolotlFingerprint.substring(2), R.string.omemo_fingerprint)) {
Toast.makeText(
EditAccountActivity.this,
R.string.toast_message_omemo_fingerprint,
Toast.LENGTH_SHORT).show();
}
} }
}); });
if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) { if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
@ -915,7 +909,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
continue; continue;
} }
boolean highlight = fingerprint.equals(messageFingerprint); boolean highlight = fingerprint.equals(messageFingerprint);
hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight, null); hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
} }
if (hasKeys && Config.supportOmemo()) { if (hasKeys && Config.supportOmemo()) {
keysCard.setVisibility(View.VISIBLE); keysCard.setVisibility(View.VISIBLE);

View file

@ -18,6 +18,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import eu.siacs.conversations.OmemoActivity;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
@ -28,7 +29,7 @@ import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated { public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {
private List<Jid> contactJids; private List<Jid> contactJids;
private Account mAccount; private Account mAccount;
@ -109,16 +110,14 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
for(final String fingerprint : ownKeysToTrust.keySet()) { for(final String fingerprint : ownKeysToTrust.keySet()) {
hasOwnKeys = true; hasOwnKeys = true;
addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false, addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, false,
new CompoundButton.OnCheckedChangeListener() { new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
ownKeysToTrust.put(fingerprint, isChecked); ownKeysToTrust.put(fingerprint, isChecked);
// own fingerprints have no impact on locked status. // own fingerprints have no impact on locked status.
} }
}, }
null,
null
); );
} }
@ -134,16 +133,14 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
final Map<String, Boolean> fingerprints = entry.getValue(); final Map<String, Boolean> fingerprints = entry.getValue();
for (final String fingerprint : fingerprints.keySet()) { for (final String fingerprint : fingerprints.keySet()) {
addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false, addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, false,
new CompoundButton.OnCheckedChangeListener() { new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
fingerprints.put(fingerprint, isChecked); fingerprints.put(fingerprint, isChecked);
lockOrUnlockAsNeeded(); lockOrUnlockAsNeeded();
} }
}, }
null,
null
); );
} }
if (fingerprints.size() == 0) { if (fingerprints.size() == 0) {
@ -212,7 +209,6 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
return ownKeysSet.size() + foreignKeysToTrust.size() > 0; return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
} }
@Override
public void onBackendConnected() { public void onBackendConnected() {
Intent intent = getIntent(); Intent intent = getIntent();
this.mAccount = extractAccount(intent); this.mAccount = extractAccount(intent);

View file

@ -49,11 +49,8 @@ import android.util.Pair;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.CompoundButton;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
@ -69,7 +66,6 @@ import java.io.FileNotFoundException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -78,20 +74,15 @@ import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder; import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.ui.widget.Switch;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
@ -780,132 +771,6 @@ public abstract class XmppActivity extends Activity {
builder.create().show(); builder.create().show();
} }
protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight, View.OnClickListener onKeyClickedListener) {
final FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
if (status == null) {
return false;
}
return addFingerprintRowWithListeners(keys, account, fingerprint, highlight, status, true,
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(isChecked));
}
},
new View.OnClickListener() {
@Override
public void onClick(View v) {
account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
v.setEnabled(true);
}
},
onKeyClickedListener
);
}
protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account,
final String fingerprint,
boolean highlight,
FingerprintStatus status,
boolean showTag,
CompoundButton.OnCheckedChangeListener
onCheckedChangeListener,
View.OnClickListener onClickListener,
View.OnClickListener onKeyClickedListener) {
if (status.isCompromised()) {
return false;
}
View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false);
TextView key = (TextView) view.findViewById(R.id.key);
key.setOnClickListener(onKeyClickedListener);
TextView keyType = (TextView) view.findViewById(R.id.key_type);
keyType.setOnClickListener(onKeyClickedListener);
Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust);
trustToggle.setVisibility(View.VISIBLE);
final View.OnLongClickListener purge = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
showPurgeKeyDialog(account, fingerprint);
return true;
}
};
view.setOnLongClickListener(purge);
key.setOnLongClickListener(purge);
keyType.setOnLongClickListener(purge);
boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
final View.OnClickListener toast;
trustToggle.setChecked(status.isTrusted(), false);
if (status.isActive()) {
key.setTextColor(getPrimaryTextColor());
keyType.setTextColor(getSecondaryTextColor());
trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED) {
trustToggle.setOnClickListener(onClickListener);
trustToggle.setEnabled(false);
} else {
trustToggle.setOnClickListener(null);
trustToggle.setEnabled(true);
}
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
hideToast();
}
};
} else {
key.setTextColor(getTertiaryTextColor());
keyType.setTextColor(getTertiaryTextColor());
trustToggle.setOnClickListener(null);
trustToggle.setEnabled(false);
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
}
};
trustToggle.setOnClickListener(toast);
}
view.setOnClickListener(toast);
key.setOnClickListener(toast);
keyType.setOnClickListener(toast);
if (showTag) {
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
} else {
keyType.setVisibility(View.GONE);
}
if (highlight) {
keyType.setTextColor(getResources().getColor(R.color.accent));
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
} else {
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
}
key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
keys.addView(view);
return true;
}
public void showPurgeKeyDialog(final Account account, final String fingerprint) {
Builder builder = new Builder(this);
builder.setTitle(getString(R.string.purge_key));
builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setMessage(getString(R.string.purge_key_desc_part1)
+ "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2))
+ "\n\n" + getString(R.string.purge_key_desc_part2));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.purge_key),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
account.getAxolotlService().purgeKey(fingerprint);
refreshUi();
}
});
builder.create().show();
}
public boolean hasStoragePermission(int requestCode) { public boolean hasStoragePermission(int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 B

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 595 B

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 928 B

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 935 B

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 857 B

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,37 +1,40 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" > android:layout_height="match_parent"
android:longClickable="true">
<RelativeLayout <RelativeLayout
android:id="@+id/key_data" android:id="@+id/key_data"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:paddingTop="8dp" android:paddingBottom="8dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingBottom="8dp"> android:paddingTop="8dp">
<TextView <TextView
android:id="@+id/key" android:id="@+id/key"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?attr/color_text_primary"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/tgl_trust" android:layout_toLeftOf="@+id/action_container"
android:fontFamily="monospace"
android:textColor="?attr/color_text_primary"
android:textSize="?attr/TextSizeBody" android:textSize="?attr/TextSizeBody"
android:typeface="monospace" android:typeface="monospace"
android:fontFamily="monospace"/> android:longClickable="true"/>
<TextView <TextView
android:id="@+id/key_type" android:id="@+id/key_type"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?attr/color_text_secondary"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_below="@+id/key" android:layout_below="@+id/key"
android:maxLines="1" android:maxLines="1"
android:textSize="?attr/TextSizeInfo"/> android:textColor="?attr/color_text_secondary"
android:textSize="?attr/TextSizeInfo"
android:longClickable="true"/>
<TextView <TextView
android:id="@+id/key_trust" android:id="@+id/key_trust"
@ -39,32 +42,46 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_below="@+id/key" android:layout_below="@+id/key"
android:visibility="gone"
android:textColor="?attr/color_text_secondary" android:textColor="?attr/color_text_secondary"
android:textSize="?attr/TextSizeInfo"/> android:textSize="?attr/TextSizeInfo"
android:visibility="gone"
android:longClickable="true"/>
<LinearLayout
android:id="@+id/action_container"
android:layout_width="96dp"
android:layout_marginRight="-32dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentRight="true"
android:layout_centerVertical="true">
<ImageButton <ImageButton
android:layout_gravity="center_horizontal"
android:id="@+id/button_remove" android:id="@+id/button_remove"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:alpha="?attr/icon_alpha"
android:layout_toRightOf="@+id/key"
android:layout_centerVertical="true"
android:background="?android:selectableItemBackground" android:background="?android:selectableItemBackground"
android:padding="@dimen/image_button_padding" android:padding="@dimen/image_button_padding"
android:alpha="?attr/icon_alpha"
android:src="?attr/icon_remove" android:src="?attr/icon_remove"
android:layout_marginRight="16dp"
android:visibility="gone" /> android:visibility="gone" />
<ImageView
android:visibility="gone"
android:id="@+id/verified_fingerprint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginRight="16dp"
android:src="@drawable/ic_verified_fingerprint" />
<eu.siacs.conversations.ui.widget.Switch <eu.siacs.conversations.ui.widget.Switch
android:id="@+id/tgl_trust" android:id="@+id/tgl_trust"
android:visibility="invisible" style="@style/MD"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:visibility="gone" />
android:layout_centerVertical="true" </LinearLayout>
style="@style/MD"/>
</RelativeLayout> </RelativeLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/purge_omemo_key"
android:title="@string/purge_key"/>
<item
android:id="@+id/copy_omemo_key"
android:title="@string/copy_fingerprint"/>
</menu>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="TAG_ACCOUNT"/>
<item type="id" name="TAG_FINGERPRINT"/>
</resources>

View file

@ -699,4 +699,7 @@
<string name="data_saver_enabled">Data saver enabled</string> <string name="data_saver_enabled">Data saver enabled</string>
<string name="data_saver_enabled_explained">Your operating system is restricting Conversations from accessing the Internet when in background. To receive notifications of new messages you should allow Conversations unrestricted access when Data saver is on.\nConversations will still make an effort to save data when possible.</string> <string name="data_saver_enabled_explained">Your operating system is restricting Conversations from accessing the Internet when in background. To receive notifications of new messages you should allow Conversations unrestricted access when Data saver is on.\nConversations will still make an effort to save data when possible.</string>
<string name="device_does_not_support_data_saver">Your device does not support disabling Data saver for Conversations.</string> <string name="device_does_not_support_data_saver">Your device does not support disabling Data saver for Conversations.</string>
<string name="error_unable_to_create_temporary_file">Unable to create temporary file</string>
<string name="this_device_has_been_verified">This device has been verified</string>
<string name="copy_fingerprint">Copy fingerprint</string>
</resources> </resources>