Removed Gravatar/Libravatar from Play store release

pull/208/head
M66B 3 years ago
parent 8adf33ce9a
commit 86ed20a1bd

@ -37,9 +37,9 @@ FairEmail **can use** these services if they are explicitly enabled (off by defa
* [Thunderbird autoconfiguration](https://wiki.mozilla.org/Thunderbird:Autoconfiguration) – [Privacy policy](https://www.mozilla.org/privacy/) * [Thunderbird autoconfiguration](https://wiki.mozilla.org/Thunderbird:Autoconfiguration) – [Privacy policy](https://www.mozilla.org/privacy/)
* [DeepL](https://www.deepl.com/) – [Privacy policy](https://www.deepl.com/privacy/) * [DeepL](https://www.deepl.com/) – [Privacy policy](https://www.deepl.com/privacy/)
* [LanguageTool](https://languagetool.org/) – [Privacy policy](https://languagetool.org/legal/privacy) * [LanguageTool](https://languagetool.org/) – [Privacy policy](https://languagetool.org/legal/privacy)
* [Gravatar](https://gravatar.com/) – [Privacy policy](https://automattic.com/privacy/) * [Gravatar](https://gravatar.com/) (GitHub version only) – [Privacy policy](https://automattic.com/privacy/)
* [Libravatar](https://www.libravatar.org/) – [Privacy policy](https://www.libravatar.org/privacy/) * [Libravatar](https://www.libravatar.org/) (GitHub version only) – [Privacy policy](https://www.libravatar.org/privacy/)
* [GitHub](https://github.com/) – [Privacy policy](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement) * [GitHub](https://github.com/) (GitHub version only) – [Privacy policy](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement)
FairEmail **can access** the websites at the domain names of email addresses (username@domain.name) FairEmail **can access** the websites at the domain names of email addresses (username@domain.name)
if [Brand Indicators for Message Identification](https://en.wikipedia.org/wiki/Brand_Indicators_for_Message_Identification) (BIMI) if [Brand Indicators for Message Identification](https://en.wikipedia.org/wiki/Brand_Indicators_for_Message_Identification) (BIMI)
@ -58,24 +58,24 @@ FairEmail **is** [GDPR compliant](https://gdpr.eu/).
This table provides a complete overview of all shared data and the conditions under which data will be shared: This table provides a complete overview of all shared data and the conditions under which data will be shared:
| Service/function | Data sent | When the data will be sent | | Service/function | Data sent | When the data will be sent |
| ----------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------- | | ------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Mozilla autoconfig| Email address of email accounts | Upon configuring an email account | | Mozilla autoconfig | Email address of email accounts | Upon configuring an email account |
| Email server | Login credentials, messages sent | Upon configuring and using an account or identity and upon sending messages | | Email server | Login credentials, messages sent | Upon configuring and using an account or identity and upon sending messages |
| ipinfo.io | IP (network) address of domain names of links or email addresses | Upon pressing a button in the link confirmation dialog | | ipinfo.io | IP (network) address of domain names of links or email addresses | Upon pressing a button in the link confirmation dialog |
| Spamhaus | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | | Spamhaus | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message |
| Spamcop | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | | Spamcop | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message |
| Barracuda | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | | Barracuda | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message |
| DeepL | Received or entered message text and target language code | Upon pressing a translate button | | DeepL | Received or entered message text and target language code | Upon pressing a translate button |
| LanguageTool | Entered message texts | Upon long pressing the save draft button | | LanguageTool | Entered message texts | Upon long pressing the save draft button |
| Gravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Gravatars are enabled, upon receiving a message | | Gravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Gravatars are enabled, upon receiving a message (GitHub version only) |
| Libravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Libravatars are enabled, upon receiving a message | | Libravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Libravatars are enabled, upon receiving a message (GitHub version only) |
| GitHub | None, but see the remarks below | Upon downloading Disconnect's Tracker Protection lists | | GitHub | None, but see the remarks below | Upon downloading Disconnect's Tracker Protection lists |
| | | Upon checking for updates (GitHub version only) | | | | Upon checking for updates (GitHub version only) |
| BIMI | Domain name of email addresses | If BIMI is enabled, upon receiving a message | | BIMI | Domain name of email addresses | If BIMI is enabled, upon receiving a message |
| Favicons | Domain name of email addresses | If favicons are enabled, upon receiving a message | | Favicons | Domain name of email addresses | If favicons are enabled, upon receiving a message |
| Link title | Link address | Upon pressing a button in the insert link dialog | | Link title | Link address | Upon pressing a button in the insert link dialog |
| Bugsnag | Information about warnings and errors | If error reporting is enabled, upon detecting an abnormal situation | | Bugsnag | Information about warnings and errors | If error reporting is enabled, upon detecting an abnormal situation |
All data is sent to improve the user experience in some way, All data is sent to improve the user experience in some way,
like to simplify account setup, identify spam and malicious messages, display message and sender information, find bugs and errors, etc. like to simplify account setup, identify spam and malicious messages, display message and sender information, find bugs and errors, etc.

@ -0,0 +1,49 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2022 by Marcel Bokhorst (M66B)
*/
import android.content.Context;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import java.util.concurrent.Callable;
public class Avatar {
static final String GRAVATAR_PRIVACY_URI = "";
static final String LIBRAVATAR_PRIVACY_URI = "";
static Callable<ContactInfo.Favicon> getGravatar(String email, int scaleToPixels, Context context) {
return new Callable<ContactInfo.Favicon>() {
@Override
public ContactInfo.Favicon call() throws Exception {
throw new NotImplementedException("Gravatar");
}
};
}
static Callable<ContactInfo.Favicon> getLibravatar(String email, int scaleToPixels, Context context) {
return new Callable<ContactInfo.Favicon>() {
@Override
public ContactInfo.Favicon call() throws Exception {
throw new NotImplementedException("Libravatar");
}
};
}
}

@ -0,0 +1,120 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2022 by Marcel Bokhorst (M66B)
*/
import android.content.Context;
import android.graphics.Bitmap;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.Callable;
public class Avatar {
static final String GRAVATAR_PRIVACY_URI = "https://automattic.com/privacy/";
static final String LIBRAVATAR_PRIVACY_URI = "https://www.libravatar.org/privacy/";
private static final String GRAVATAR_URI = "https://www.gravatar.com/avatar/";
private static final int GRAVATAR_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
private static final int GRAVATAR_READ_TIMEOUT = 10 * 1000; // milliseconds
private static final int LIBRAVATAR_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
private static final int LIBRAVATAR_READ_TIMEOUT = 10 * 1000; // milliseconds
private static final String LIBRAVATAR_DNS = "_avatars-sec._tcp,_avatars._tcp";
private static final String LIBRAVATAR_URI = "https://seccdn.libravatar.org/avatar/";
static Callable<ContactInfo.Favicon> getGravatar(String email, int scaleToPixels, Context context) {
return new Callable<ContactInfo.Favicon>() {
@Override
public ContactInfo.Favicon call() throws Exception {
String hash = Helper.md5(email.getBytes());
URL url = new URL(GRAVATAR_URI + hash + "?d=404");
Log.i("Gravatar key=" + email + " url=" + url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(GRAVATAR_READ_TIMEOUT);
urlConnection.setConnectTimeout(GRAVATAR_CONNECT_TIMEOUT);
ConnectionHelper.setUserAgent(context, urlConnection);
urlConnection.connect();
try {
int status = urlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
// Positive reply
Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels);
return (bitmap == null ? null : new ContactInfo.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();
}
}
};
}
static Callable<ContactInfo.Favicon> getLibravatar(String email, int scaleToPixels, Context context) {
return new Callable<ContactInfo.Favicon>() {
@Override
public ContactInfo.Favicon call() throws Exception {
String domain = UriHelper.getEmailDomain(email);
// https://wiki.libravatar.org/api/
String baseUrl = LIBRAVATAR_URI;
for (String dns : LIBRAVATAR_DNS.split(",")) {
DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, dns + "." + domain, "srv");
if (records.length > 0) {
baseUrl = (records[0].port == 443 ? "https" : "http") + "://" + records[0].name + "/avatar/";
break;
}
}
String hash = Helper.md5(email.getBytes());
URL url = new URL(baseUrl + hash + "?d=404");
Log.i("Libravatar key=" + email + " url=" + url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(LIBRAVATAR_READ_TIMEOUT);
urlConnection.setConnectTimeout(LIBRAVATAR_CONNECT_TIMEOUT);
ConnectionHelper.setUserAgent(context, urlConnection);
urlConnection.connect();
try {
int status = urlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
// Positive reply
Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels);
return (bitmap == null ? null : new ContactInfo.Favicon(bitmap, "libravatar", false));
} else if (status == HttpURLConnection.HTTP_NOT_FOUND) {
// Negative reply
return null;
} else
throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage());
} finally {
urlConnection.disconnect();
}
}
};
}
}

@ -6888,8 +6888,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
boolean contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS); boolean contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS);
boolean avatars = prefs.getBoolean("avatars", true); boolean avatars = prefs.getBoolean("avatars", true);
boolean bimi = prefs.getBoolean("bimi", false); boolean bimi = prefs.getBoolean("bimi", false);
boolean gravatars = prefs.getBoolean("gravatars", false); boolean gravatars = (prefs.getBoolean("gravatars", false) && !BuildConfig.PLAY_STORE_RELEASE);
boolean libravatars = prefs.getBoolean("libravatars", false); boolean libravatars = (prefs.getBoolean("libravatars", false) && !BuildConfig.PLAY_STORE_RELEASE);
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);

@ -117,14 +117,6 @@ public class ContactInfo {
private static final long CACHE_FAVICON_DURATION = 2 * 7 * 24 * 60 * 60 * 1000L; // milliseconds private static final long CACHE_FAVICON_DURATION = 2 * 7 * 24 * 60 * 60 * 1000L; // milliseconds
private static final float MIN_FAVICON_LUMINANCE = 0.2f; private static final float MIN_FAVICON_LUMINANCE = 0.2f;
private static final String GRAVATAR_URI = "https://www.gravatar.com/avatar/";
private static final int GRAVATAR_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
private static final int GRAVATAR_READ_TIMEOUT = 10 * 1000; // milliseconds
private static final int LIBRAVATAR_CONNECT_TIMEOUT = 5 * 1000; // milliseconds
private static final int LIBRAVATAR_READ_TIMEOUT = 10 * 1000; // milliseconds
private static final String LIBRAVATAR_DNS = "_avatars-sec._tcp,_avatars._tcp";
private static final String LIBRAVATAR_URI = "https://seccdn.libravatar.org/avatar/";
// https://css-tricks.com/prefetching-preloading-prebrowsing/ // https://css-tricks.com/prefetching-preloading-prebrowsing/
// https://developer.mozilla.org/en-US/docs/Web/Performance/dns-prefetch // https://developer.mozilla.org/en-US/docs/Web/Performance/dns-prefetch
private static final List<String> REL_EXCLUDE = Collections.unmodifiableList(Arrays.asList( private static final List<String> REL_EXCLUDE = Collections.unmodifiableList(Arrays.asList(
@ -265,8 +257,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 bimi = prefs.getBoolean("bimi", false); boolean bimi = prefs.getBoolean("bimi", false);
boolean gravatars = prefs.getBoolean("gravatars", false); boolean gravatars = (prefs.getBoolean("gravatars", false) && !BuildConfig.PLAY_STORE_RELEASE);
boolean libravatars = prefs.getBoolean("libravatars", false); boolean libravatars = (prefs.getBoolean("libravatars", false) && !BuildConfig.PLAY_STORE_RELEASE);
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);
@ -388,9 +380,9 @@ public class ContactInfo {
})); }));
if (gravatars) if (gravatars)
futures.add(executorFavicon.submit(getGravatar(email, scaleToPixels, context))); futures.add(executorFavicon.submit(Avatar.getGravatar(email, scaleToPixels, context)));
if (libravatars) if (libravatars)
futures.add(executorFavicon.submit(getLibravatar(email, scaleToPixels, context))); futures.add(executorFavicon.submit(Avatar.getLibravatar(email, scaleToPixels, context)));
if (favicons) { if (favicons) {
String host = domain; String host = domain;
@ -876,85 +868,6 @@ public class ContactInfo {
} }
} }
static Callable<ContactInfo.Favicon> getGravatar(String email, int scaleToPixels, Context context) {
return new Callable<ContactInfo.Favicon>() {
@Override
public ContactInfo.Favicon call() throws Exception {
String hash = Helper.md5(email.getBytes());
URL url = new URL(GRAVATAR_URI + hash + "?d=404");
Log.i("Gravatar key=" + email + " url=" + url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(GRAVATAR_READ_TIMEOUT);
urlConnection.setConnectTimeout(GRAVATAR_CONNECT_TIMEOUT);
ConnectionHelper.setUserAgent(context, urlConnection);
urlConnection.connect();
try {
int status = urlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
// Positive reply
Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels);
return (bitmap == null ? null : new ContactInfo.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();
}
}
};
}
static Callable<ContactInfo.Favicon> getLibravatar(String email, int scaleToPixels, Context context) {
return new Callable<ContactInfo.Favicon>() {
@Override
public ContactInfo.Favicon call() throws Exception {
String domain = UriHelper.getEmailDomain(email);
// https://wiki.libravatar.org/api/
String baseUrl = LIBRAVATAR_URI;
for (String dns : LIBRAVATAR_DNS.split(",")) {
DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, dns + "." + domain, "srv");
if (records.length > 0) {
baseUrl = (records[0].port == 443 ? "https" : "http") + "://" + records[0].name + "/avatar/";
break;
}
}
String hash = Helper.md5(email.getBytes());
URL url = new URL(baseUrl + hash + "?d=404");
Log.i("Libravatar key=" + email + " url=" + url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(LIBRAVATAR_READ_TIMEOUT);
urlConnection.setConnectTimeout(LIBRAVATAR_CONNECT_TIMEOUT);
ConnectionHelper.setUserAgent(context, urlConnection);
urlConnection.connect();
try {
int status = urlConnection.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
// Positive reply
Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels);
return (bitmap == null ? null : new ContactInfo.Favicon(bitmap, "libravatar", false));
} else if (status == HttpURLConnection.HTTP_NOT_FOUND) {
// Negative reply
return null;
} else
throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage());
} finally {
urlConnection.disconnect();
}
}
};
}
private static boolean isRecoverable(Throwable ex, Context context) { private static boolean isRecoverable(Throwable ex, Context context) {
if (ex instanceof SocketTimeoutException) { if (ex instanceof SocketTimeoutException) {
ConnectivityManager cm = Helper.getSystemService(context, ConnectivityManager.class); ConnectivityManager cm = Helper.getSystemService(context, ConnectivityManager.class);

@ -180,6 +180,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private SwitchCompat swAuthentication; private SwitchCompat swAuthentication;
private SwitchCompat swAuthenticationIndicator; private SwitchCompat swAuthenticationIndicator;
private Group grpAvatar;
private Group grpUnzip; private Group grpUnzip;
private NumberFormat NF = NumberFormat.getNumberInstance(); private NumberFormat NF = NumberFormat.getNumberInstance();
@ -334,6 +335,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swAuthentication = view.findViewById(R.id.swAuthentication); swAuthentication = view.findViewById(R.id.swAuthentication);
swAuthenticationIndicator = view.findViewById(R.id.swAuthenticationIndicator); swAuthenticationIndicator = view.findViewById(R.id.swAuthenticationIndicator);
grpAvatar = view.findViewById(R.id.grpAvatar);
grpUnzip = view.findViewById(R.id.grpUnzip); grpUnzip = view.findViewById(R.id.grpUnzip);
List<StyleHelper.FontDescriptor> fonts = StyleHelper.getFonts(getContext()); List<StyleHelper.FontDescriptor> fonts = StyleHelper.getFonts(getContext());
@ -744,7 +746,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
tvGravatarPrivacy.setOnClickListener(new View.OnClickListener() { tvGravatarPrivacy.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(Avatar.GRAVATAR_PRIVACY_URI), true);
} }
}); });
@ -760,7 +762,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
tvLibravatarPrivacy.setOnClickListener(new View.OnClickListener() { tvLibravatarPrivacy.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Helper.view(v.getContext(), Uri.parse(Helper.LIBRAVATAR_PRIVACY_URI), true); Helper.view(v.getContext(), Uri.parse(Avatar.LIBRAVATAR_PRIVACY_URI), true);
} }
}); });
@ -1257,6 +1259,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
FragmentDialogTheme.setBackground(getContext(), view, false); FragmentDialogTheme.setBackground(getContext(), view, false);
swFaviconsPartial.setText(getString(R.string.title_advanced_favicons_partial, swFaviconsPartial.setText(getString(R.string.title_advanced_favicons_partial,
Helper.humanReadableByteCount(ContactInfo.FAVICON_READ_BYTES, false))); Helper.humanReadableByteCount(ContactInfo.FAVICON_READ_BYTES, false)));
grpAvatar.setVisibility(BuildConfig.PLAY_STORE_RELEASE ? View.GONE : View.VISIBLE);
grpUnzip.setVisibility(Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? View.GONE : View.VISIBLE); grpUnzip.setVisibility(Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? View.GONE : View.VISIBLE);
tvBimiUnverified.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); tvBimiUnverified.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);

@ -184,8 +184,6 @@ public class Helper {
static final String SUPPORT_URI = "https://contact.faircode.eu/"; static final String SUPPORT_URI = "https://contact.faircode.eu/";
static final String TEST_URI = "https://play.google.com/apps/testing/" + BuildConfig.APPLICATION_ID; static final String TEST_URI = "https://play.google.com/apps/testing/" + BuildConfig.APPLICATION_ID;
static final String BIMI_PRIVACY_URI = "https://datatracker.ietf.org/doc/html/draft-brotman-ietf-bimi-guidance-03#section-7.4"; static final String BIMI_PRIVACY_URI = "https://datatracker.ietf.org/doc/html/draft-brotman-ietf-bimi-guidance-03#section-7.4";
static final String GRAVATAR_PRIVACY_URI = "https://automattic.com/privacy/";
static final String LIBRAVATAR_PRIVACY_URI = "https://www.libravatar.org/privacy/";
static final String ID_COMMAND_URI = "https://datatracker.ietf.org/doc/html/rfc2971#section-3.1"; static final String ID_COMMAND_URI = "https://datatracker.ietf.org/doc/html/rfc2971#section-3.1";
static final String AUTH_RESULTS_URI = "https://datatracker.ietf.org/doc/html/rfc7601"; static final String AUTH_RESULTS_URI = "https://datatracker.ietf.org/doc/html/rfc7601";
static final String FAVICON_PRIVACY_URI = "https://en.wikipedia.org/wiki/Favicon"; static final String FAVICON_PRIVACY_URI = "https://en.wikipedia.org/wiki/Favicon";

@ -1524,6 +1524,14 @@
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/tvPreviewLinesHint" /> app:layout_constraintTop_toBottomOf="@id/tvPreviewLinesHint" />
<androidx.constraintlayout.widget.Group
android:id="@+id/grpAvatar"
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="
swGravatars,tvGravatarsHint,tvGravatarPrivacy,
swLibravatars,tvLibravatarsHint,tvLibravatarPrivacy" />
</eu.faircode.email.ConstraintLayoutEx> </eu.faircode.email.ConstraintLayoutEx>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

Loading…
Cancel
Save