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