TrustKeysActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.content.Intent;
  4import android.os.Bundle;
  5import android.view.View;
  6import android.view.View.OnClickListener;
  7import android.widget.Button;
  8import android.widget.CompoundButton;
  9import android.widget.LinearLayout;
 10import android.widget.TextView;
 11
 12import org.whispersystems.libaxolotl.IdentityKey;
 13
 14import java.util.HashMap;
 15import java.util.Map;
 16import java.util.Set;
 17
 18import eu.siacs.conversations.R;
 19import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
 20import eu.siacs.conversations.entities.Account;
 21import eu.siacs.conversations.entities.Contact;
 22import eu.siacs.conversations.entities.Conversation;
 23import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
 24import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 25import eu.siacs.conversations.xmpp.jid.Jid;
 26
 27public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated {
 28	private Jid accountJid;
 29	private Jid contactJid;
 30	private boolean hasOtherTrustedKeys = false;
 31	private boolean hasPendingFetches = false;
 32	private boolean hasNoTrustedKeys = true;
 33
 34	private Contact contact;
 35	private TextView ownKeysTitle;
 36	private LinearLayout ownKeys;
 37	private LinearLayout ownKeysCard;
 38	private TextView foreignKeysTitle;
 39	private LinearLayout foreignKeys;
 40	private LinearLayout foreignKeysCard;
 41	private Button mSaveButton;
 42	private Button mCancelButton;
 43
 44	private final Map<IdentityKey, Boolean> ownKeysToTrust = new HashMap<>();
 45	private final Map<IdentityKey, Boolean> foreignKeysToTrust = new HashMap<>();
 46
 47	private final OnClickListener mSaveButtonListener = new OnClickListener() {
 48		@Override
 49		public void onClick(View v) {
 50			commitTrusts();
 51			Intent data = new Intent();
 52			data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
 53			setResult(RESULT_OK, data);
 54			finish();
 55		}
 56	};
 57
 58	private final OnClickListener mCancelButtonListener = new OnClickListener() {
 59		@Override
 60		public void onClick(View v) {
 61			setResult(RESULT_CANCELED);
 62			finish();
 63		}
 64	};
 65
 66	@Override
 67	protected void refreshUiReal() {
 68		invalidateOptionsMenu();
 69		populateView();
 70	}
 71
 72	@Override
 73	protected String getShareableUri() {
 74		if (contact != null) {
 75			return contact.getShareableUri();
 76		} else {
 77			return "";
 78		}
 79	}
 80
 81	@Override
 82	protected void onCreate(final Bundle savedInstanceState) {
 83		super.onCreate(savedInstanceState);
 84		setContentView(R.layout.activity_trust_keys);
 85		try {
 86			this.accountJid = Jid.fromString(getIntent().getExtras().getString("account"));
 87		} catch (final InvalidJidException ignored) {
 88		}
 89		try {
 90			this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
 91		} catch (final InvalidJidException ignored) {
 92		}
 93		hasNoTrustedKeys = getIntent().getBooleanExtra("has_no_trusted", false);
 94
 95		ownKeysTitle = (TextView) findViewById(R.id.own_keys_title);
 96		ownKeys = (LinearLayout) findViewById(R.id.own_keys_details);
 97		ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card);
 98		foreignKeysTitle = (TextView) findViewById(R.id.foreign_keys_title);
 99		foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys_details);
100		foreignKeysCard = (LinearLayout) findViewById(R.id.foreign_keys_card);
101		mCancelButton = (Button) findViewById(R.id.cancel_button);
102		mCancelButton.setOnClickListener(mCancelButtonListener);
103		mSaveButton = (Button) findViewById(R.id.save_button);
104		mSaveButton.setOnClickListener(mSaveButtonListener);
105
106
107		if (getActionBar() != null) {
108			getActionBar().setHomeButtonEnabled(true);
109			getActionBar().setDisplayHomeAsUpEnabled(true);
110		}
111	}
112
113	private void populateView() {
114		setTitle(getString(R.string.trust_keys));
115		ownKeys.removeAllViews();
116		foreignKeys.removeAllViews();
117		boolean hasOwnKeys = false;
118		boolean hasForeignKeys = false;
119		for(final IdentityKey identityKey : ownKeysToTrust.keySet()) {
120			hasOwnKeys = true;
121			addFingerprintRowWithListeners(ownKeys, contact.getAccount(), identityKey, false,
122					XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(identityKey)), false,
123					new CompoundButton.OnCheckedChangeListener() {
124						@Override
125						public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
126							ownKeysToTrust.put(identityKey, isChecked);
127							// own fingerprints have no impact on locked status.
128						}
129					},
130					null
131			);
132		}
133		for(final IdentityKey identityKey : foreignKeysToTrust.keySet()) {
134			hasForeignKeys = true;
135			addFingerprintRowWithListeners(foreignKeys, contact.getAccount(), identityKey, false,
136					XmppAxolotlSession.Trust.fromBoolean(foreignKeysToTrust.get(identityKey)), false,
137					new CompoundButton.OnCheckedChangeListener() {
138						@Override
139						public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
140							foreignKeysToTrust.put(identityKey, isChecked);
141							lockOrUnlockAsNeeded();
142						}
143					},
144					null
145			);
146		}
147
148		if(hasOwnKeys) {
149			ownKeysTitle.setText(accountJid.toString());
150			ownKeysCard.setVisibility(View.VISIBLE);
151		}
152		if(hasForeignKeys) {
153			foreignKeysTitle.setText(contactJid.toString());
154			foreignKeysCard.setVisibility(View.VISIBLE);
155		}
156		if(hasPendingFetches) {
157			setFetching();
158			lock();
159		} else {
160			lockOrUnlockAsNeeded();
161			setDone();
162		}
163	}
164
165	private void getFingerprints(final Account account) {
166		Set<IdentityKey> ownKeysSet = account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED);
167		Set<IdentityKey> foreignKeysSet = account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact);
168		if (hasNoTrustedKeys) {
169			ownKeysSet.addAll(account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED));
170			foreignKeysSet.addAll(account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact));
171		}
172		for(final IdentityKey identityKey : ownKeysSet) {
173			if(!ownKeysToTrust.containsKey(identityKey)) {
174				ownKeysToTrust.put(identityKey, false);
175			}
176		}
177		for(final IdentityKey identityKey : foreignKeysSet) {
178			if(!foreignKeysToTrust.containsKey(identityKey)) {
179				foreignKeysToTrust.put(identityKey, false);
180			}
181		}
182	}
183
184	@Override
185	public void onBackendConnected() {
186		if ((accountJid != null) && (contactJid != null)) {
187			final Account account = xmppConnectionService
188				.findAccountByJid(accountJid);
189			if (account == null) {
190				return;
191			}
192			this.contact = account.getRoster().getContact(contactJid);
193			ownKeysToTrust.clear();
194			foreignKeysToTrust.clear();
195			getFingerprints(account);
196
197			if(account.getAxolotlService().getNumTrustedKeys(contact) > 0) {
198				hasOtherTrustedKeys = true;
199			}
200			Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, false);
201			if(account.getAxolotlService().hasPendingKeyFetches(conversation)) {
202				hasPendingFetches = true;
203			}
204
205			populateView();
206		}
207	}
208
209	@Override
210	public void onKeyStatusUpdated() {
211		final Account account = xmppConnectionService.findAccountByJid(accountJid);
212		hasPendingFetches = false;
213		getFingerprints(account);
214		refreshUi();
215	}
216
217	private void commitTrusts() {
218		for(IdentityKey identityKey:ownKeysToTrust.keySet()) {
219			contact.getAccount().getAxolotlService().setFingerprintTrust(
220					identityKey.getFingerprint().replaceAll("\\s", ""),
221					XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(identityKey)));
222		}
223		for(IdentityKey identityKey:foreignKeysToTrust.keySet()) {
224			contact.getAccount().getAxolotlService().setFingerprintTrust(
225					identityKey.getFingerprint().replaceAll("\\s", ""),
226					XmppAxolotlSession.Trust.fromBoolean(foreignKeysToTrust.get(identityKey)));
227		}
228	}
229
230	private void unlock() {
231		mSaveButton.setEnabled(true);
232		mSaveButton.setTextColor(getPrimaryTextColor());
233	}
234
235	private void lock() {
236		mSaveButton.setEnabled(false);
237		mSaveButton.setTextColor(getSecondaryTextColor());
238	}
239
240	private void lockOrUnlockAsNeeded() {
241		if (!hasOtherTrustedKeys && !foreignKeysToTrust.values().contains(true)){
242			lock();
243		} else {
244			unlock();
245		}
246	}
247
248	private void setDone() {
249		mSaveButton.setText(getString(R.string.done));
250	}
251
252	private void setFetching() {
253		mSaveButton.setText(getString(R.string.fetching_keys));
254	}
255}