Added options to open links, show images and original messages without confirmation

pull/172/head
M66B 5 years ago
parent 45f46cf852
commit 3768f5c0ee

@ -106,7 +106,6 @@ Related questions:
Anything on this list is in random order and *might* be added in the near future. Anything on this list is in random order and *might* be added in the near future.
## Frequently requested features ## Frequently requested features
The design is based on many discussions and if you like you can discuss about it [in this forum](https://forum.xda-developers.com/android/apps-games/source-email-t3824168) too. The design is based on many discussions and if you like you can discuss about it [in this forum](https://forum.xda-developers.com/android/apps-games/source-email-t3824168) too.
@ -114,22 +113,6 @@ The goal of the design is to be minimalistic (no unnecessary menus, buttons, etc
All displayed things should be useful in one or another way and should be carefully positioned for easy usage. All displayed things should be useful in one or another way and should be carefully positioned for easy usage.
Fonts, sizes, colors, etc should be material design whenever possible. Fonts, sizes, colors, etc should be material design whenever possible.
Since FairEmail is privacy oriented, the following will not be added:
* Opening links without confirmation (if you want to reset the default *Open with* apps, please [see here](https://support.google.com/android/answer/9415055))
* Showing images and original messages without confirmation, see also [this FAQ](#user-content-faq35)
* Direct file/folder access: for security/privacy reasons (other) apps should use the [Storage Access Framework](https://developer.android.com/guide/topics/providers/document-provider), see also [this FAQ](#user-content-faq49)
Confirmation is just one tap, which is just a small price for better privacy.
You can show images and original messages by default for trusted senders on a case-by-case basis by checking *Do not ask this again for ...*.
Note that your contacts could unknowingly send malicious messages if they got infected with malware.
Stripped and reformatted messages are often better readable than original messages because the margins are removed, and font colors and sizes are standardized.
FairEmail does not allow other apps access to your messages and attachments without your approval.
FairEmail follows all the best practices for an email client as described in [this EFF article](https://www.eff.org/deeplinks/2019/01/stop-tracking-my-emails).
## Frequently Asked Questions ## Frequently Asked Questions
* [(1) Which permissions are needed and why?](#user-content-faq1) * [(1) Which permissions are needed and why?](#user-content-faq1)
@ -1116,10 +1099,16 @@ Note that your contacts could unknowingly send malicious messages if they got in
FairEmail formats messages again causing messages to look different from the original, but also uncovering phishing links. FairEmail formats messages again causing messages to look different from the original, but also uncovering phishing links.
Note that reformatted messages are often better readable than original messages because the margins are removed, and font colors and sizes are standardized.
The Gmail app shows images by default by downloading the images through a Google proxy server. The Gmail app shows images by default by downloading the images through a Google proxy server.
Since the images are downloaded from the source server [in real-time](https://blog.filippo.io/how-the-new-gmail-image-proxy-works-and-what-this-means-for-you/), Since the images are downloaded from the source server [in real-time](https://blog.filippo.io/how-the-new-gmail-image-proxy-works-and-what-this-means-for-you/),
this is even less secure because Google is involved too without providing much benefit. this is even less secure because Google is involved too without providing much benefit.
You can show images and original messages by default for trusted senders on a case-by-case basis by checking *Do not ask this again for ...*.
If you want to reset the default *Open with* apps, please [see here](https://support.google.com/android/answer/9415055)).
<br /> <br />
<a name="faq36"></a> <a name="faq36"></a>
@ -2053,7 +2042,9 @@ See also [this FAQ](#user-content-faq92).
For privacy and security reasons FairEmail does not have permissions to directly access files, For privacy and security reasons FairEmail does not have permissions to directly access files,
instead the Storage Access Framework, available and recommended since Android 4.4 KitKat (released in 2013), is used to select files. instead the Storage Access Framework, available and recommended since Android 4.4 KitKat (released in 2013), is used to select files.
If an app is listed depends on if the app implements a [document provider](https://developer.android.com/guide/topics/providers/document-provider). If an app is listed depends on if the app implements a [document provider](https://developer.android.com/guide/topics/providers/document-provider).
If the app is not listed, you might need to ask the developer of the app to add support for the Storage Access Framework.
Android Q will make it harder and maybe even impossible to directly access files, Android Q will make it harder and maybe even impossible to directly access files,
see [here](https://developer.android.com/preview/privacy/scoped-storage) and [here](https://www.xda-developers.com/android-q-storage-access-framework-scoped-storage/) for more details. see [here](https://developer.android.com/preview/privacy/scoped-storage) and [here](https://www.xda-developers.com/android-q-storage-access-framework-scoped-storage/) for more details.

@ -1484,6 +1484,14 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
} }
} }
boolean confirm_images = prefs.getBoolean("confirm_images", true);
boolean confirm_html = prefs.getBoolean("confirm_html", true);
if (!confirm_images)
properties.setValue("images", message.id, true);
if (!confirm_html)
properties.setValue("full", message.id, true);
boolean show_full = properties.getValue("full", message.id); boolean show_full = properties.getValue("full", message.id);
boolean show_images = properties.getValue("images", message.id); boolean show_images = properties.getValue("images", message.id);
boolean show_quotes = (properties.getValue("quotes", message.id) || !collapse_quotes); boolean show_quotes = (properties.getValue("quotes", message.id) || !collapse_quotes);
@ -3389,13 +3397,17 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if ("cid".equals(uri.getScheme()) || "data".equals(uri.getScheme())) if ("cid".equals(uri.getScheme()) || "data".equals(uri.getScheme()))
return false; return false;
Bundle args = new Bundle(); boolean confirm_links = prefs.getBoolean("confirm_links", true);
args.putParcelable("uri", uri); if (confirm_links) {
args.putString("title", title); Bundle args = new Bundle();
args.putParcelable("uri", uri);
args.putString("title", title);
FragmentDialogLink fragment = new FragmentDialogLink(); FragmentDialogLink fragment = new FragmentDialogLink();
fragment.setArguments(args); fragment.setArguments(args);
fragment.show(parentFragment.getParentFragmentManager(), "open:link"); fragment.show(parentFragment.getParentFragmentManager(), "open:link");
} else
Helper.view(context, uri, false);
} }
return true; return true;

@ -63,6 +63,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class FragmentOptionsPrivacy extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener { public class FragmentOptionsPrivacy extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener {
private SwitchCompat swConfirmLinks;
private SwitchCompat swConfirmImages;
private SwitchCompat swConfirmHtml;
private SwitchCompat swDisableTracking; private SwitchCompat swDisableTracking;
private SwitchCompat swDisplayHidden; private SwitchCompat swDisplayHidden;
private Spinner spEncryptMethod; private Spinner spEncryptMethod;
@ -84,6 +87,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
private List<String> openPgpProvider = new ArrayList<>(); private List<String> openPgpProvider = new ArrayList<>();
private final static String[] RESET_OPTIONS = new String[]{ private final static String[] RESET_OPTIONS = new String[]{
"confirm_links", "confirm_images", "confirm_html",
"disable_tracking", "display_hidden", "disable_tracking", "display_hidden",
"default_encrypt_method", "openpgp_provider", "autocrypt", "autocrypt_mutual", "default_encrypt_method", "openpgp_provider", "autocrypt", "autocrypt_mutual",
"sign_default", "encrypt_default", "auto_decrypt", "sign_default", "encrypt_default", "auto_decrypt",
@ -102,6 +106,9 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
// Get controls // Get controls
swConfirmLinks = view.findViewById(R.id.swConfirmLinks);
swConfirmImages = view.findViewById(R.id.swConfirmImages);
swConfirmHtml = view.findViewById(R.id.swConfirmHtml);
swDisableTracking = view.findViewById(R.id.swDisableTracking); swDisableTracking = view.findViewById(R.id.swDisableTracking);
swDisplayHidden = view.findViewById(R.id.swDisplayHidden); swDisplayHidden = view.findViewById(R.id.swDisplayHidden);
spEncryptMethod = view.findViewById(R.id.spEncryptMethod); spEncryptMethod = view.findViewById(R.id.spEncryptMethod);
@ -137,6 +144,27 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
swConfirmLinks.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("confirm_links", checked).apply();
}
});
swConfirmImages.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("confirm_images", checked).apply();
}
});
swConfirmHtml.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("confirm_html", checked).apply();
}
});
swDisableTracking.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { swDisableTracking.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@ -353,6 +381,9 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
private void setOptions() { private void setOptions() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
swConfirmLinks.setChecked(prefs.getBoolean("confirm_links", true));
swConfirmImages.setChecked(prefs.getBoolean("confirm_images", true));
swConfirmHtml.setChecked(prefs.getBoolean("confirm_html", true));
swDisableTracking.setChecked(prefs.getBoolean("disable_tracking", true)); swDisableTracking.setChecked(prefs.getBoolean("disable_tracking", true));
swDisplayHidden.setChecked(prefs.getBoolean("display_hidden", false)); swDisplayHidden.setChecked(prefs.getBoolean("display_hidden", false));

@ -12,6 +12,81 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swConfirmLinks"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:checked="true"
android:text="@string/title_advanced_confirm_links"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:switchPadding="12dp" />
<TextView
android:id="@+id/tvConfirmLinksHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="48dp"
android:text="@string/title_advanced_display_harmful_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swConfirmLinks" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swConfirmImages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:checked="true"
android:text="@string/title_advanced_confirm_images"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvConfirmLinksHint"
app:switchPadding="12dp" />
<TextView
android:id="@+id/tvConfirmImagesHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="48dp"
android:text="@string/title_advanced_display_harmful_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swConfirmImages" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swConfirmHtml"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:checked="true"
android:text="@string/title_advanced_confirm_html"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvConfirmImagesHint"
app:switchPadding="12dp" />
<TextView
android:id="@+id/tvConfirmHtmlHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="48dp"
android:text="@string/title_advanced_display_harmful_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swConfirmHtml" />
<androidx.appcompat.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/swDisableTracking" android:id="@+id/swDisableTracking"
android:layout_width="0dp" android:layout_width="0dp"
@ -21,7 +96,7 @@
android:text="@string/title_advanced_tracking" android:text="@string/title_advanced_tracking"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/tvConfirmHtmlHint"
app:switchPadding="12dp" /> app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat

@ -373,6 +373,9 @@
<string name="title_advanced_sound">Select notification sound</string> <string name="title_advanced_sound">Select notification sound</string>
<string name="title_advanced_alert_once" translatable="false">MIUI notification sound workaround</string> <string name="title_advanced_alert_once" translatable="false">MIUI notification sound workaround</string>
<string name="title_advanced_confirm_links">Confirm opening links</string>
<string name="title_advanced_confirm_images">Confirm showing images</string>
<string name="title_advanced_confirm_html">Show reformatted messages by default</string>
<string name="title_advanced_tracking">Automatically recognize and disable tracking images</string> <string name="title_advanced_tracking">Automatically recognize and disable tracking images</string>
<string name="title_advanced_display_hidden">Display hidden message texts</string> <string name="title_advanced_display_hidden">Display hidden message texts</string>
<string name="title_advanced_encrypt_method">Default encryption method</string> <string name="title_advanced_encrypt_method">Default encryption method</string>
@ -447,6 +450,7 @@
<string name="title_advanced_autoclose_hint">Automatically close conversations when all messages are archived, sent or trashed</string> <string name="title_advanced_autoclose_hint">Automatically close conversations when all messages are archived, sent or trashed</string>
<string name="title_advanced_sender_hint">Most providers do not allow modified sender addresses</string> <string name="title_advanced_sender_hint">Most providers do not allow modified sender addresses</string>
<string name="title_advanced_display_harmful_hint">Disabling this option might be harmful for your privacy</string>
<string name="title_advanced_display_hidden_hint">This can result in odd looking and double texts</string> <string name="title_advanced_display_hidden_hint">This can result in odd looking and double texts</string>
<string name="title_advanced_display_pin_hint">A PIN takes precedence over biometrics authentication</string> <string name="title_advanced_display_pin_hint">A PIN takes precedence over biometrics authentication</string>

Loading…
Cancel
Save