Double check

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

@ -315,8 +315,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private View vwColor; private View vwColor;
private ImageButton ibExpander; private ImageButton ibExpander;
private ImageView ibFlagged; private ImageView ibFlagged;
private ImageView ivVerified;
private ImageButton ibAvatar; private ImageButton ibAvatar;
private ImageButton ibVerified;
private ImageButton ibAuth; private ImageButton ibAuth;
private ImageButton ibPriority; private ImageButton ibPriority;
private ImageView ivImportance; private ImageView ivImportance;
@ -489,8 +489,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
vwColor = itemView.findViewById(R.id.vwColor); vwColor = itemView.findViewById(R.id.vwColor);
ibExpander = itemView.findViewById(R.id.ibExpander); ibExpander = itemView.findViewById(R.id.ibExpander);
ibFlagged = itemView.findViewById(R.id.ibFlagged); ibFlagged = itemView.findViewById(R.id.ibFlagged);
ivVerified = itemView.findViewById(R.id.ivVerified);
ibAvatar = itemView.findViewById(R.id.ibAvatar); ibAvatar = itemView.findViewById(R.id.ibAvatar);
ibVerified = itemView.findViewById(R.id.ibVerified);
ibAuth = itemView.findViewById(R.id.ibAuth); ibAuth = itemView.findViewById(R.id.ibAuth);
ibPriority = itemView.findViewById(R.id.ibPriority); ibPriority = itemView.findViewById(R.id.ibPriority);
ivImportance = itemView.findViewById(R.id.ivImportance); ivImportance = itemView.findViewById(R.id.ivImportance);
@ -727,6 +727,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
}); });
ibAvatar.setOnClickListener(this); ibAvatar.setOnClickListener(this);
ibVerified.setOnClickListener(this);
ibAuth.setOnClickListener(this); ibAuth.setOnClickListener(this);
ibPriority.setOnClickListener(this); ibPriority.setOnClickListener(this);
ibSnoozed.setOnClickListener(this); ibSnoozed.setOnClickListener(this);
@ -852,6 +853,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
touch.setOnClickListener(null); touch.setOnClickListener(null);
ibAvatar.setOnClickListener(null); ibAvatar.setOnClickListener(null);
ibVerified.setOnClickListener(null);
ibAuth.setOnClickListener(null); ibAuth.setOnClickListener(null);
ibPriority.setOnClickListener(null); ibPriority.setOnClickListener(null);
ibSnoozed.setOnClickListener(null); ibSnoozed.setOnClickListener(null);
@ -976,8 +978,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (viewType == ViewType.THREAD) { if (viewType == ViewType.THREAD) {
boolean dim = (message.duplicate || EntityFolder.TRASH.equals(message.folderType)); boolean dim = (message.duplicate || EntityFolder.TRASH.equals(message.folderType));
ibFlagged.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f); ibFlagged.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivVerified.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibAvatar.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); ibAuth.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibPriority.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f); ibPriority.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivImportance.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); ibExpander.setVisibility(View.GONE);
// Photo // Photo
ivVerified.setVisibility(View.GONE);
ibAvatar.setVisibility(avatars ? View.INVISIBLE : View.GONE); ibAvatar.setVisibility(avatars ? View.INVISIBLE : View.GONE);
// Line 1 // Line 1
ibVerified.setVisibility(View.GONE);
ibAuth.setVisibility(authentication && !authenticated ? View.VISIBLE : View.GONE); ibAuth.setVisibility(authentication && !authenticated ? View.VISIBLE : View.GONE);
if (EntityMessage.PRIORITIY_HIGH.equals(message.ui_priority)) { 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.spf) &&
Boolean.TRUE.equals(message.dkim) && Boolean.TRUE.equals(message.dkim) &&
Boolean.TRUE.equals(message.dmarc)); 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) { if (distinguish_contacts) {
@ -3116,6 +3125,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (view.getId() == R.id.ibAvatar) if (view.getId() == R.id.ibAvatar)
onViewContact(message); onViewContact(message);
else if (view.getId() == R.id.ibVerified)
onShowVerified(message);
else if (view.getId() == R.id.ibAuth) else if (view.getId() == R.id.ibAuth)
onShowAuth(message); onShowAuth(message);
else if (view.getId() == R.id.ibPriority) 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) { private void onShowAuth(TupleMessageEx message) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

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

@ -49,6 +49,7 @@ import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -89,6 +90,7 @@ import javax.net.ssl.SSLSession;
public class ContactInfo { public class ContactInfo {
private String email; private String email;
private Bitmap bitmap; private Bitmap bitmap;
private String type;
private boolean verified; private boolean verified;
private String displayName; private String displayName;
private Uri lookupUri; private Uri lookupUri;
@ -97,7 +99,6 @@ public class ContactInfo {
private static Map<String, Lookup> emailLookup = new ConcurrentHashMap<>(); private static Map<String, Lookup> emailLookup = new ConcurrentHashMap<>();
private static final Map<String, ContactInfo> emailContactInfo = new HashMap<>(); private static final Map<String, ContactInfo> emailContactInfo = new HashMap<>();
private static final Map<String, Gravatar> emailGravatar = new HashMap<>();
private static final ExecutorService executorLookup = private static final ExecutorService executorLookup =
Helper.getBackgroundExecutor(1, "contact"); Helper.getBackgroundExecutor(1, "contact");
@ -125,6 +126,10 @@ public class ContactInfo {
private ContactInfo() { private ContactInfo() {
} }
String getType() {
return type;
}
boolean isVerified() { boolean isVerified() {
return (bitmap != null && verified); return (bitmap != null && verified);
} }
@ -189,10 +194,6 @@ public class ContactInfo {
emailContactInfo.clear(); emailContactInfo.clear();
} }
synchronized (emailGravatar) {
emailGravatar.clear();
}
if (!files) if (!files)
return; return;
@ -251,8 +252,8 @@ public class ContactInfo {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean avatars = prefs.getBoolean("avatars", true); boolean avatars = prefs.getBoolean("avatars", true);
boolean gravatars = prefs.getBoolean("gravatars", false);
boolean bimi = prefs.getBoolean("bimi", false); boolean bimi = prefs.getBoolean("bimi", false);
boolean gravatars = prefs.getBoolean("gravatars", false);
boolean favicons = prefs.getBoolean("favicons", false); boolean favicons = prefs.getBoolean("favicons", false);
boolean generated = prefs.getBoolean("generated_icons", true); boolean generated = prefs.getBoolean("generated_icons", true);
boolean identicons = prefs.getBoolean("identicons", false); boolean identicons = prefs.getBoolean("identicons", false);
@ -286,6 +287,7 @@ public class ContactInfo {
try (InputStream is = ContactsContract.Contacts.openContactPhotoInputStream( try (InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(
resolver, lookupUri, false)) { resolver, lookupUri, false)) {
info.bitmap = BitmapFactory.decodeStream(is); info.bitmap = BitmapFactory.decodeStream(is);
info.type = "contact";
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e(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 // Favicon
if (info.bitmap == null && (bimi || favicons) && if (info.bitmap == null &&
!EntityFolder.JUNK.equals(folderType)) { !EntityFolder.JUNK.equals(folderType) &&
String domain = UriHelper.getEmailDomain(info.email); (bimi || (gravatars && canGravatars()) || favicons)) {
String d = UriHelper.getEmailDomain(info.email);
if (domain != null) { if (d != null) {
// Prevent using Doodles // Prevent using Doodles
if ("google.com".equals(domain) || if ("google.com".equals(d) ||
"gmail.com".equals(domain) || "gmail.com".equals(d) ||
"googlemail.com".equals(domain)) "googlemail.com".equals(d))
domain = "support.google.com"; d = "support.google.com";
// https://yahoo.fr redirects unsafely to http://fr.yahoo.com/favicon.ico // https://yahoo.fr redirects unsafely to http://fr.yahoo.com/favicon.ico
String[] d = domain.split("\\."); String[] dparts = d.split("\\.");
if (d.length > 1 && "yahoo".equals(d[d.length - 2])) if (dparts.length > 1 && "yahoo".equals(dparts[dparts.length - 2]))
domain = "yahoo.com"; 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"); File dir = new File(context.getCacheDir(), "favicons");
if (!dir.exists()) if (!dir.exists())
@ -371,32 +327,79 @@ public class ContactInfo {
try { try {
// check cache // check cache
File verified = new File(dir, domain + "_verified"); File[] files = null;
File input = (verified.exists() ? verified : new File(dir, domain)); if (gravatars && canGravatars()) {
if (input.exists()) File f = new File(dir, email + ".gravatar");
if (input.length() == 0) if (f.exists())
Log.i("Favicon blacklisted domain=" + domain); 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 { else {
info.bitmap = BitmapFactory.decodeFile(input.getAbsolutePath()); Log.i("Avatar from cache=" + files[0].getName());
info.verified = input.getName().endsWith("_verified"); 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); final int scaleToPixels = Helper.dp2pixels(context, FAVICON_ICON_SIZE);
List<Future<Favicon>> futures = new ArrayList<>(); List<Future<Favicon>> futures = new ArrayList<>();
if (bimi) { if (bimi)
final String _domain = domain;
futures.add(executorFavicon.submit(new Callable<Favicon>() { futures.add(executorFavicon.submit(new Callable<Favicon>() {
@Override @Override
public Favicon call() throws Exception { public Favicon call() throws Exception {
// TODO: BIMI selector // TODO: BIMI selector
Pair<Bitmap, Boolean> bimi = Pair<Bitmap, Boolean> bimi =
Bimi.get(context, _domain, "default", scaleToPixels); Bimi.get(context, domain, "default", scaleToPixels);
return (bimi == null ? null : new Favicon(bimi.first, bimi.second)); 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) { if (favicons) {
String host = domain; String host = domain;
@ -443,6 +446,7 @@ public class ContactInfo {
Favicon favicon = future.get(); Favicon favicon = future.get();
if (favicon != null) { if (favicon != null) {
info.bitmap = favicon.bitmap; info.bitmap = favicon.bitmap;
info.type = favicon.type;
info.verified = favicon.verified; info.verified = favicon.verified;
break; break;
} }
@ -456,10 +460,14 @@ public class ContactInfo {
throw ex; throw ex;
// Add to cache // 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))) { try (OutputStream os = new BufferedOutputStream(new FileOutputStream(output))) {
info.bitmap.compress(Bitmap.CompressFormat.PNG, 90, os); info.bitmap.compress(Bitmap.CompressFormat.PNG, 90, os);
} }
Log.i("Avatar to cache=" + output.getName());
} }
} catch (Throwable ex) { } catch (Throwable ex) {
if (isRecoverable(ex, context)) if (isRecoverable(ex, context))
@ -489,9 +497,12 @@ public class ContactInfo {
identicon = true; identicon = true;
info.bitmap = ImageHelper.generateIdenticon( info.bitmap = ImageHelper.generateIdenticon(
info.email, dp, 5, context); info.email, dp, 5, context);
} else info.type = "identicon";
} else {
info.bitmap = ImageHelper.generateLetterIcon( info.bitmap = ImageHelper.generateLetterIcon(
info.email, address.getPersonal(), dp, context); info.email, address.getPersonal(), dp, context);
info.type = "generated";
}
} }
} }
@ -1023,33 +1034,17 @@ public class ContactInfo {
private static class Favicon { private static class Favicon {
private Bitmap bitmap; private Bitmap bitmap;
private String type;
private boolean verified; private boolean verified;
private Favicon(@NonNull Bitmap bitmap) { 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.bitmap = bitmap;
this.type = type;
this.verified = verified; 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", "cards", "beige", "tabular_card_bg", "shadow_unread",
"indentation", "date", "date_bold", "threading", "threading_unread", "indentation", "date", "date_bold", "threading", "threading_unread",
"highlight_unread", "highlight_color", "color_stripe", "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", "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", "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", "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 ViewButtonColor btnHighlightColor;
private SwitchCompat swColorStripe; private SwitchCompat swColorStripe;
private SwitchCompat swAvatars; private SwitchCompat swAvatars;
private TextView tvGravatarsHint;
private SwitchCompat swGravatars;
private SwitchCompat swBimi; private SwitchCompat swBimi;
private SwitchCompat swBimiVmc;
private ImageButton ibBimi; private ImageButton ibBimi;
private TextView tvGravatarsHint;
private SwitchCompat swGravatars;
private SwitchCompat swFavicons; private SwitchCompat swFavicons;
private TextView tvFaviconsHint; private TextView tvFaviconsHint;
private SwitchCompat swGeneratedIcons; private SwitchCompat swGeneratedIcons;
@ -156,7 +155,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
"date", "date_bold", "navbar_colorize", "portrait2", "landscape", "landscape3", "date", "date_bold", "navbar_colorize", "portrait2", "landscape", "landscape3",
"threading", "threading_unread", "indentation", "seekbar", "actionbar", "actionbar_color", "threading", "threading_unread", "indentation", "seekbar", "actionbar", "actionbar_color",
"highlight_unread", "highlight_color", "color_stripe", "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", "email_format", "prefer_contact", "only_contact", "distinguish_contacts", "show_recipients",
"subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize", "subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize",
"keywords_header", "labels_header", "flags", "flags_background", "keywords_header", "labels_header", "flags", "flags_background",
@ -202,11 +201,10 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
btnHighlightColor = view.findViewById(R.id.btnHighlightColor); btnHighlightColor = view.findViewById(R.id.btnHighlightColor);
swColorStripe = view.findViewById(R.id.swColorStripe); swColorStripe = view.findViewById(R.id.swColorStripe);
swAvatars = view.findViewById(R.id.swAvatars); swAvatars = view.findViewById(R.id.swAvatars);
swGravatars = view.findViewById(R.id.swGravatars);
tvGravatarsHint = view.findViewById(R.id.tvGravatarsHint);
swBimi = view.findViewById(R.id.swBimi); swBimi = view.findViewById(R.id.swBimi);
swBimiVmc = view.findViewById(R.id.swBimiVmc);
ibBimi = view.findViewById(R.id.ibBimi); ibBimi = view.findViewById(R.id.ibBimi);
swGravatars = view.findViewById(R.id.swGravatars);
tvGravatarsHint = view.findViewById(R.id.tvGravatarsHint);
swFavicons = view.findViewById(R.id.swFavicons); swFavicons = view.findViewById(R.id.swFavicons);
tvFaviconsHint = view.findViewById(R.id.tvFaviconsHint); tvFaviconsHint = view.findViewById(R.id.tvFaviconsHint);
swGeneratedIcons = view.findViewById(R.id.swGeneratedIcons); 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 @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("gravatars", checked).apply(); prefs.edit().putBoolean("bimi", checked).apply();
ContactInfo.clearCache(getContext()); ContactInfo.clearCache(getContext());
} }
}); });
tvGravatarsHint.getPaint().setUnderlineText(true); ibBimi.setOnClickListener(new View.OnClickListener() {
tvGravatarsHint.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Helper.view(v.getContext(), Uri.parse(Helper.GRAVATAR_PRIVACY_URI), true); Helper.view(v.getContext(), Uri.parse("https://bimigroup.org/"), 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());
} }
}); });
swBimiVmc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { swGravatars.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("bimi_vmc", checked).apply(); prefs.edit().putBoolean("gravatars", checked).apply();
ContactInfo.clearCache(getContext()); ContactInfo.clearCache(getContext());
} }
}); });
ibBimi.setOnClickListener(new View.OnClickListener() { tvGravatarsHint.getPaint().setUnderlineText(true);
tvGravatarsHint.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { 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)); swColorStripe.setChecked(prefs.getBoolean("color_stripe", true));
swAvatars.setChecked(prefs.getBoolean("avatars", true)); swAvatars.setChecked(prefs.getBoolean("avatars", true));
swGravatars.setChecked(prefs.getBoolean("gravatars", false));
swBimi.setChecked(prefs.getBoolean("bimi", false)); swBimi.setChecked(prefs.getBoolean("bimi", false));
swBimiVmc.setChecked(prefs.getBoolean("bimi_vmc", false)); swGravatars.setChecked(prefs.getBoolean("gravatars", false));
swBimiVmc.setEnabled(swBimi.isChecked());
swFavicons.setChecked(prefs.getBoolean("favicons", false)); swFavicons.setChecked(prefs.getBoolean("favicons", false));
swGeneratedIcons.setChecked(prefs.getBoolean("generated_icons", true)); swGeneratedIcons.setChecked(prefs.getBoolean("generated_icons", true));
swIdenticons.setChecked(prefs.getBoolean("identicons", false)); 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" /> app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/swGravatars" android:id="@+id/swBimi"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_advanced_gravatars" android:text="@string/title_advanced_bimi"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swAvatars" app:layout_constraintTop_toBottomOf="@id/swAvatars"
app:switchPadding="12dp" /> app:switchPadding="12dp" />
<eu.faircode.email.FixedTextView <eu.faircode.email.FixedTextView
android:id="@+id/tvGravatarsHint" android:id="@+id/tvBimiUnverified"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:drawablePadding="6dp"
android:drawableTint="?attr/colorWarning" android:drawableTint="?attr/colorSeparator"
android:text="@string/title_advanced_gravatars_hint" android:text="@string/title_advanced_bimi_unverified"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning" android:textStyle="italic"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swGravatars" /> app:layout_constraintTop_toBottomOf="@id/swBimi" />
<androidx.appcompat.widget.SwitchCompat <eu.faircode.email.FixedTextView
android:id="@+id/swBimi" android:id="@+id/tvBimiVerified"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginStart="12dp"
android:text="@string/title_advanced_bimi" android:layout_marginTop="6dp"
app:layout_constraintEnd_toEndOf="parent" 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_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvGravatarsHint" app:layout_constraintTop_toBottomOf="@id/tvBimiUnverified" />
app:switchPadding="12dp" />
<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 <androidx.appcompat.widget.SwitchCompat
android:id="@+id/swBimiVmc" android:id="@+id/swGravatars"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="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_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swBimi" app:layout_constraintTop_toBottomOf="@id/ibBimi"
app:switchPadding="12dp" /> app:switchPadding="12dp" />
<ImageButton <eu.faircode.email.FixedTextView
android:id="@+id/ibBimi" android:id="@+id/tvGravatarsHint"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:drawableEnd="@drawable/twotone_open_in_new_12"
android:layout_marginTop="12dp" android:drawablePadding="6dp"
android:contentDescription="@string/title_info" android:drawableTint="?attr/colorWarning"
android:tooltipText="@string/title_info" android:text="@string/title_advanced_gravatars_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/swBimiVmc" app:layout_constraintTop_toBottomOf="@id/swGravatars" />
app:srcCompat="@drawable/twotone_info_24" />
<androidx.appcompat.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/swFavicons" android:id="@+id/swFavicons"
@ -553,7 +571,7 @@
android:text="@string/title_advanced_favicons" android:text="@string/title_advanced_favicons"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ibBimi" app:layout_constraintTop_toBottomOf="@id/tvGravatarsHint"
app:switchPadding="12dp" /> app:switchPadding="12dp" />
<eu.faircode.email.FixedTextView <eu.faircode.email.FixedTextView

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

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

@ -398,7 +398,8 @@
<string name="title_advanced_avatars">Show contact photos</string> <string name="title_advanced_avatars">Show contact photos</string>
<string name="title_advanced_gravatars">Show Gravatars</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" 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_favicons">Show favicons</string>
<string name="title_advanced_generated_icons">Show generated icons</string> <string name="title_advanced_generated_icons">Show generated icons</string>
<string name="title_advanced_identicons">Show identicons</string> <string name="title_advanced_identicons">Show identicons</string>

Loading…
Cancel
Save