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