diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java index 40493e0d29..c392a85745 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java @@ -107,6 +107,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer private SwitchCompat swMonospaced; private SwitchCompat swTextColor; private SwitchCompat swTextSize; + private SwitchCompat swTextAlign; private SwitchCompat swCollapseQuotes; private SwitchCompat swImagesInline; private SwitchCompat swAttachmentsAlt; @@ -124,7 +125,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer "keywords_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines", "addresses", "button_archive_trash", "button_move", - "contrast", "monospaced", "text_color", "text_size", + "contrast", "monospaced", "text_color", "text_size", "text_align", "inline_images", "collapse_quotes", "attachments_alt", "parse_classes", "authentication" }; @@ -189,6 +190,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer swMonospaced = view.findViewById(R.id.swMonospaced); swTextColor = view.findViewById(R.id.swTextColor); swTextSize = view.findViewById(R.id.swTextSize); + swTextAlign = view.findViewById(R.id.swTextAlign); swCollapseQuotes = view.findViewById(R.id.swCollapseQuotes); swImagesInline = view.findViewById(R.id.swImagesInline); swAttachmentsAlt = view.findViewById(R.id.swAttachmentsAlt); @@ -602,6 +604,13 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer } }); + swTextAlign.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + prefs.edit().putBoolean("text_align", checked).apply(); + } + }); + swCollapseQuotes.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { @@ -776,6 +785,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer swMonospaced.setChecked(prefs.getBoolean("monospaced", false)); swTextColor.setChecked(prefs.getBoolean("text_color", true)); swTextSize.setChecked(prefs.getBoolean("text_size", true)); + swTextAlign.setChecked(prefs.getBoolean("text_align", true)); swCollapseQuotes.setChecked(prefs.getBoolean("collapse_quotes", false)); swImagesInline.setChecked(prefs.getBoolean("inline_images", false)); swAttachmentsAlt.setChecked(prefs.getBoolean("attachments_alt", false)); diff --git a/app/src/main/java/eu/faircode/email/HtmlHelper.java b/app/src/main/java/eu/faircode/email/HtmlHelper.java index adcd0237c8..6706f0ebb3 100644 --- a/app/src/main/java/eu/faircode/email/HtmlHelper.java +++ b/app/src/main/java/eu/faircode/email/HtmlHelper.java @@ -36,6 +36,7 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; +import android.text.style.AlignmentSpan; import android.text.style.BulletSpan; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; @@ -50,6 +51,7 @@ import android.text.style.TypefaceSpan; import android.text.style.URLSpan; import android.text.style.UnderlineSpan; import android.util.Base64; +import android.view.View; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextLanguage; @@ -313,6 +315,7 @@ public class HtmlHelper { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean text_color = prefs.getBoolean("text_color", true); boolean text_size = prefs.getBoolean("text_size", true); + boolean text_align = prefs.getBoolean("text_align", true); boolean display_hidden = prefs.getBoolean("display_hidden", false); boolean disable_tracking = prefs.getBoolean("disable_tracking", true); boolean parse_classes = prefs.getBoolean("parse_classes", false); @@ -449,6 +452,8 @@ public class HtmlHelper { .addProtocols("a", "href", "full"); if (text_color) whitelist.addAttributes("font", "color"); + if (text_align) + whitelist.addTags("center").addAttributes(":all", "align"); if (!view) whitelist.addProtocols("img", "src", "content"); @@ -506,6 +511,19 @@ public class HtmlHelper { // Element style style = mergeStyles(style, element.attr("style")); + if (text_align) { + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/center + if ("center".equals(element.tagName())) { + style = mergeStyles(style, "text-align:center"); + element.tagName("div"); + } + + // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes + String align = element.attr("align"); + if (!TextUtils.isEmpty(align)) + style = mergeStyles(style, "text-align:" + align); + } + // Process style if (!TextUtils.isEmpty(style)) { StringBuilder sb = new StringBuilder(); @@ -637,6 +655,12 @@ public class HtmlHelper { element.attr("line-after", "true"); } break; + + case "text-align": + // https://developer.mozilla.org/en-US/docs/Web/CSS/text-align + if (text_align) + sb.append(key).append(':').append(value).append(';'); + break; } } } @@ -1721,6 +1745,7 @@ public class HtmlHelper { final int dp3 = Helper.dp2pixels(context, 3); final int dp6 = Helper.dp2pixels(context, 6); final int dp24 = Helper.dp2pixels(context, 24); + final boolean ltr = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_LTR); // https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements NodeTraversor.traverse(new NodeVisitor() { @@ -1871,6 +1896,22 @@ public class HtmlHelper { if ("line-through".equals(value)) ssb.setSpan(new StrikethroughSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break; + case "text-align": + Layout.Alignment alignment = null; + switch (value) { + case "left": + alignment = (ltr ? Layout.Alignment.ALIGN_NORMAL : Layout.Alignment.ALIGN_OPPOSITE); + break; + case "center": + alignment = Layout.Alignment.ALIGN_CENTER; + break; + case "right": + alignment = (ltr ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL); + break; + } + if (alignment != null) + ssb.setSpan(new AlignmentSpan.Standard(alignment), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + break; } } } diff --git a/app/src/main/res/layout/fragment_options_display.xml b/app/src/main/res/layout/fragment_options_display.xml index 4f72728925..5785776f48 100644 --- a/app/src/main/res/layout/fragment_options_display.xml +++ b/app/src/main/res/layout/fragment_options_display.xml @@ -826,6 +826,18 @@ app:layout_constraintTop_toBottomOf="@id/swTextColor" app:switchPadding="12dp" /> + + Use monospaced font for message text Show text colors Show small and large texts + Show left/center/right aligned texts Collapse quoted text Automatically show inline images Show relative conversation position with a dot