Double check

pull/200/head
M66B 3 years ago
parent 3d07d3e3a8
commit c35643865f

@ -315,8 +315,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private View vwColor;
private ImageButton ibExpander;
private ImageView ibFlagged;
private ImageView ivVerified;
private ImageButton ibAvatar;
private ImageButton ibVerified;
private ImageButton ibAuth;
private ImageButton ibPriority;
private ImageView ivImportance;
@ -489,8 +489,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
vwColor = itemView.findViewById(R.id.vwColor);
ibExpander = itemView.findViewById(R.id.ibExpander);
ibFlagged = itemView.findViewById(R.id.ibFlagged);
ivVerified = itemView.findViewById(R.id.ivVerified);
ibAvatar = itemView.findViewById(R.id.ibAvatar);
ibVerified = itemView.findViewById(R.id.ibVerified);
ibAuth = itemView.findViewById(R.id.ibAuth);
ibPriority = itemView.findViewById(R.id.ibPriority);
ivImportance = itemView.findViewById(R.id.ivImportance);
@ -727,6 +727,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
});
ibAvatar.setOnClickListener(this);
ibVerified.setOnClickListener(this);
ibAuth.setOnClickListener(this);
ibPriority.setOnClickListener(this);
ibSnoozed.setOnClickListener(this);
@ -852,6 +853,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
touch.setOnClickListener(null);
ibAvatar.setOnClickListener(null);
ibVerified.setOnClickListener(null);
ibAuth.setOnClickListener(null);
ibPriority.setOnClickListener(null);
ibSnoozed.setOnClickListener(null);
@ -976,8 +978,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (viewType == ViewType.THREAD) {
boolean dim = (message.duplicate || EntityFolder.TRASH.equals(message.folderType));
ibFlagged.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivVerified.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibAvatar.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibVerified.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibAuth.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibPriority.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivImportance.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
@ -1027,10 +1029,10 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibExpander.setVisibility(View.GONE);
// Photo
ivVerified.setVisibility(View.GONE);
ibAvatar.setVisibility(avatars ? View.INVISIBLE : View.GONE);
// Line 1
ibVerified.setVisibility(View.GONE);
ibAuth.setVisibility(authentication && !authenticated ? View.VISIBLE : View.GONE);
if (EntityMessage.PRIORITIY_HIGH.equals(message.ui_priority)) {
@ -1533,7 +1535,14 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
Boolean.TRUE.equals(message.spf) &&
Boolean.TRUE.equals(message.dkim) &&
Boolean.TRUE.equals(message.dmarc));
ivVerified.setVisibility(verified ? View.VISIBLE : View.GONE);
ibVerified.setImageLevel(verified ? 1 : 0);
ibVerified.setImageTintList(ColorStateList.valueOf(verified
? colorAccent : colorSeparator));
ibVerified.setContentDescription(context.getString(verified
? R.string.title_advanced_bimi_verified : R.string.title_advanced_bimi_unverified));
ibVerified.setVisibility(
main != null && "vmc".endsWith(main.getType())
? View.VISIBLE : View.GONE);
}
if (distinguish_contacts) {
@ -3116,6 +3125,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (view.getId() == R.id.ibAvatar)
onViewContact(message);
else if (view.getId() == R.id.ibVerified)
onShowVerified(message);
else if (view.getId() == R.id.ibAuth)
onShowAuth(message);
else if (view.getId() == R.id.ibPriority)
@ -3440,6 +3451,10 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
}
}
private void onShowVerified(TupleMessageEx message) {
ToastEx.makeText(context, ibVerified.getContentDescription(), Toast.LENGTH_LONG).show();
}
private void onShowAuth(TupleMessageEx message) {
StringBuilder sb = new StringBuilder();

@ -20,14 +20,11 @@ package eu.faircode.email;
*/
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.Pair;
import androidx.preference.PreferenceManager;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERIA5String;
@ -77,11 +74,8 @@ public class Bimi {
static Pair<Bitmap, Boolean> get(
Context context, String domain, String selector, int scaleToPixels)
throws IOException {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean bimi_vmc = prefs.getBoolean("bimi_vmc", false);
Bitmap bitmap = null;
boolean verified = !bimi_vmc;
boolean verified = false;
// Get DNS record
String txt = selector + "._bimi." + domain;
@ -146,7 +140,7 @@ public class Bimi {
// Certificate link
case "a": {
if (!bimi_vmc)
if (verified)
continue;
String a = values.get(tag);

@ -49,6 +49,7 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -89,6 +90,7 @@ import javax.net.ssl.SSLSession;
public class ContactInfo {
private String email;
private Bitmap bitmap;
private String type;
private boolean verified;
private String displayName;
private Uri lookupUri;
@ -97,7 +99,6 @@ public class ContactInfo {
private static Map<String, Lookup> emailLookup = new ConcurrentHashMap<>();
private static final Map<String, ContactInfo> emailContactInfo = new HashMap<>();
private static final Map<String, Gravatar> emailGravatar = new HashMap<>();
private static final ExecutorService executorLookup =
Helper.getBackgroundExecutor(1, "contact");
@ -125,6 +126,10 @@ public class ContactInfo {
private ContactInfo() {
}
String getType() {
return type;
}
boolean isVerified() {
return (bitmap != null && verified);
}
@ -189,10 +194,6 @@ public class ContactInfo {
emailContactInfo.clear();
}
synchronized (emailGravatar) {
emailGravatar.clear();
}
if (!files)
return;
@ -251,8 +252,8 @@ public class ContactInfo {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean avatars = prefs.getBoolean("avatars", true);
boolean gravatars = prefs.getBoolean("gravatars", false);
boolean bimi = prefs.getBoolean("bimi", false);
boolean gravatars = prefs.getBoolean("gravatars", false);
boolean favicons = prefs.getBoolean("favicons", false);
boolean generated = prefs.getBoolean("generated_icons", true);
boolean identicons = prefs.getBoolean("identicons", false);
@ -286,6 +287,7 @@ public class ContactInfo {
try (InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(
resolver, lookupUri, false)) {
info.bitmap = BitmapFactory.decodeStream(is);
info.type = "contact";
} catch (Throwable ex) {
Log.e(ex);
}
@ -299,71 +301,25 @@ public class ContactInfo {
}
}
// Gravatar
if (info.bitmap == null && gravatars && canGravatars()) {
if (!TextUtils.isEmpty(info.email)) {
String gkey = info.email.toLowerCase(Locale.ROOT);
boolean lookup;
synchronized (emailGravatar) {
Gravatar avatar = emailGravatar.get(gkey);
lookup = (avatar == null || avatar.isExpired() || avatar.isAvailable());
}
if (lookup) {
HttpURLConnection urlConnection = null;
try {
String hash = Helper.md5(gkey.getBytes());
URL url = new URL(BuildConfig.GRAVATAR_URI + hash + "?d=404");
Log.i("Gravatar key=" + gkey + " url=" + url);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(GRAVATAR_TIMEOUT);
urlConnection.setConnectTimeout(GRAVATAR_TIMEOUT);
urlConnection.setRequestProperty("User-Agent", WebViewEx.getUserAgent(context));
urlConnection.connect();
int status = urlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
info.bitmap = BitmapFactory.decodeStream(urlConnection.getInputStream());
// Positive reply
synchronized (emailGravatar) {
emailGravatar.put(gkey, new Gravatar(true));
}
} else if (status == HttpURLConnection.HTTP_NOT_FOUND) {
// Negative reply
synchronized (emailGravatar) {
emailGravatar.put(gkey, new Gravatar(false));
}
} else
throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage());
} catch (Throwable ex) {
Log.w(ex);
} finally {
if (urlConnection != null)
urlConnection.disconnect();
}
}
}
}
// Favicon
if (info.bitmap == null && (bimi || favicons) &&
!EntityFolder.JUNK.equals(folderType)) {
String domain = UriHelper.getEmailDomain(info.email);
if (domain != null) {
if (info.bitmap == null &&
!EntityFolder.JUNK.equals(folderType) &&
(bimi || (gravatars && canGravatars()) || favicons)) {
String d = UriHelper.getEmailDomain(info.email);
if (d != null) {
// Prevent using Doodles
if ("google.com".equals(domain) ||
"gmail.com".equals(domain) ||
"googlemail.com".equals(domain))
domain = "support.google.com";
if ("google.com".equals(d) ||
"gmail.com".equals(d) ||
"googlemail.com".equals(d))
d = "support.google.com";
// https://yahoo.fr redirects unsafely to http://fr.yahoo.com/favicon.ico
String[] d = domain.split("\\.");
if (d.length > 1 && "yahoo".equals(d[d.length - 2]))
domain = "yahoo.com";
String[] dparts = d.split("\\.");
if (dparts.length > 1 && "yahoo".equals(dparts[dparts.length - 2]))
d = "yahoo.com";
final String domain = d.toLowerCase(Locale.ROOT);
final String email = info.email.toLowerCase(Locale.ROOT);
File dir = new File(context.getCacheDir(), "favicons");
if (!dir.exists())
@ -371,32 +327,79 @@ public class ContactInfo {
try {
// check cache
File verified = new File(dir, domain + "_verified");
File input = (verified.exists() ? verified : new File(dir, domain));
if (input.exists())
if (input.length() == 0)
Log.i("Favicon blacklisted domain=" + domain);
File[] files = null;
if (gravatars && canGravatars()) {
File f = new File(dir, email + ".gravatar");
if (f.exists())
files = new File[]{f};
}
if (files == null)
files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String name) {
return name.startsWith(domain);
}
});
if (files != null && files.length == 1) {
if (files[0].length() == 0)
Log.i("Avatar blacklisted cache" + files[0].getName());
else {
info.bitmap = BitmapFactory.decodeFile(input.getAbsolutePath());
info.verified = input.getName().endsWith("_verified");
Log.i("Avatar from cache=" + files[0].getName());
String ext = Helper.getExtension(files[0].getName());
String[] data = (ext == null ? null : ext.split("_"));
info.bitmap = BitmapFactory.decodeFile(files[0].getAbsolutePath());
if (data != null) {
info.type = (data.length > 0 ? data[0] : "unknown");
info.verified = (data.length > 1 && "verified".equals(data[1]));
}
}
else {
} else {
final int scaleToPixels = Helper.dp2pixels(context, FAVICON_ICON_SIZE);
List<Future<Favicon>> futures = new ArrayList<>();
if (bimi) {
final String _domain = domain;
if (bimi)
futures.add(executorFavicon.submit(new Callable<Favicon>() {
@Override
public Favicon call() throws Exception {
// TODO: BIMI selector
Pair<Bitmap, Boolean> bimi =
Bimi.get(context, _domain, "default", scaleToPixels);
return (bimi == null ? null : new Favicon(bimi.first, bimi.second));
Bimi.get(context, domain, "default", scaleToPixels);
return (bimi == null ? null : new Favicon(bimi.first, "vmc", bimi.second));
}
}));
if (gravatars && canGravatars())
futures.add(executorFavicon.submit(new Callable<Favicon>() {
@Override
public Favicon call() throws Exception {
String hash = Helper.md5(email.getBytes());
URL url = new URL(BuildConfig.GRAVATAR_URI + hash + "?d=404");
Log.i("Gravatar key=" + email + " url=" + url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(GRAVATAR_TIMEOUT);
urlConnection.setConnectTimeout(GRAVATAR_TIMEOUT);
urlConnection.setRequestProperty("User-Agent", WebViewEx.getUserAgent(context));
urlConnection.connect();
try {
int status = urlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
// Positive reply
Bitmap bitmap = BitmapFactory.decodeStream(urlConnection.getInputStream());
return (bitmap == null ? null : new Favicon(bitmap, "gravatar", false));
} else if (status == HttpURLConnection.HTTP_NOT_FOUND) {
// Negative reply
return null;
} else
throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage());
} finally {
urlConnection.disconnect();
}
}
}));
}
if (favicons) {
String host = domain;
@ -443,6 +446,7 @@ public class ContactInfo {
Favicon favicon = future.get();
if (favicon != null) {
info.bitmap = favicon.bitmap;
info.type = favicon.type;
info.verified = favicon.verified;
break;
}
@ -456,10 +460,14 @@ public class ContactInfo {
throw ex;
// Add to cache
File output = new File(dir, domain + (info.verified ? "_verified" : ""));
File output = new File(dir,
("gravatar".equals(info.type) ? email : domain) +
"." + info.type +
(info.verified ? "_verified" : ""));
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(output))) {
info.bitmap.compress(Bitmap.CompressFormat.PNG, 90, os);
}
Log.i("Avatar to cache=" + output.getName());
}
} catch (Throwable ex) {
if (isRecoverable(ex, context))
@ -489,9 +497,12 @@ public class ContactInfo {
identicon = true;
info.bitmap = ImageHelper.generateIdenticon(
info.email, dp, 5, context);
} else
info.type = "identicon";
} else {
info.bitmap = ImageHelper.generateLetterIcon(
info.email, address.getPersonal(), dp, context);
info.type = "generated";
}
}
}
@ -1023,33 +1034,17 @@ public class ContactInfo {
private static class Favicon {
private Bitmap bitmap;
private String type;
private boolean verified;
private Favicon(@NonNull Bitmap bitmap) {
this(bitmap, false);
this(bitmap, "favicon", false);
}
private Favicon(@NonNull Bitmap bitmap, boolean verified) {
private Favicon(@NonNull Bitmap bitmap, String type, boolean verified) {
this.bitmap = bitmap;
this.type = type;
this.verified = verified;
}
}
private static class Gravatar {
private boolean available;
private long time;
Gravatar(boolean available) {
this.available = available;
this.time = new Date().getTime();
}
boolean isAvailable() {
return available;
}
boolean isExpired() {
return (new Date().getTime() - time > CACHE_GRAVATAR_DURATION);
}
}
}

@ -111,7 +111,7 @@ public class FragmentOptions extends FragmentBase {
"cards", "beige", "tabular_card_bg", "shadow_unread",
"indentation", "date", "date_bold", "threading", "threading_unread",
"highlight_unread", "highlight_color", "color_stripe",
"avatars", "gravatars", "bimi", "favicons", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold",
"avatars", "bimi", "gravatars", "favicons", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold",
"email_format", "prefer_contact", "only_contact", "distinguish_contacts", "show_recipients", "authentication",
"subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize",
"keywords_header", "labels_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines",

@ -85,11 +85,10 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private ViewButtonColor btnHighlightColor;
private SwitchCompat swColorStripe;
private SwitchCompat swAvatars;
private TextView tvGravatarsHint;
private SwitchCompat swGravatars;
private SwitchCompat swBimi;
private SwitchCompat swBimiVmc;
private ImageButton ibBimi;
private TextView tvGravatarsHint;
private SwitchCompat swGravatars;
private SwitchCompat swFavicons;
private TextView tvFaviconsHint;
private SwitchCompat swGeneratedIcons;
@ -156,7 +155,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
"date", "date_bold", "navbar_colorize", "portrait2", "landscape", "landscape3",
"threading", "threading_unread", "indentation", "seekbar", "actionbar", "actionbar_color",
"highlight_unread", "highlight_color", "color_stripe",
"avatars", "gravatars", "bimi", "bimi_vmc", "favicons", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold",
"avatars", "bimi", "gravatars", "favicons", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold",
"email_format", "prefer_contact", "only_contact", "distinguish_contacts", "show_recipients",
"subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize",
"keywords_header", "labels_header", "flags", "flags_background",
@ -202,11 +201,10 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
btnHighlightColor = view.findViewById(R.id.btnHighlightColor);
swColorStripe = view.findViewById(R.id.swColorStripe);
swAvatars = view.findViewById(R.id.swAvatars);
swGravatars = view.findViewById(R.id.swGravatars);
tvGravatarsHint = view.findViewById(R.id.tvGravatarsHint);
swBimi = view.findViewById(R.id.swBimi);
swBimiVmc = view.findViewById(R.id.swBimiVmc);
ibBimi = view.findViewById(R.id.ibBimi);
swGravatars = view.findViewById(R.id.swGravatars);
tvGravatarsHint = view.findViewById(R.id.tvGravatarsHint);
swFavicons = view.findViewById(R.id.swFavicons);
tvFaviconsHint = view.findViewById(R.id.tvFaviconsHint);
swGeneratedIcons = view.findViewById(R.id.swGeneratedIcons);
@ -470,43 +468,34 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
}
});
swGravatars.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
swBimi.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("gravatars", checked).apply();
prefs.edit().putBoolean("bimi", checked).apply();
ContactInfo.clearCache(getContext());
}
});
tvGravatarsHint.getPaint().setUnderlineText(true);
tvGravatarsHint.setOnClickListener(new View.OnClickListener() {
ibBimi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.view(v.getContext(), Uri.parse(Helper.GRAVATAR_PRIVACY_URI), true);
}
});
swBimi.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("bimi", checked).apply();
swBimiVmc.setEnabled(checked);
ContactInfo.clearCache(getContext());
Helper.view(v.getContext(), Uri.parse("https://bimigroup.org/"), true);
}
});
swBimiVmc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
swGravatars.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("bimi_vmc", checked).apply();
prefs.edit().putBoolean("gravatars", checked).apply();
ContactInfo.clearCache(getContext());
}
});
ibBimi.setOnClickListener(new View.OnClickListener() {
tvGravatarsHint.getPaint().setUnderlineText(true);
tvGravatarsHint.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.view(v.getContext(), Uri.parse("https://bimigroup.org/"), true);
Helper.view(v.getContext(), Uri.parse(Helper.GRAVATAR_PRIVACY_URI), true);
}
});
@ -1027,10 +1016,8 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swColorStripe.setChecked(prefs.getBoolean("color_stripe", true));
swAvatars.setChecked(prefs.getBoolean("avatars", true));
swGravatars.setChecked(prefs.getBoolean("gravatars", false));
swBimi.setChecked(prefs.getBoolean("bimi", false));
swBimiVmc.setChecked(prefs.getBoolean("bimi_vmc", false));
swBimiVmc.setEnabled(swBimi.isChecked());
swGravatars.setChecked(prefs.getBoolean("gravatars", false));
swFavicons.setChecked(prefs.getBoolean("favicons", false));
swGeneratedIcons.setChecked(prefs.getBoolean("generated_icons", true));
swIdenticons.setChecked(prefs.getBoolean("identicons", false));

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/twotone_check_24"
android:maxLevel="0" />
<item
android:drawable="@drawable/twotone_done_all_24"
android:maxLevel="1" />
</level-list>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
</vector>

@ -487,63 +487,81 @@
app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swGravatars"
android:id="@+id/swBimi"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_gravatars"
android:text="@string/title_advanced_bimi"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swAvatars"
app:switchPadding="12dp" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvGravatarsHint"
android:id="@+id/tvBimiUnverified"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/twotone_open_in_new_12"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
android:drawableStart="@drawable/twotone_check_24"
android:drawablePadding="6dp"
android:drawableTint="?attr/colorWarning"
android:text="@string/title_advanced_gravatars_hint"
android:drawableTint="?attr/colorSeparator"
android:text="@string/title_advanced_bimi_unverified"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textStyle="italic"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swGravatars" />
app:layout_constraintTop_toBottomOf="@id/swBimi" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swBimi"
android:layout_width="0dp"
<eu.faircode.email.FixedTextView
android:id="@+id/tvBimiVerified"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_bimi"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
android:drawableStart="@drawable/twotone_done_all_24"
android:drawablePadding="6dp"
android:drawableTint="?attr/colorAccent"
android:text="@string/title_advanced_bimi_verified"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvGravatarsHint"
app:switchPadding="12dp" />
app:layout_constraintTop_toBottomOf="@id/tvBimiUnverified" />
<ImageButton
android:id="@+id/ibBimi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
android:contentDescription="@string/title_info"
android:tooltipText="@string/title_info"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvBimiVerified"
app:srcCompat="@drawable/twotone_info_24" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swBimiVmc"
android:id="@+id/swGravatars"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_bimi_vmc"
android:text="@string/title_advanced_gravatars"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swBimi"
app:layout_constraintTop_toBottomOf="@id/ibBimi"
app:switchPadding="12dp" />
<ImageButton
android:id="@+id/ibBimi"
<eu.faircode.email.FixedTextView
android:id="@+id/tvGravatarsHint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:contentDescription="@string/title_info"
android:tooltipText="@string/title_info"
android:drawableEnd="@drawable/twotone_open_in_new_12"
android:drawablePadding="6dp"
android:drawableTint="?attr/colorWarning"
android:text="@string/title_advanced_gravatars_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/swBimiVmc"
app:srcCompat="@drawable/twotone_info_24" />
app:layout_constraintTop_toBottomOf="@id/swGravatars" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swFavicons"
@ -553,7 +571,7 @@
android:text="@string/title_advanced_favicons"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ibBimi"
app:layout_constraintTop_toBottomOf="@id/tvGravatarsHint"
app:switchPadding="12dp" />
<eu.faircode.email.FixedTextView

@ -57,15 +57,16 @@
app:layout_constraintTop_toBottomOf="@id/paddingTop"
app:srcCompat="@drawable/twotone_person_24" />
<ImageView
android:id="@+id/ivVerified"
<ImageButton
android:id="@+id/ibVerified"
android:layout_width="21dp"
android:layout_height="21dp"
android:layout_marginStart="6dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="@+id/tvFrom"
app:layout_constraintStart_toEndOf="@id/ibAvatar"
app:layout_constraintTop_toTopOf="@+id/tvFrom"
app:srcCompat="@drawable/twotone_check_24"
app:srcCompat="@drawable/checkmarks"
app:tint="?attr/colorAccent" />
<ImageButton
@ -76,7 +77,7 @@
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/title_legend_auth"
app:layout_constraintBottom_toBottomOf="@+id/tvFrom"
app:layout_constraintStart_toEndOf="@id/ivVerified"
app:layout_constraintStart_toEndOf="@id/ibVerified"
app:layout_constraintTop_toTopOf="@+id/tvFrom"
app:srcCompat="@drawable/twotone_flag_24"
app:tint="?attr/colorWarning" />

@ -56,15 +56,16 @@
app:layout_constraintTop_toBottomOf="@id/paddingTop"
app:srcCompat="@drawable/twotone_person_24" />
<ImageView
android:id="@+id/ivVerified"
<ImageButton
android:id="@+id/ibVerified"
android:layout_width="21dp"
android:layout_height="21dp"
android:layout_marginStart="6dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
app:layout_constraintBottom_toBottomOf="@+id/tvFrom"
app:layout_constraintStart_toEndOf="@id/ibAvatar"
app:layout_constraintTop_toTopOf="@+id/tvFrom"
app:srcCompat="@drawable/twotone_check_24"
app:srcCompat="@drawable/checkmarks"
app:tint="?attr/colorAccent" />
<ImageButton
@ -75,7 +76,7 @@
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/title_legend_auth"
app:layout_constraintBottom_toBottomOf="@+id/tvFrom"
app:layout_constraintStart_toEndOf="@id/ivVerified"
app:layout_constraintStart_toEndOf="@id/ibVerified"
app:layout_constraintTop_toTopOf="@+id/tvFrom"
app:srcCompat="@drawable/twotone_flag_24"
app:tint="?attr/colorWarning" />

@ -398,7 +398,8 @@
<string name="title_advanced_avatars">Show contact photos</string>
<string name="title_advanced_gravatars">Show Gravatars</string>
<string name="title_advanced_bimi" translatable="false">Show Brand Indicators for Message Identification (BIMI)</string>
<string name="title_advanced_bimi_vmc">Check security certificate</string>
<string name="title_advanced_bimi_unverified">Unverified sender</string>
<string name="title_advanced_bimi_verified">Verified sender</string>
<string name="title_advanced_favicons">Show favicons</string>
<string name="title_advanced_generated_icons">Show generated icons</string>
<string name="title_advanced_identicons">Show identicons</string>

Loading…
Cancel
Save