OmemoActivity.java

  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}