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