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 protected 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 // TODO can we rework this into using Intents?
54 this.mSelectedAccount = (Account) account;
55 this.mSelectedFingerprint = (String) fingerprint;
56 }
57 }
58
59 @Override
60 public boolean onContextItemSelected(final MenuItem item) {
61 final var itemId = item.getItemId();
62 if (itemId == R.id.distrust_key) {
63 showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
64 return true;
65 } else if (itemId == R.id.copy_omemo_key) {
66 copyOmemoFingerprint(mSelectedFingerprint);
67 return true;
68 } else if (itemId == R.id.verify_scan) {
69 ScanActivity.scan(this);
70 return true;
71 } else {
72 return super.onContextItemSelected(item);
73 }
74 }
75
76 @Override
77 public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
78 super.onActivityResult(requestCode, resultCode, intent);
79 if (requestCode == ScanActivity.REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
80 String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
81 XmppUri uri = new XmppUri(result == null ? "" : result);
82 if (xmppConnectionServiceBound) {
83 processFingerprintVerification(uri);
84 } else {
85 this.mPendingFingerprintVerificationUri = uri;
86 }
87 }
88 }
89
90 protected abstract void processFingerprintVerification(XmppUri uri);
91
92 protected void copyOmemoFingerprint(String fingerprint) {
93 if (copyTextToClipboard(
94 CryptoHelper.prettifyFingerprint(fingerprint.substring(2)),
95 R.string.omemo_fingerprint)) {
96 Toast.makeText(this, R.string.toast_message_omemo_fingerprint, Toast.LENGTH_SHORT)
97 .show();
98 }
99 }
100
101 protected void addFingerprintRow(
102 LinearLayout keys, final XmppAxolotlSession session, boolean highlight) {
103 final Account account = session.getAccount();
104 final String fingerprint = session.getFingerprint();
105 addFingerprintRowWithListeners(
106 keys,
107 session.getAccount(),
108 fingerprint,
109 highlight,
110 session.getTrust(),
111 true,
112 true,
113 (buttonView, isChecked) ->
114 account.getAxolotlService()
115 .setFingerprintTrust(
116 fingerprint, FingerprintStatus.createActive(isChecked)));
117 }
118
119 protected void addFingerprintRowWithListeners(
120 LinearLayout keys,
121 final Account account,
122 final String fingerprint,
123 boolean highlight,
124 FingerprintStatus status,
125 boolean showTag,
126 boolean undecidedNeedEnablement,
127 CompoundButton.OnCheckedChangeListener onCheckedChangeListener) {
128 ItemDeviceFingerprintBinding binding =
129 DataBindingUtil.inflate(
130 getLayoutInflater(), R.layout.item_device_fingerprint, keys, true);
131 binding.tglTrust.setVisibility(View.VISIBLE);
132 registerForContextMenu(binding.getRoot());
133 binding.getRoot().setTag(R.id.TAG_ACCOUNT, account);
134 binding.getRoot().setTag(R.id.TAG_FINGERPRINT, fingerprint);
135 binding.getRoot().setTag(R.id.TAG_FINGERPRINT_STATUS, status);
136 boolean x509 =
137 Config.X509_VERIFICATION
138 && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
139 final View.OnClickListener toast;
140 binding.tglTrust.setChecked(status.isTrusted());
141 binding.tglTrust.jumpDrawablesToCurrentState();
142
143 if (status.isActive()) {
144 binding.key.setTextColor(
145 MaterialColors.getColor(
146 binding.key, com.google.android.material.R.attr.colorOnSurface));
147 binding.keyType.setTextColor(
148 MaterialColors.getColor(
149 binding.keyType, com.google.android.material.R.attr.colorOnSurface));
150 if (status.isVerified()) {
151 binding.verifiedFingerprint.setVisibility(View.VISIBLE);
152 binding.verifiedFingerprint.setAlpha(1.0f);
153 binding.tglTrust.setVisibility(View.GONE);
154 binding.verifiedFingerprint.setOnClickListener(
155 v ->
156 replaceToast(
157 getString(R.string.this_device_has_been_verified), false));
158 toast = null;
159 } else {
160 binding.verifiedFingerprint.setVisibility(View.GONE);
161 binding.tglTrust.setVisibility(View.VISIBLE);
162 binding.tglTrust.setOnCheckedChangeListener(onCheckedChangeListener);
163 if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED
164 && undecidedNeedEnablement) {
165 binding.buttonEnableDevice.setVisibility(View.VISIBLE);
166 binding.buttonEnableDevice.setOnClickListener(
167 v -> {
168 account.getAxolotlService()
169 .setFingerprintTrust(
170 fingerprint, FingerprintStatus.createActive(false));
171 binding.buttonEnableDevice.setVisibility(View.GONE);
172 binding.tglTrust.setVisibility(View.VISIBLE);
173 });
174 binding.tglTrust.setVisibility(View.GONE);
175 } else {
176 binding.tglTrust.setOnClickListener(null);
177 binding.tglTrust.setEnabled(true);
178 }
179 toast = v -> hideToast();
180 }
181 } else {
182 binding.key.setTextColor(
183 MaterialColors.getColor(
184 binding.key, com.google.android.material.R.attr.colorOnSurfaceVariant));
185 binding.keyType.setTextColor(
186 MaterialColors.getColor(
187 binding.keyType,
188 com.google.android.material.R.attr.colorOnSurfaceVariant));
189 toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
190 if (status.isVerified()) {
191 binding.tglTrust.setVisibility(View.GONE);
192 binding.verifiedFingerprint.setVisibility(View.VISIBLE);
193 binding.verifiedFingerprint.setAlpha(0.4368f);
194 binding.verifiedFingerprint.setOnClickListener(toast);
195 } else {
196 binding.tglTrust.setVisibility(View.VISIBLE);
197 binding.verifiedFingerprint.setVisibility(View.GONE);
198 binding.tglTrust.setEnabled(false);
199 }
200 }
201
202 binding.getRoot().setOnClickListener(toast);
203 binding.key.setOnClickListener(toast);
204 binding.keyType.setOnClickListener(toast);
205 if (showTag) {
206 binding.keyType.setText(
207 getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
208 } else {
209 binding.keyType.setVisibility(View.GONE);
210 }
211 if (highlight) {
212 binding.keyType.setTextColor(
213 MaterialColors.getColor(
214 binding.keyType,
215 com.google.android.material.R.attr.colorPrimaryVariant));
216 binding.keyType.setText(
217 getString(
218 x509
219 ? R.string.omemo_fingerprint_x509_selected_message
220 : R.string.omemo_fingerprint_selected_message));
221 } else {
222 binding.keyType.setText(
223 getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
224 }
225
226 binding.key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
227 }
228
229 public void showPurgeKeyDialog(final Account account, final String fingerprint) {
230 final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
231 builder.setTitle(R.string.distrust_omemo_key);
232 builder.setMessage(R.string.distrust_omemo_key_text);
233 builder.setNegativeButton(getString(R.string.cancel), null);
234 builder.setPositiveButton(
235 R.string.confirm,
236 (dialog, which) -> {
237 account.getAxolotlService().distrustFingerprint(fingerprint);
238 refreshUi();
239 });
240 builder.create().show();
241 }
242
243 @Override
244 public void onRequestPermissionsResult(
245 int requestCode, String[] permissions, int[] grantResults) {
246 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
247 ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
248 }
249}