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}