1package eu.siacs.conversations.ui;
2
3import android.databinding.DataBindingUtil;
4import android.net.Uri;
5import android.support.v7.app.AlertDialog;
6import android.content.Intent;
7import android.os.Bundle;
8import android.support.v4.content.ContextCompat;
9import android.view.ContextMenu;
10import android.view.MenuItem;
11import android.view.View;
12import android.widget.CompoundButton;
13
14import android.widget.LinearLayout;
15import android.widget.TextView;
16import android.widget.Toast;
17
18import java.security.cert.X509Certificate;
19import java.util.Arrays;
20
21import eu.siacs.conversations.Config;
22import eu.siacs.conversations.R;
23import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
24import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
25import eu.siacs.conversations.databinding.ContactKeyBinding;
26import eu.siacs.conversations.entities.Account;
27import eu.siacs.conversations.utils.CryptoHelper;
28import eu.siacs.conversations.utils.XmppUri;
29
30public abstract class OmemoActivity extends XmppActivity {
31
32 private Account mSelectedAccount;
33 private String mSelectedFingerprint;
34
35 protected XmppUri mPendingFingerprintVerificationUri = null;
36
37 @Override
38 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
39 super.onCreateContextMenu(menu, v, menuInfo);
40 Object account = v.getTag(R.id.TAG_ACCOUNT);
41 Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
42 Object fingerprintStatus = v.getTag(R.id.TAG_FINGERPRINT_STATUS);
43 if (account != null
44 && fingerprint != null
45 && account instanceof Account
46 && fingerprintStatus != null
47 && fingerprint instanceof String
48 && fingerprintStatus instanceof FingerprintStatus) {
49 getMenuInflater().inflate(R.menu.omemo_key_context, menu);
50 MenuItem distrust = menu.findItem(R.id.distrust_key);
51 MenuItem verifyScan = menu.findItem(R.id.verify_scan);
52 if (this instanceof TrustKeysActivity) {
53 distrust.setVisible(false);
54 verifyScan.setVisible(false);
55 } else {
56 FingerprintStatus status = (FingerprintStatus) fingerprintStatus;
57 if (!status.isActive() || status.isVerified()) {
58 verifyScan.setVisible(false);
59 }
60 distrust.setVisible(status.isVerified() || (!status.isActive() && status.isTrusted()));
61 }
62 this.mSelectedAccount = (Account) account;
63 this.mSelectedFingerprint = (String) fingerprint;
64 }
65 }
66
67 @Override
68 public boolean onContextItemSelected(MenuItem item) {
69 switch (item.getItemId()) {
70 case R.id.distrust_key:
71 showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
72 break;
73 case R.id.copy_omemo_key:
74 copyOmemoFingerprint(mSelectedFingerprint);
75 break;
76 case R.id.verify_scan:
77 ScanActivity.scan(this);
78 break;
79 }
80 return true;
81 }
82
83 @Override
84 public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
85 super.onActivityResult(requestCode, resultCode, intent);
86 if (requestCode == ScanActivity.REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
87 String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
88 XmppUri uri = new XmppUri(result == null ? "" : result);
89 if (xmppConnectionServiceBound) {
90 processFingerprintVerification(uri);
91 } else {
92 this.mPendingFingerprintVerificationUri = uri;
93 }
94 }
95 }
96
97 protected abstract void processFingerprintVerification(XmppUri uri);
98
99 protected void copyOmemoFingerprint(String fingerprint) {
100 if (copyTextToClipboard(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)), R.string.omemo_fingerprint)) {
101 Toast.makeText(
102 this,
103 R.string.toast_message_omemo_fingerprint,
104 Toast.LENGTH_SHORT).show();
105 }
106 }
107
108 protected void addFingerprintRow(LinearLayout keys, final XmppAxolotlSession session, boolean highlight) {
109 final Account account = session.getAccount();
110 final String fingerprint = session.getFingerprint();
111 addFingerprintRowWithListeners(keys,
112 session.getAccount(),
113 fingerprint,
114 highlight,
115 session.getTrust(),
116 true,
117 true,
118 (buttonView, isChecked) -> account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(isChecked)));
119 }
120
121 protected void addFingerprintRowWithListeners(LinearLayout keys, final Account account,
122 final String fingerprint,
123 boolean highlight,
124 FingerprintStatus status,
125 boolean showTag,
126 boolean undecidedNeedEnablement,
127 CompoundButton.OnCheckedChangeListener
128 onCheckedChangeListener) {
129 ContactKeyBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.contact_key, keys, true);
130 binding.tglTrust.setVisibility(View.VISIBLE);
131 registerForContextMenu(binding.getRoot());
132 binding.getRoot().setTag(R.id.TAG_ACCOUNT, account);
133 binding.getRoot().setTag(R.id.TAG_FINGERPRINT, fingerprint);
134 binding.getRoot().setTag(R.id.TAG_FINGERPRINT_STATUS, status);
135 boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
136 final View.OnClickListener toast;
137 binding.tglTrust.setChecked(status.isTrusted());
138
139 if (status.isActive()) {
140 binding.key.setTextAppearance(this,R.style.TextAppearance_Conversations_Fingerprint);
141 binding.keyType.setTextAppearance(this,R.style.TextAppearance_Conversations_Caption);
142 if (status.isVerified()) {
143 binding.verifiedFingerprint.setVisibility(View.VISIBLE);
144 binding.verifiedFingerprint.setAlpha(1.0f);
145 binding.tglTrust.setVisibility(View.GONE);
146 binding.verifiedFingerprint.setOnClickListener(v -> replaceToast(getString(R.string.this_device_has_been_verified), false));
147 toast = null;
148 } else {
149 binding.verifiedFingerprint.setVisibility(View.GONE);
150 binding.tglTrust.setVisibility(View.VISIBLE);
151 binding.tglTrust.setOnCheckedChangeListener(onCheckedChangeListener);
152 if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED && undecidedNeedEnablement) {
153 binding.buttonEnableDevice.setVisibility(View.VISIBLE);
154 binding.buttonEnableDevice.setOnClickListener(v -> {
155 account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(false));
156 binding.buttonEnableDevice.setVisibility(View.GONE);
157 binding.tglTrust.setVisibility(View.VISIBLE);
158 });
159 binding.tglTrust.setVisibility(View.GONE);
160 } else {
161 binding.tglTrust.setOnClickListener(null);
162 binding.tglTrust.setEnabled(true);
163 }
164 toast = v -> hideToast();
165 }
166 } else {
167 binding.key.setTextAppearance(this,R.style.TextAppearance_Conversations_Fingerprint_Disabled);
168 binding.keyType.setTextAppearance(this,R.style.TextAppearance_Conversations_Caption_Disabled);
169 toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
170 if (status.isVerified()) {
171 binding.tglTrust.setVisibility(View.GONE);
172 binding.verifiedFingerprint.setVisibility(View.VISIBLE);
173 binding.verifiedFingerprint.setAlpha(0.4368f);
174 binding.verifiedFingerprint.setOnClickListener(toast);
175 } else {
176 binding.tglTrust.setVisibility(View.VISIBLE);
177 binding.verifiedFingerprint.setVisibility(View.GONE);
178 binding.tglTrust.setEnabled(false);
179 }
180 }
181
182 binding.getRoot().setOnClickListener(toast);
183 binding.key.setOnClickListener(toast);
184 binding.keyType.setOnClickListener(toast);
185 if (showTag) {
186 binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
187 } else {
188 binding.keyType.setVisibility(View.GONE);
189 }
190 if (highlight) {
191 binding.keyType.setTextAppearance(this,R.style.TextAppearance_Conversations_Caption_Highlight);
192 binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
193 } else {
194 binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
195 }
196
197 binding.key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
198 }
199
200 public void showPurgeKeyDialog(final Account account, final String fingerprint) {
201 AlertDialog.Builder builder = new AlertDialog.Builder(this);
202 builder.setTitle(R.string.distrust_omemo_key);
203 builder.setMessage(R.string.distrust_omemo_key_text);
204 builder.setNegativeButton(getString(R.string.cancel), null);
205 builder.setPositiveButton(R.string.confirm,
206 (dialog, which) -> {
207 account.getAxolotlService().distrustFingerprint(fingerprint);
208 refreshUi();
209 });
210 builder.create().show();
211 }
212
213 @Override
214 public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
215 ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
216 }
217}