OmemoActivity.java

  1package eu.siacs.conversations;
  2
  3import android.app.AlertDialog;
  4import android.content.DialogInterface;
  5import android.os.Bundle;
  6import android.view.ContextMenu;
  7import android.view.MenuItem;
  8import android.view.View;
  9import android.widget.CompoundButton;
 10import android.widget.ImageView;
 11import android.widget.LinearLayout;
 12import android.widget.TextView;
 13import android.widget.Toast;
 14
 15import java.security.cert.X509Certificate;
 16
 17import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
 18import eu.siacs.conversations.entities.Account;
 19import eu.siacs.conversations.ui.XmppActivity;
 20import eu.siacs.conversations.ui.widget.Switch;
 21import eu.siacs.conversations.utils.CryptoHelper;
 22
 23
 24public abstract class OmemoActivity extends XmppActivity {
 25
 26    private Account mSelectedAccount;
 27    private String mSelectedFingerprint;
 28
 29    @Override
 30    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
 31        super.onCreateContextMenu(menu,v,menuInfo);
 32        Object account = v.getTag(R.id.TAG_ACCOUNT);
 33        Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
 34        if (account != null && fingerprint != null && account instanceof Account && fingerprint instanceof String) {
 35            getMenuInflater().inflate(R.menu.omemo_key_context, menu);
 36            this.mSelectedAccount = (Account) account;
 37            this.mSelectedFingerprint = (String) fingerprint;
 38        }
 39    }
 40
 41    @Override
 42    public boolean onContextItemSelected(MenuItem item) {
 43        switch (item.getItemId()) {
 44            case R.id.purge_omemo_key:
 45                showPurgeKeyDialog(mSelectedAccount,mSelectedFingerprint);
 46                break;
 47            case R.id.copy_omemo_key:
 48                copyOmemoFingerprint(mSelectedFingerprint);
 49                break;
 50        }
 51        return true;
 52    }
 53
 54    protected void copyOmemoFingerprint(String fingerprint) {
 55        if (copyTextToClipboard(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)), R.string.omemo_fingerprint)) {
 56            Toast.makeText(
 57                    this,
 58                    R.string.toast_message_omemo_fingerprint,
 59                    Toast.LENGTH_SHORT).show();
 60        }
 61    }
 62
 63    protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight) {
 64        final FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
 65        return status != null && addFingerprintRowWithListeners(keys, account, fingerprint, highlight, status, true, true, new CompoundButton.OnCheckedChangeListener() {
 66            @Override
 67            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
 68                account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(isChecked));
 69            }
 70        });
 71    }
 72
 73    protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account,
 74                                                     final String fingerprint,
 75                                                     boolean highlight,
 76                                                     FingerprintStatus status,
 77                                                     boolean showTag,
 78                                                     boolean undecidedNeedEnablement,
 79                                                     CompoundButton.OnCheckedChangeListener
 80                                                             onCheckedChangeListener) {
 81        if (status.isCompromised()) {
 82            return false;
 83        }
 84        View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false);
 85        TextView key = (TextView) view.findViewById(R.id.key);
 86        TextView keyType = (TextView) view.findViewById(R.id.key_type);
 87        if (Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
 88            View.OnClickListener listener = new View.OnClickListener() {
 89                @Override
 90                public void onClick(View v) {
 91                    showX509Certificate(account,fingerprint);
 92                }
 93            };
 94            key.setOnClickListener(listener);
 95            keyType.setOnClickListener(listener);
 96        }
 97        Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust);
 98        ImageView verifiedFingerprintSymbol = (ImageView) view.findViewById(R.id.verified_fingerprint);
 99        trustToggle.setVisibility(View.VISIBLE);
100        registerForContextMenu(view);
101        view.setTag(R.id.TAG_ACCOUNT,account);
102        view.setTag(R.id.TAG_FINGERPRINT,fingerprint);
103        boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
104        final View.OnClickListener toast;
105        trustToggle.setChecked(status.isTrusted(), false);
106
107        if (status.isActive()){
108            key.setTextColor(getPrimaryTextColor());
109            keyType.setTextColor(getSecondaryTextColor());
110            if (status.isVerified()) {
111                verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
112                trustToggle.setVisibility(View.GONE);
113                verifiedFingerprintSymbol.setOnClickListener(new View.OnClickListener() {
114                    @Override
115                    public void onClick(View v) {
116                        replaceToast(getString(R.string.this_device_has_been_verified), false);
117                    }
118                });
119                toast = null;
120            } else {
121                verifiedFingerprintSymbol.setVisibility(View.GONE);
122                trustToggle.setVisibility(View.VISIBLE);
123                trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
124                if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED && undecidedNeedEnablement) {
125                    trustToggle.setOnClickListener(new View.OnClickListener() {
126                        @Override
127                        public void onClick(View v) {
128                            account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
129                            v.setEnabled(true);
130                            v.setOnClickListener(null);
131                        }
132                    });
133                    trustToggle.setEnabled(false);
134                } else {
135                    trustToggle.setOnClickListener(null);
136                    trustToggle.setEnabled(true);
137                }
138                toast = new View.OnClickListener() {
139                    @Override
140                    public void onClick(View v) {
141                        hideToast();
142                    }
143                };
144            }
145        } else {
146            key.setTextColor(getTertiaryTextColor());
147            keyType.setTextColor(getTertiaryTextColor());
148            toast = new View.OnClickListener() {
149                @Override
150                public void onClick(View v) {
151                    replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
152                }
153            };
154            if (status.isVerified()) {
155                trustToggle.setVisibility(View.GONE);
156                verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
157                verifiedFingerprintSymbol.setOnClickListener(toast);
158            } else {
159                trustToggle.setVisibility(View.VISIBLE);
160                verifiedFingerprintSymbol.setVisibility(View.GONE);
161                trustToggle.setOnClickListener(null);
162                trustToggle.setEnabled(false);
163                trustToggle.setOnClickListener(toast);
164            }
165        }
166
167        view.setOnClickListener(toast);
168        key.setOnClickListener(toast);
169        keyType.setOnClickListener(toast);
170        if (showTag) {
171            keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
172        } else {
173            keyType.setVisibility(View.GONE);
174        }
175        if (highlight) {
176            keyType.setTextColor(getResources().getColor(R.color.accent));
177            keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
178        } else {
179            keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
180        }
181
182        key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
183
184        keys.addView(view);
185        return true;
186    }
187
188    public void showPurgeKeyDialog(final Account account, final String fingerprint) {
189        AlertDialog.Builder builder = new AlertDialog.Builder(this);
190        builder.setTitle(getString(R.string.purge_key));
191        builder.setIconAttribute(android.R.attr.alertDialogIcon);
192        builder.setMessage(getString(R.string.purge_key_desc_part1)
193                + "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2))
194                + "\n\n" + getString(R.string.purge_key_desc_part2));
195        builder.setNegativeButton(getString(R.string.cancel), null);
196        builder.setPositiveButton(getString(R.string.purge_key),
197                new DialogInterface.OnClickListener() {
198                    @Override
199                    public void onClick(DialogInterface dialog, int which) {
200                        account.getAxolotlService().purgeKey(fingerprint);
201                        refreshUi();
202                    }
203                });
204        builder.create().show();
205    }
206
207    private void showX509Certificate(Account account, String fingerprint) {
208        X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint);
209        if (x509Certificate != null) {
210            showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
211        } else {
212            Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
213        }
214    }
215
216    private void showCertificateInformationDialog(Bundle bundle) {
217        View view = getLayoutInflater().inflate(R.layout.certificate_information, null);
218        final String not_available = getString(R.string.certicate_info_not_available);
219        TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn);
220        TextView subject_o = (TextView) view.findViewById(R.id.subject_o);
221        TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn);
222        TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o);
223        TextView sha1 = (TextView) view.findViewById(R.id.sha1);
224
225        subject_cn.setText(bundle.getString("subject_cn", not_available));
226        subject_o.setText(bundle.getString("subject_o", not_available));
227        issuer_cn.setText(bundle.getString("issuer_cn", not_available));
228        issuer_o.setText(bundle.getString("issuer_o", not_available));
229        sha1.setText(bundle.getString("sha1", not_available));
230
231        AlertDialog.Builder builder = new AlertDialog.Builder(this);
232        builder.setTitle(R.string.certificate_information);
233        builder.setView(view);
234        builder.setPositiveButton(R.string.ok, null);
235        builder.create().show();
236    }
237}