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