Refactor trust key ui and show in account details

Refactored the trust key row UI element so it can be used in multiple
places. It now also uses a slider to toggle the trust state, and the
redundant trust state description was removed.

EditAccountActivity now shows the keys of other devices associated with
that account.
This commit is contained in:
Andreas Straub 2015-07-19 14:09:49 +02:00
parent 9c4d55f82c
commit 6f67469bda
8 changed files with 154 additions and 58 deletions

View file

@ -38,6 +38,7 @@ dependencies {
compile 'me.leolin:ShortcutBadger:1.1.1@aar' compile 'me.leolin:ShortcutBadger:1.1.1@aar'
compile 'com.kyleduo.switchbutton:library:1.2.8' compile 'com.kyleduo.switchbutton:library:1.2.8'
compile 'org.whispersystems:axolotl-android:1.3.4' compile 'org.whispersystems:axolotl-android:1.3.4'
compile 'com.kyleduo.switchbutton:library:1.2.8'
} }
android { android {

View file

@ -35,7 +35,6 @@ import java.util.List;
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.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;
@ -381,53 +380,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
for(final IdentityKey identityKey : xmppConnectionService.databaseBackend.loadIdentityKeys( for(final IdentityKey identityKey : xmppConnectionService.databaseBackend.loadIdentityKeys(
contact.getAccount(), contact.getJid().toBareJid().toString())) { contact.getAccount(), contact.getJid().toBareJid().toString())) {
hasKeys = true; hasKeys = true;
View view = inflater.inflate(R.layout.contact_key, keys, false); addFingerprintRow(keys, contact.getAccount(), identityKey);
TextView key = (TextView) view.findViewById(R.id.key);
TextView keyType = (TextView) view.findViewById(R.id.key_type);
TextView keyTrust = (TextView) view.findViewById(R.id.key_trust);
ImageButton removeButton = (ImageButton) view
.findViewById(R.id.button_remove);
ImageButton trustButton = (ImageButton) view
.findViewById(R.id.button_trust);
final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
final String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
final Jid bareJid = contactJid.toBareJid();
AxolotlService.SQLiteAxolotlStore.Trust trust = contact.getAccount().getAxolotlService()
.getFingerprintTrust(fingerprint);
switch (trust) {
case TRUSTED:
removeButton.setVisibility(View.VISIBLE);
//Log.d(Config.LOGTAG, AxolotlService.getLogprefix(contact.getAccount()) + "Setting remove button visible!");
break;
case UNDECIDED:
case UNTRUSTED:
//Log.d(Config.LOGTAG, AxolotlService.getLogprefix(contact.getAccount()) + "Setting trust button visible!");
trustButton.setVisibility(View.VISIBLE);
break;
}
keyType.setText("Axolotl Fingerprint");
key.setText(CryptoHelper.prettifyFingerprint(identityKey.getFingerprint()));
keyTrust.setText(trust.toString());
keyTrust.setVisibility(View.VISIBLE);
keys.addView(view);
removeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
axolotlService.setFingerprintTrust(fingerprint,
AxolotlService.SQLiteAxolotlStore.Trust.UNTRUSTED);
refreshUi();
xmppConnectionService.updateConversationUi();
}
});
trustButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
axolotlService.setFingerprintTrust(fingerprint,
AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED);
refreshUi();
xmppConnectionService.updateConversationUi();
}
});
} }
if (contact.getPgpKeyId() != 0) { if (contact.getPgpKeyId() != 0) {
hasKeys = true; hasKeys = true;

View file

@ -26,6 +26,8 @@ import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.whispersystems.libaxolotl.IdentityKey;
import java.util.Set; import java.util.Set;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
@ -69,6 +71,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private ImageButton mAxolotlFingerprintToClipboardButton; private ImageButton mAxolotlFingerprintToClipboardButton;
private ImageButton mWipeAxolotlPepButton; private ImageButton mWipeAxolotlPepButton;
private ImageButton mRegenerateAxolotlKeyButton; private ImageButton mRegenerateAxolotlKeyButton;
private LinearLayout keys;
private LinearLayout keysCard;
private Jid jidToEdit; private Jid jidToEdit;
private Account mAccount; private Account mAccount;
@ -329,6 +333,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mAxolotlDevicelist = (TextView) findViewById(R.id.axolotl_devicelist); this.mAxolotlDevicelist = (TextView) findViewById(R.id.axolotl_devicelist);
this.mAxolotlDevicelistBox = (RelativeLayout) findViewById(R.id.axolotl_devices_box); this.mAxolotlDevicelistBox = (RelativeLayout) findViewById(R.id.axolotl_devices_box);
this.mWipeAxolotlPepButton = (ImageButton) findViewById(R.id.action_wipe_axolotl_pep); this.mWipeAxolotlPepButton = (ImageButton) findViewById(R.id.action_wipe_axolotl_pep);
this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
this.mSaveButton = (Button) findViewById(R.id.save_button); this.mSaveButton = (Button) findViewById(R.id.save_button);
this.mCancelButton = (Button) findViewById(R.id.cancel_button); this.mCancelButton = (Button) findViewById(R.id.cancel_button);
this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener); this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
@ -568,6 +574,22 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} else { } else {
this.mAxolotlFingerprintBox.setVisibility(View.GONE); this.mAxolotlFingerprintBox.setVisibility(View.GONE);
} }
final IdentityKey ownKey = mAccount.getAxolotlService().getOwnPublicKey();
boolean hasKeys = false;
keys.removeAllViews();
for(final IdentityKey identityKey : xmppConnectionService.databaseBackend.loadIdentityKeys(
mAccount, mAccount.getJid().toBareJid().toString())) {
if(ownKey.equals(identityKey)) {
continue;
}
hasKeys = true;
addFingerprintRow(keys, mAccount, identityKey);
}
if (hasKeys) {
keysCard.setVisibility(View.VISIBLE);
} else {
keysCard.setVisibility(View.GONE);
}
} else { } else {
if (this.mAccount.errorStatus()) { if (this.mAccount.errorStatus()) {
this.mAccountJid.setError(getString(this.mAccount.getStatus().getReadableId())); this.mAccountJid.setError(getString(this.mAccount.getStatus().getReadableId()));

View file

@ -43,8 +43,11 @@ import android.util.Log;
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;
@ -53,9 +56,12 @@ import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.kyleduo.switchbutton.SwitchButton;
import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionID;
import org.whispersystems.libaxolotl.IdentityKey;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
@ -65,6 +71,7 @@ import java.util.concurrent.RejectedExecutionException;
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.AxolotlService;
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;
@ -74,6 +81,8 @@ import eu.siacs.conversations.entities.Presences;
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.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@ -588,6 +597,76 @@ public abstract class XmppActivity extends Activity {
builder.create().show(); builder.create().show();
} }
protected void addFingerprintRow(LinearLayout keys, final Account account, IdentityKey identityKey) {
final String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
final AxolotlService.SQLiteAxolotlStore.Trust trust = account.getAxolotlService()
.getFingerprintTrust(fingerprint);
addFingerprintRowWithListeners(keys, account, identityKey, trust, true,
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked != (trust == AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED)) {
account.getAxolotlService().setFingerprintTrust(fingerprint,
(isChecked) ? AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED :
AxolotlService.SQLiteAxolotlStore.Trust.UNTRUSTED);
}
refreshUi();
xmppConnectionService.updateAccountUi();
xmppConnectionService.updateConversationUi();
}
},
new View.OnClickListener() {
@Override
public void onClick(View v) {
account.getAxolotlService().setFingerprintTrust(fingerprint,
AxolotlService.SQLiteAxolotlStore.Trust.UNTRUSTED);
refreshUi();
xmppConnectionService.updateAccountUi();
xmppConnectionService.updateConversationUi();
}
}
);
}
protected void addFingerprintRowWithListeners(LinearLayout keys, final Account account,
IdentityKey identityKey,
AxolotlService.SQLiteAxolotlStore.Trust trust,
boolean showTag,
CompoundButton.OnCheckedChangeListener
onCheckedChangeListener,
View.OnClickListener onClickListener) {
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);
Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust);
trustToggle.setVisibility(View.VISIBLE);
trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
trustToggle.setOnClickListener(onClickListener);
switch (trust) {
case UNTRUSTED:
case TRUSTED:
trustToggle.setChecked(trust == AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED, false);
trustToggle.setEnabled(true);
break;
case UNDECIDED:
trustToggle.setChecked(false, false);
trustToggle.setEnabled(false);
break;
}
if (showTag) {
keyType.setText(getString(R.string.axolotl_fingerprint));
} else {
keyType.setVisibility(View.GONE);
}
key.setText(CryptoHelper.prettifyFingerprint(identityKey.getFingerprint()));
keys.addView(view);
}
public void selectPresence(final Conversation conversation, public void selectPresence(final Conversation conversation,
final OnPresenceSelected listener) { final OnPresenceSelected listener) {
final Contact contact = conversation.getContact(); final Contact contact = conversation.getContact();

View file

@ -368,7 +368,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@color/black54" android:textColor="@color/black54"
android:textSize="?attr/TextSizeInfo" android:textSize="?attr/TextSizeInfo"
android:text="@string/axolotl_fingerprint"/> android:text="@string/this_device_axolotl_fingerprint"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@ -444,6 +444,37 @@
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/other_device_keys_card"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:background="@drawable/infocard_border"
android:orientation="vertical"
android:padding="@dimen/infocard_padding"
android:visibility="gone">
<TextView
android:id="@+id/other_device_keys_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black87"
android:textSize="?attr/TextSizeHeadline"
android:textStyle="bold"
android:text="@string/other_devices"/>
<LinearLayout
android:id="@+id/other_device_keys"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
android:showDividers="middle" >
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View file

@ -4,10 +4,10 @@
android:layout_height="match_parent" > android:layout_height="match_parent" >
<RelativeLayout <RelativeLayout
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:layout_toLeftOf="@+id/button_remove"
android:padding="8dp" > android:padding="8dp" >
<TextView <TextView
@ -16,6 +16,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@color/black87" android:textColor="@color/black87"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/tgl_trust"
android:textSize="?attr/TextSizeBody" android:textSize="?attr/TextSizeBody"
android:typeface="monospace" /> android:typeface="monospace" />
@ -37,27 +38,28 @@
android:visibility="gone" android:visibility="gone"
android:textColor="@color/black54" android:textColor="@color/black54"
android:textSize="?attr/TextSizeInfo"/> android:textSize="?attr/TextSizeInfo"/>
</RelativeLayout>
<ImageButton <ImageButton
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:layout_alignParentRight="true"
android:layout_toRightOf="@+id/key"
android:layout_centerVertical="true" 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:src="?attr/icon_remove" android:src="?attr/icon_remove"
android:visibility="invisible" /> android:visibility="gone" />
<ImageButton
android:id="@+id/button_trust" <eu.siacs.conversations.ui.widget.Switch
android:id="@+id/tgl_trust"
android:visibility="invisible"
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:layout_alignParentRight="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:background="?android:selectableItemBackground" style="@style/MaterialDesignButton"/>
android:padding="@dimen/image_button_padding"
android:src="?attr/icon_done" </RelativeLayout>
android:visibility="invisible" />
</RelativeLayout> </RelativeLayout>

View file

@ -209,6 +209,8 @@
<string name="your_fingerprint">Your fingerprint</string> <string name="your_fingerprint">Your fingerprint</string>
<string name="otr_fingerprint">OTR fingerprint</string> <string name="otr_fingerprint">OTR fingerprint</string>
<string name="axolotl_fingerprint">Axolotl fingerprint</string> <string name="axolotl_fingerprint">Axolotl fingerprint</string>
<string name="this_device_axolotl_fingerprint">Own Axolotl fingerprint</string>
<string name="other_devices">Other devices</string>
<string name="axolotl_devicelist">Other own Axolotl Devices</string> <string name="axolotl_devicelist">Other own Axolotl Devices</string>
<string name="verify">Verify</string> <string name="verify">Verify</string>
<string name="decrypt">Decrypt</string> <string name="decrypt">Decrypt</string>

View file

@ -18,4 +18,10 @@
<item name="android:padding">16dp</item> <item name="android:padding">16dp</item>
</style> </style>
<style name="MaterialDesignButton" parent="MD">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">16dp</item>
</style>
</resources> </resources>