1package eu.siacs.conversations.ui;
2
3import android.content.Intent;
4import android.os.Bundle;
5import android.util.Log;
6import android.view.View;
7import android.view.View.OnClickListener;
8import android.widget.Button;
9import android.widget.CompoundButton;
10import android.widget.LinearLayout;
11import android.widget.TextView;
12
13import org.whispersystems.libaxolotl.IdentityKey;
14
15
16import java.util.HashMap;
17import java.util.Map;
18import java.util.Set;
19
20import eu.siacs.conversations.Config;
21import eu.siacs.conversations.R;
22import eu.siacs.conversations.crypto.axolotl.AxolotlService;
23import eu.siacs.conversations.crypto.axolotl.AxolotlService.SQLiteAxolotlStore.Trust;
24import eu.siacs.conversations.entities.Account;
25import eu.siacs.conversations.entities.Contact;
26import eu.siacs.conversations.entities.Conversation;
27import eu.siacs.conversations.xmpp.OnNewKeysAvailable;
28import eu.siacs.conversations.xmpp.jid.InvalidJidException;
29import eu.siacs.conversations.xmpp.jid.Jid;
30
31public class TrustKeysActivity extends XmppActivity implements OnNewKeysAvailable {
32 private Jid accountJid;
33 private Jid contactJid;
34
35 private Contact contact;
36 private TextView ownKeysTitle;
37 private LinearLayout ownKeys;
38 private LinearLayout ownKeysCard;
39 private TextView foreignKeysTitle;
40 private LinearLayout foreignKeys;
41 private LinearLayout foreignKeysCard;
42 private Button mSaveButton;
43 private Button mCancelButton;
44
45 private final Map<IdentityKey, Boolean> ownKeysToTrust = new HashMap<>();
46 private final Map<IdentityKey, Boolean> foreignKeysToTrust = new HashMap<>();
47
48 private final OnClickListener mSaveButtonListener = new OnClickListener() {
49 @Override
50 public void onClick(View v) {
51 commitTrusts();
52 Intent data = new Intent();
53 data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
54 setResult(RESULT_OK, data);
55 finish();
56 }
57 };
58
59 private final OnClickListener mCancelButtonListener = new OnClickListener() {
60 @Override
61 public void onClick(View v) {
62 setResult(RESULT_CANCELED);
63 finish();
64 }
65 };
66
67 @Override
68 protected void refreshUiReal() {
69 invalidateOptionsMenu();
70 populateView();
71 }
72
73 @Override
74 protected String getShareableUri() {
75 if (contact != null) {
76 return contact.getShareableUri();
77 } else {
78 return "";
79 }
80 }
81
82 @Override
83 protected void onCreate(final Bundle savedInstanceState) {
84 super.onCreate(savedInstanceState);
85 setContentView(R.layout.activity_trust_keys);
86 try {
87 this.accountJid = Jid.fromString(getIntent().getExtras().getString("account"));
88 } catch (final InvalidJidException ignored) {
89 }
90 try {
91 this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
92 } catch (final InvalidJidException ignored) {
93 }
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,
122 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 refreshUi();
128 xmppConnectionService.updateAccountUi();
129 xmppConnectionService.updateConversationUi();
130 }
131 },
132 null
133 );
134 }
135 for(final IdentityKey identityKey : foreignKeysToTrust.keySet()) {
136 hasForeignKeys = true;
137 addFingerprintRowWithListeners(foreignKeys, contact.getAccount(), identityKey,
138 Trust.fromBoolean(foreignKeysToTrust.get(identityKey)), false,
139 new CompoundButton.OnCheckedChangeListener() {
140 @Override
141 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
142 foreignKeysToTrust.put(identityKey, isChecked);
143 refreshUi();
144 xmppConnectionService.updateAccountUi();
145 xmppConnectionService.updateConversationUi();
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 }
161
162 private void getFingerprints(final Account account) {
163 Set<IdentityKey> ownKeysSet = account.getAxolotlService().getPendingKeys();
164 for(final IdentityKey identityKey : ownKeysSet) {
165 if(!ownKeysToTrust.containsKey(identityKey)) {
166 ownKeysToTrust.put(identityKey, false);
167 }
168 }
169 Set<IdentityKey> foreignKeysSet = account.getAxolotlService().getPendingKeys(contact);
170 for(final IdentityKey identityKey : foreignKeysSet) {
171 if(!foreignKeysToTrust.containsKey(identityKey)) {
172 foreignKeysToTrust.put(identityKey, false);
173 }
174 }
175 }
176
177 @Override
178 public void onBackendConnected() {
179 if ((accountJid != null) && (contactJid != null)) {
180 final Account account = xmppConnectionService
181 .findAccountByJid(accountJid);
182 if (account == null) {
183 return;
184 }
185 this.contact = account.getRoster().getContact(contactJid);
186 ownKeysToTrust.clear();
187 foreignKeysToTrust.clear();
188 getFingerprints(account);
189
190 Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, false);
191 if(account.getAxolotlService().hasPendingKeyFetches(conversation)) {
192 lock();
193 }
194
195 populateView();
196 }
197 }
198
199 @Override
200 public void onNewKeysAvailable() {
201 runOnUiThread(new Runnable() {
202 @Override
203 public void run() {
204 final Account account = xmppConnectionService
205 .findAccountByJid(accountJid);
206 unlock();
207 getFingerprints(account);
208 refreshUi();
209 }
210 });
211 }
212
213 private void commitTrusts() {
214 for(IdentityKey identityKey:ownKeysToTrust.keySet()) {
215 contact.getAccount().getAxolotlService().setFingerprintTrust(
216 identityKey.getFingerprint().replaceAll("\\s", ""),
217 Trust.fromBoolean(ownKeysToTrust.get(identityKey)));
218 }
219 for(IdentityKey identityKey:foreignKeysToTrust.keySet()) {
220 contact.getAccount().getAxolotlService().setFingerprintTrust(
221 identityKey.getFingerprint().replaceAll("\\s", ""),
222 Trust.fromBoolean(foreignKeysToTrust.get(identityKey)));
223 }
224 }
225
226 private void unlock() {
227 mSaveButton.setEnabled(true);
228 mSaveButton.setText(getString(R.string.done));
229 mSaveButton.setTextColor(getPrimaryTextColor());
230 }
231
232 private void lock() {
233 mSaveButton.setEnabled(false);
234 mSaveButton.setText(getString(R.string.fetching_keys));
235 mSaveButton.setTextColor(getSecondaryTextColor());
236 }
237}