diff --git a/CHANGELOG.md b/CHANGELOG.md index e3d88ba4c7..f8c585ff18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Added slider to change message column width * Added option for formal/informal DeepL translation +* Added insert line to long press menu * Small improvements and minor bug fixes ### [Epidexipteryx](https://en.wikipedia.org/wiki/Epidexipteryx) diff --git a/app/src/main/assets/CHANGELOG.md b/app/src/main/assets/CHANGELOG.md index e3d88ba4c7..f8c585ff18 100644 --- a/app/src/main/assets/CHANGELOG.md +++ b/app/src/main/assets/CHANGELOG.md @@ -8,6 +8,7 @@ * Added slider to change message column width * Added option for formal/informal DeepL translation +* Added insert line to long press menu * Small improvements and minor bug fixes ### [Epidexipteryx](https://en.wikipedia.org/wiki/Epidexipteryx) diff --git a/app/src/main/java/eu/faircode/email/EditTextCompose.java b/app/src/main/java/eu/faircode/email/EditTextCompose.java index 92090617d0..5754858e24 100644 --- a/app/src/main/java/eu/faircode/email/EditTextCompose.java +++ b/app/src/main/java/eu/faircode/email/EditTextCompose.java @@ -30,6 +30,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.Editable; import android.text.Html; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -43,6 +44,7 @@ import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.widget.Toast; import androidx.core.view.inputmethod.EditorInfoCompat; import androidx.core.view.inputmethod.InputConnectionCompat; @@ -95,6 +97,7 @@ public class EditTextCompose extends FixedEditText { menu.add(Menu.CATEGORY_ALTERNATIVE, R.string.title_undo, 1, getTitle(R.string.title_undo)); if (can(android.R.id.redo)) menu.add(Menu.CATEGORY_ALTERNATIVE, R.string.title_redo, 2, getTitle(R.string.title_redo)); + menu.add(Menu.CATEGORY_ALTERNATIVE, R.string.title_insert_line, 3, R.string.title_insert_line); return true; } @@ -117,6 +120,8 @@ public class EditTextCompose extends FixedEditText { return EditTextCompose.super.onTextContextMenuItem(android.R.id.undo); else if (id == R.string.title_redo) return EditTextCompose.super.onTextContextMenuItem(android.R.id.redo); + else if (id == R.string.title_insert_line) + return insertLine(); } return false; } @@ -125,6 +130,40 @@ public class EditTextCompose extends FixedEditText { public void onDestroyActionMode(ActionMode mode) { // Do nothing } + + private boolean insertLine() { + try { + int start = getSelectionStart(); + if (start < 0) + return false; + + Editable edit = getText(); + if (edit == null) + return false; + + if (start == 0 || edit.charAt(start - 1) != '\n') + edit.insert(start++, "\n"); + if (start == edit.length() || edit.charAt(start) != '\n') + edit.insert(start, "\n"); + + edit.insert(start, "\uFFFC"); // Object replacement character + + int colorSeparator = Helper.resolveColor(getContext(), R.attr.colorSeparator); + float stroke = context.getResources().getDisplayMetrics().density; + edit.setSpan( + new LineSpan(colorSeparator, stroke, 0f), + start, start + 1, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + setSelection(start + 2); + + return true; + } catch (Throwable ex) { + Log.e(ex); + ToastEx.makeText(context, Log.formatThrowable(ex), Toast.LENGTH_LONG).show(); + return false; + } + } }); } } diff --git a/app/src/main/java/eu/faircode/email/HtmlEx.java b/app/src/main/java/eu/faircode/email/HtmlEx.java index 463a182193..e837f185b1 100644 --- a/app/src/main/java/eu/faircode/email/HtmlEx.java +++ b/app/src/main/java/eu/faircode/email/HtmlEx.java @@ -252,6 +252,13 @@ public class HtmlEx { if (i != text.length()) out.append("
\n"); } else { + eu.faircode.email.LineSpan[] line = text.getSpans(i, next, eu.faircode.email.LineSpan.class); + if (line.length > 0) { + for (int l = 0; l < line.length; l++) + out.append("
"); + continue; + } + int level = 0; Boolean isBulletListItem = null; ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class); diff --git a/app/src/main/java/eu/faircode/email/HtmlHelper.java b/app/src/main/java/eu/faircode/email/HtmlHelper.java index 42cc58ea23..d7176d8bfc 100644 --- a/app/src/main/java/eu/faircode/email/HtmlHelper.java +++ b/app/src/main/java/eu/faircode/email/HtmlHelper.java @@ -1007,11 +1007,8 @@ public class HtmlHelper { // Lines // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr - if (!view) - for (Element hr : document.select("hr")) { - hr.tagName("div"); - hr.text(LINE); - } + for (Element hr : document.select("hr")) + hr.attr("x-keep-line", "true"); // Descriptions // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl @@ -2385,6 +2382,12 @@ public class HtmlHelper { } } + for (LineSpan span : ssb.getSpans(0, ssb.length(), LineSpan.class)) { + int start = ssb.getSpanStart(span); + int end = ssb.getSpanEnd(span); + ssb.replace(start, end, LINE); + } + return ssb.toString(); } @@ -3001,32 +3004,34 @@ public class HtmlHelper { break; case "hr": // Suppress successive lines - LineSpan[] lines = ssb.getSpans(0, ssb.length(), LineSpan.class); - int last = -1; - if (lines != null) - for (LineSpan line : lines) { - int e = ssb.getSpanEnd(line); - if (e > last) - last = e; - } - if (last >= 0) { - boolean blank = true; - for (int i = last; i < ssb.length(); i++) { - char kar = ssb.charAt(i); - if (kar != ' ' && kar != '\n' && kar != '\u00a0') { - blank = false; - break; + if (!"true".equals(element.attr("x-keep-line"))) { + LineSpan[] lines = ssb.getSpans(0, ssb.length(), LineSpan.class); + int last = -1; + if (lines != null) + for (LineSpan line : lines) { + int e = ssb.getSpanEnd(line); + if (e > last) + last = e; + } + if (last >= 0) { + boolean blank = true; + for (int i = last; i < ssb.length(); i++) { + char kar = ssb.charAt(i); + if (kar != ' ' && kar != '\n' && kar != '\u00a0') { + blank = false; + break; + } } - } - if (blank) - break; + if (blank) + break; + } } boolean dashed = "true".equals(element.attr("x-dashed")); float stroke = context.getResources().getDisplayMetrics().density; float dash = (dashed ? line_dash_length : 0f); - ssb.append(LINE); + ssb.append("\uFFFC"); // Object replacement character setSpan(ssb, new LineSpan(colorSeparator, stroke, dash), start, ssb.length()); break; case "img": @@ -3264,7 +3269,8 @@ public class HtmlHelper { .removeAttr("x-tracking") .removeAttr("x-border") .removeAttr("x-list-style") - .removeAttr("x-plain"); + .removeAttr("x-plain") + .remove("x-keep-line"); } static Spanned fromHtml(@NonNull String html, Context context) { @@ -3358,6 +3364,12 @@ public class HtmlHelper { last.remove(); } + for (Element line : doc.select("hr")) { + Element next = line.nextElementSibling(); + if (next != null && "br".equals(next.tagName())) + next.remove(); + } + return doc.html(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cf89e88c93..8839ab4636 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1736,6 +1736,7 @@ Later Undo Redo + Insert line Add Open with Info diff --git a/metadata/en-US/changelogs/1830.txt b/metadata/en-US/changelogs/1830.txt index e3d88ba4c7..f8c585ff18 100644 --- a/metadata/en-US/changelogs/1830.txt +++ b/metadata/en-US/changelogs/1830.txt @@ -8,6 +8,7 @@ * Added slider to change message column width * Added option for formal/informal DeepL translation +* Added insert line to long press menu * Small improvements and minor bug fixes ### [Epidexipteryx](https://en.wikipedia.org/wiki/Epidexipteryx)