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