use material switch for device fingerprints

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java |   4 
src/main/java/eu/siacs/conversations/ui/OmemoActivity.java          | 412 
src/main/res/layout/item_device_fingerprint.xml                     |   4 
src/main/res/values/dimens.xml                                      |   2 
4 files changed, 229 insertions(+), 193 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java 🔗

@@ -570,7 +570,9 @@ public class ContactDetailsActivity extends OmemoActivity
         }
         if (Config.supportOpenPgp() && contact.getPgpKeyId() != 0) {
             hasKeys = true;
-            View view = inflater.inflate(R.layout.contact_key, binding.detailsContactKeys, false);
+            View view =
+                    inflater.inflate(
+                            R.layout.item_device_fingerprint, binding.detailsContactKeys, false);
             TextView key = view.findViewById(R.id.key);
             TextView keyType = view.findViewById(R.id.key_type);
             keyType.setText(R.string.openpgp_key_id);

src/main/java/eu/siacs/conversations/ui/OmemoActivity.java 🔗

@@ -7,205 +7,241 @@ import android.view.View;
 import android.widget.CompoundButton;
 import android.widget.LinearLayout;
 import android.widget.Toast;
-
-import androidx.appcompat.app.AlertDialog;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.android.material.color.MaterialColors;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
 import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
-import eu.siacs.conversations.databinding.ContactKeyBinding;
+import eu.siacs.conversations.databinding.ItemDeviceFingerprintBinding;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.XmppUri;
 
 public abstract class OmemoActivity extends XmppActivity {
 
-	private Account mSelectedAccount;
-	private String mSelectedFingerprint;
-
-	protected XmppUri mPendingFingerprintVerificationUri = null;
-
-	@Override
-	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
-		super.onCreateContextMenu(menu, v, menuInfo);
-		Object account = v.getTag(R.id.TAG_ACCOUNT);
-		Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
-		Object fingerprintStatus = v.getTag(R.id.TAG_FINGERPRINT_STATUS);
-		if (account instanceof Account
-				&& fingerprint instanceof String
-				&& fingerprintStatus instanceof FingerprintStatus) {
-			getMenuInflater().inflate(R.menu.omemo_key_context, menu);
-			MenuItem distrust = menu.findItem(R.id.distrust_key);
-			MenuItem verifyScan = menu.findItem(R.id.verify_scan);
-			if (this instanceof TrustKeysActivity) {
-				distrust.setVisible(false);
-				verifyScan.setVisible(false);
-			} else {
-				FingerprintStatus status = (FingerprintStatus) fingerprintStatus;
-				if (!status.isActive() || status.isVerified()) {
-					verifyScan.setVisible(false);
-				}
-				distrust.setVisible(status.isVerified() || (!status.isActive() && status.isTrusted()));
-			}
-			this.mSelectedAccount = (Account) account;
-			this.mSelectedFingerprint = (String) fingerprint;
-		}
-	}
-
-	@Override
-	public boolean onContextItemSelected(MenuItem item) {
-		switch (item.getItemId()) {
-			case R.id.distrust_key:
-				showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
-				break;
-			case R.id.copy_omemo_key:
-				copyOmemoFingerprint(mSelectedFingerprint);
-				break;
-			case R.id.verify_scan:
-				ScanActivity.scan(this);
-				break;
-		}
-		return true;
-	}
-
-	@Override
-	public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
-		super.onActivityResult(requestCode, resultCode, intent);
-		if (requestCode == ScanActivity.REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
-			String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
-			XmppUri uri = new XmppUri(result == null ? "" : result);
-			if (xmppConnectionServiceBound) {
-				processFingerprintVerification(uri);
-			} else {
-				this.mPendingFingerprintVerificationUri = uri;
-			}
-		}
-	}
-
-	protected abstract void processFingerprintVerification(XmppUri uri);
-
-	protected void copyOmemoFingerprint(String fingerprint) {
-		if (copyTextToClipboard(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)), R.string.omemo_fingerprint)) {
-			Toast.makeText(
-					this,
-					R.string.toast_message_omemo_fingerprint,
-					Toast.LENGTH_SHORT).show();
-		}
-	}
-
-	protected void addFingerprintRow(LinearLayout keys, final XmppAxolotlSession session, boolean highlight) {
-		final Account account = session.getAccount();
-		final String fingerprint = session.getFingerprint();
-		addFingerprintRowWithListeners(keys,
-				session.getAccount(),
-				fingerprint,
-				highlight,
-				session.getTrust(),
-				true,
-				true,
-				(buttonView, isChecked) -> account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(isChecked)));
-	}
-
-	protected void addFingerprintRowWithListeners(LinearLayout keys, final Account account,
-	                                              final String fingerprint,
-	                                              boolean highlight,
-	                                              FingerprintStatus status,
-	                                              boolean showTag,
-	                                              boolean undecidedNeedEnablement,
-	                                              CompoundButton.OnCheckedChangeListener
-			                                              onCheckedChangeListener) {
-		ContactKeyBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.contact_key, keys, true);
-		binding.tglTrust.setVisibility(View.VISIBLE);
-		registerForContextMenu(binding.getRoot());
-		binding.getRoot().setTag(R.id.TAG_ACCOUNT, account);
-		binding.getRoot().setTag(R.id.TAG_FINGERPRINT, fingerprint);
-		binding.getRoot().setTag(R.id.TAG_FINGERPRINT_STATUS, status);
-		boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
-		final View.OnClickListener toast;
-		binding.tglTrust.setChecked(status.isTrusted());
-
-		if (status.isActive()) {
-			binding.key.setTextColor(MaterialColors.getColor(binding.key, com.google.android.material.R.attr.colorOnSurface));
-			binding.keyType.setTextColor(MaterialColors.getColor(binding.keyType, com.google.android.material.R.attr.colorOnSurface));
-			if (status.isVerified()) {
-				binding.verifiedFingerprint.setVisibility(View.VISIBLE);
-				binding.verifiedFingerprint.setAlpha(1.0f);
-				binding.tglTrust.setVisibility(View.GONE);
-				binding.verifiedFingerprint.setOnClickListener(v -> replaceToast(getString(R.string.this_device_has_been_verified), false));
-				toast = null;
-			} else {
-				binding.verifiedFingerprint.setVisibility(View.GONE);
-				binding.tglTrust.setVisibility(View.VISIBLE);
-				binding.tglTrust.setOnCheckedChangeListener(onCheckedChangeListener);
-				if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED && undecidedNeedEnablement) {
-					binding.buttonEnableDevice.setVisibility(View.VISIBLE);
-					binding.buttonEnableDevice.setOnClickListener(v -> {
-						account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(false));
-						binding.buttonEnableDevice.setVisibility(View.GONE);
-						binding.tglTrust.setVisibility(View.VISIBLE);
-					});
-					binding.tglTrust.setVisibility(View.GONE);
-				} else {
-					binding.tglTrust.setOnClickListener(null);
-					binding.tglTrust.setEnabled(true);
-				}
-				toast = v -> hideToast();
-			}
-		} else {
-			binding.key.setTextColor(MaterialColors.getColor(binding.key, com.google.android.material.R.attr.colorOnSurfaceVariant));
-			binding.keyType.setTextColor(MaterialColors.getColor(binding.keyType, com.google.android.material.R.attr.colorOnSurfaceVariant));
-			toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
-			if (status.isVerified()) {
-				binding.tglTrust.setVisibility(View.GONE);
-				binding.verifiedFingerprint.setVisibility(View.VISIBLE);
-				binding.verifiedFingerprint.setAlpha(0.4368f);
-				binding.verifiedFingerprint.setOnClickListener(toast);
-			} else {
-				binding.tglTrust.setVisibility(View.VISIBLE);
-				binding.verifiedFingerprint.setVisibility(View.GONE);
-				binding.tglTrust.setEnabled(false);
-			}
-		}
-
-		binding.getRoot().setOnClickListener(toast);
-		binding.key.setOnClickListener(toast);
-		binding.keyType.setOnClickListener(toast);
-		if (showTag) {
-			binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
-		} else {
-			binding.keyType.setVisibility(View.GONE);
-		}
-		if (highlight) {
-			binding.keyType.setTextColor(MaterialColors.getColor(binding.keyType, com.google.android.material.R.attr.colorPrimaryVariant));
-			binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
-		} else {
-			binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
-		}
-
-		binding.key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
-	}
-
-	public void showPurgeKeyDialog(final Account account, final String fingerprint) {
-		final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
-		builder.setTitle(R.string.distrust_omemo_key);
-		builder.setMessage(R.string.distrust_omemo_key_text);
-		builder.setNegativeButton(getString(R.string.cancel), null);
-		builder.setPositiveButton(R.string.confirm,
-				(dialog, which) -> {
-					account.getAxolotlService().distrustFingerprint(fingerprint);
-					refreshUi();
-				});
-		builder.create().show();
-	}
-
-	@Override
-	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
-		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-		ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
-	}
+    private Account mSelectedAccount;
+    private String mSelectedFingerprint;
+
+    protected XmppUri mPendingFingerprintVerificationUri = null;
+
+    @Override
+    public void onCreateContextMenu(
+            ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+        Object account = v.getTag(R.id.TAG_ACCOUNT);
+        Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
+        Object fingerprintStatus = v.getTag(R.id.TAG_FINGERPRINT_STATUS);
+        if (account instanceof Account
+                && fingerprint instanceof String
+                && fingerprintStatus instanceof FingerprintStatus) {
+            getMenuInflater().inflate(R.menu.omemo_key_context, menu);
+            MenuItem distrust = menu.findItem(R.id.distrust_key);
+            MenuItem verifyScan = menu.findItem(R.id.verify_scan);
+            if (this instanceof TrustKeysActivity) {
+                distrust.setVisible(false);
+                verifyScan.setVisible(false);
+            } else {
+                FingerprintStatus status = (FingerprintStatus) fingerprintStatus;
+                if (!status.isActive() || status.isVerified()) {
+                    verifyScan.setVisible(false);
+                }
+                distrust.setVisible(
+                        status.isVerified() || (!status.isActive() && status.isTrusted()));
+            }
+            this.mSelectedAccount = (Account) account;
+            this.mSelectedFingerprint = (String) fingerprint;
+        }
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.distrust_key:
+                showPurgeKeyDialog(mSelectedAccount, mSelectedFingerprint);
+                break;
+            case R.id.copy_omemo_key:
+                copyOmemoFingerprint(mSelectedFingerprint);
+                break;
+            case R.id.verify_scan:
+                ScanActivity.scan(this);
+                break;
+        }
+        return true;
+    }
+
+    @Override
+    public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
+        super.onActivityResult(requestCode, resultCode, intent);
+        if (requestCode == ScanActivity.REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
+            String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
+            XmppUri uri = new XmppUri(result == null ? "" : result);
+            if (xmppConnectionServiceBound) {
+                processFingerprintVerification(uri);
+            } else {
+                this.mPendingFingerprintVerificationUri = uri;
+            }
+        }
+    }
+
+    protected abstract void processFingerprintVerification(XmppUri uri);
+
+    protected void copyOmemoFingerprint(String fingerprint) {
+        if (copyTextToClipboard(
+                CryptoHelper.prettifyFingerprint(fingerprint.substring(2)),
+                R.string.omemo_fingerprint)) {
+            Toast.makeText(this, R.string.toast_message_omemo_fingerprint, Toast.LENGTH_SHORT)
+                    .show();
+        }
+    }
+
+    protected void addFingerprintRow(
+            LinearLayout keys, final XmppAxolotlSession session, boolean highlight) {
+        final Account account = session.getAccount();
+        final String fingerprint = session.getFingerprint();
+        addFingerprintRowWithListeners(
+                keys,
+                session.getAccount(),
+                fingerprint,
+                highlight,
+                session.getTrust(),
+                true,
+                true,
+                (buttonView, isChecked) ->
+                        account.getAxolotlService()
+                                .setFingerprintTrust(
+                                        fingerprint, FingerprintStatus.createActive(isChecked)));
+    }
+
+    protected void addFingerprintRowWithListeners(
+            LinearLayout keys,
+            final Account account,
+            final String fingerprint,
+            boolean highlight,
+            FingerprintStatus status,
+            boolean showTag,
+            boolean undecidedNeedEnablement,
+            CompoundButton.OnCheckedChangeListener onCheckedChangeListener) {
+        ItemDeviceFingerprintBinding binding =
+                DataBindingUtil.inflate(
+                        getLayoutInflater(), R.layout.item_device_fingerprint, keys, true);
+        binding.tglTrust.setVisibility(View.VISIBLE);
+        registerForContextMenu(binding.getRoot());
+        binding.getRoot().setTag(R.id.TAG_ACCOUNT, account);
+        binding.getRoot().setTag(R.id.TAG_FINGERPRINT, fingerprint);
+        binding.getRoot().setTag(R.id.TAG_FINGERPRINT_STATUS, status);
+        boolean x509 =
+                Config.X509_VERIFICATION
+                        && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
+        final View.OnClickListener toast;
+        binding.tglTrust.setChecked(status.isTrusted());
+        binding.tglTrust.jumpDrawablesToCurrentState();
+
+        if (status.isActive()) {
+            binding.key.setTextColor(
+                    MaterialColors.getColor(
+                            binding.key, com.google.android.material.R.attr.colorOnSurface));
+            binding.keyType.setTextColor(
+                    MaterialColors.getColor(
+                            binding.keyType, com.google.android.material.R.attr.colorOnSurface));
+            if (status.isVerified()) {
+                binding.verifiedFingerprint.setVisibility(View.VISIBLE);
+                binding.verifiedFingerprint.setAlpha(1.0f);
+                binding.tglTrust.setVisibility(View.GONE);
+                binding.verifiedFingerprint.setOnClickListener(
+                        v ->
+                                replaceToast(
+                                        getString(R.string.this_device_has_been_verified), false));
+                toast = null;
+            } else {
+                binding.verifiedFingerprint.setVisibility(View.GONE);
+                binding.tglTrust.setVisibility(View.VISIBLE);
+                binding.tglTrust.setOnCheckedChangeListener(onCheckedChangeListener);
+                if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED
+                        && undecidedNeedEnablement) {
+                    binding.buttonEnableDevice.setVisibility(View.VISIBLE);
+                    binding.buttonEnableDevice.setOnClickListener(
+                            v -> {
+                                account.getAxolotlService()
+                                        .setFingerprintTrust(
+                                                fingerprint, FingerprintStatus.createActive(false));
+                                binding.buttonEnableDevice.setVisibility(View.GONE);
+                                binding.tglTrust.setVisibility(View.VISIBLE);
+                            });
+                    binding.tglTrust.setVisibility(View.GONE);
+                } else {
+                    binding.tglTrust.setOnClickListener(null);
+                    binding.tglTrust.setEnabled(true);
+                }
+                toast = v -> hideToast();
+            }
+        } else {
+            binding.key.setTextColor(
+                    MaterialColors.getColor(
+                            binding.key, com.google.android.material.R.attr.colorOnSurfaceVariant));
+            binding.keyType.setTextColor(
+                    MaterialColors.getColor(
+                            binding.keyType,
+                            com.google.android.material.R.attr.colorOnSurfaceVariant));
+            toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
+            if (status.isVerified()) {
+                binding.tglTrust.setVisibility(View.GONE);
+                binding.verifiedFingerprint.setVisibility(View.VISIBLE);
+                binding.verifiedFingerprint.setAlpha(0.4368f);
+                binding.verifiedFingerprint.setOnClickListener(toast);
+            } else {
+                binding.tglTrust.setVisibility(View.VISIBLE);
+                binding.verifiedFingerprint.setVisibility(View.GONE);
+                binding.tglTrust.setEnabled(false);
+            }
+        }
+
+        binding.getRoot().setOnClickListener(toast);
+        binding.key.setOnClickListener(toast);
+        binding.keyType.setOnClickListener(toast);
+        if (showTag) {
+            binding.keyType.setText(
+                    getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
+        } else {
+            binding.keyType.setVisibility(View.GONE);
+        }
+        if (highlight) {
+            binding.keyType.setTextColor(
+                    MaterialColors.getColor(
+                            binding.keyType,
+                            com.google.android.material.R.attr.colorPrimaryVariant));
+            binding.keyType.setText(
+                    getString(
+                            x509
+                                    ? R.string.omemo_fingerprint_x509_selected_message
+                                    : R.string.omemo_fingerprint_selected_message));
+        } else {
+            binding.keyType.setText(
+                    getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
+        }
+
+        binding.key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
+    }
+
+    public void showPurgeKeyDialog(final Account account, final String fingerprint) {
+        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
+        builder.setTitle(R.string.distrust_omemo_key);
+        builder.setMessage(R.string.distrust_omemo_key_text);
+        builder.setNegativeButton(getString(R.string.cancel), null);
+        builder.setPositiveButton(
+                R.string.confirm,
+                (dialog, which) -> {
+                    account.getAxolotlService().distrustFingerprint(fingerprint);
+                    refreshUi();
+                });
+        builder.create().show();
+    }
+
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, String[] permissions, int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
+    }
 }

src/main/res/layout/contact_key.xml → src/main/res/layout/item_device_fingerprint.xml 🔗

@@ -36,7 +36,7 @@
 
         <LinearLayout
             android:id="@+id/action_container"
-            android:layout_width="@dimen/key_action_width"
+            android:layout_width="56dp"
             android:layout_height="48dp"
             android:layout_alignParentEnd="true"
             android:layout_centerVertical="true"
@@ -69,7 +69,7 @@
                 android:visibility="gone"
                 app:tint="@color/light_green_600" />
 
-            <androidx.appcompat.widget.SwitchCompat
+            <com.google.android.material.materialswitch.MaterialSwitch
                 android:id="@+id/tgl_trust"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"

src/main/res/values/dimens.xml 🔗

@@ -8,8 +8,6 @@
     <dimen name="card_padding_list">8dp</dimen> <!-- card_padding_regular minus list_padding -->
     <dimen name="list_padding">8dp</dimen>
     <dimen name="image_button_padding">12dp</dimen>
-    <dimen name="key_action_width">48dp
-    </dimen>  <!-- icon width (24dp) + 2 * image button padding -->
     <dimen name="fineprint_size">11sp</dimen>
     <dimen name="audio_player_width">224dp</dimen>
     <dimen name="image_preview_width">224dp</dimen>