From de8ad55e5865f05f87cbf3e33d94dc9e0ba3524b Mon Sep 17 00:00:00 2001 From: M66B Date: Thu, 6 May 2021 07:58:04 +0200 Subject: [PATCH] Style in place --- .../java/eu/faircode/email/StyleHelper.java | 219 +++++++++--------- 1 file changed, 108 insertions(+), 111 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/StyleHelper.java b/app/src/main/java/eu/faircode/email/StyleHelper.java index 19a390b8e7..cf814e9217 100644 --- a/app/src/main/java/eu/faircode/email/StyleHelper.java +++ b/app/src/main/java/eu/faircode/email/StyleHelper.java @@ -66,57 +66,55 @@ public class StyleHelper { Log.i("Style action=" + action); try { - int start = etBody.getSelectionStart(); - int end = etBody.getSelectionEnd(); + int _start = etBody.getSelectionStart(); + int _end = etBody.getSelectionEnd(); - if (start < 0) - start = 0; - if (end < 0) - end = 0; + if (_start < 0) + _start = 0; + if (_end < 0) + _end = 0; - if (start > end) { - int tmp = start; - start = end; - end = tmp; + if (_start > _end) { + int tmp = _start; + _start = _end; + _end = tmp; } - SpannableString ss = new SpannableString(etBody.getText()); + final Editable edit = etBody.getText(); + final int start = _start; + final int end = _end; if (action == R.id.menu_bold || action == R.id.menu_italic) { int style = (action == R.id.menu_bold ? Typeface.BOLD : Typeface.ITALIC); boolean has = false; - for (StyleSpan span : ss.getSpans(start, end, StyleSpan.class)) + for (StyleSpan span : edit.getSpans(start, end, StyleSpan.class)) if (span.getStyle() == style) { has = true; - ss.removeSpan(span); + edit.removeSpan(span); } if (!has) - ss.setSpan(new StyleSpan(style), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new StyleSpan(style), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(ss); + etBody.setText(edit); etBody.setSelection(start, end); return true; } else if (action == R.id.menu_underline) { boolean has = false; - for (UnderlineSpan span : ss.getSpans(start, end, UnderlineSpan.class)) { + for (UnderlineSpan span : edit.getSpans(start, end, UnderlineSpan.class)) { has = true; - ss.removeSpan(span); + edit.removeSpan(span); } if (!has) - ss.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(ss); + etBody.setText(edit); etBody.setSelection(start, end); return true; } else if (action == R.id.menu_style) { - final int s = start; - final int e = end; - final SpannableStringBuilder t = new SpannableStringBuilder(ss); - PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(anchor.getContext(), owner, anchor); popupMenu.inflate(R.menu.popup_style); @@ -156,9 +154,9 @@ public class StyleHelper { } private boolean setSize(MenuItem item) { - RelativeSizeSpan[] spans = t.getSpans(s, e, RelativeSizeSpan.class); + RelativeSizeSpan[] spans = edit.getSpans(start, end, RelativeSizeSpan.class); for (RelativeSizeSpan span : spans) - t.removeSpan(span); + edit.removeSpan(span); Float size; if (item.getItemId() == R.id.menu_style_size_small) @@ -169,10 +167,10 @@ public class StyleHelper { size = null; if (size != null) - t.setSpan(new RelativeSizeSpan(size), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new RelativeSizeSpan(size), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(t); - etBody.setSelection(s, e); + etBody.setText(edit); + etBody.setSelection(start, end); return true; } @@ -210,24 +208,24 @@ public class StyleHelper { } private void _setColor(Integer color) { - for (ForegroundColorSpan span : t.getSpans(s, e, ForegroundColorSpan.class)) - t.removeSpan(span); + for (ForegroundColorSpan span : edit.getSpans(start, end, ForegroundColorSpan.class)) + edit.removeSpan(span); if (color != null) - t.setSpan(new ForegroundColorSpan(color), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(t); - etBody.setSelection(s, e); + etBody.setText(edit); + etBody.setSelection(start, end); } private boolean setAlignment(MenuItem item) { - Pair paragraph = ensureParagraph(t, s, e); - int start = paragraph.first; - int end = paragraph.second; + Pair paragraph = ensureParagraph(edit, start, end); + int s = paragraph.first; + int e = paragraph.second; - AlignmentSpan[] spans = t.getSpans(start, end, AlignmentSpan.class); + AlignmentSpan[] spans = edit.getSpans(s, e, AlignmentSpan.class); for (AlignmentSpan span : spans) - t.removeSpan(span); + edit.removeSpan(span); Layout.Alignment alignment = null; boolean ltr = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_LTR); @@ -241,11 +239,11 @@ public class StyleHelper { } if (alignment != null) - t.setSpan(new AlignmentSpan.Standard(alignment), - start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); + edit.setSpan(new AlignmentSpan.Standard(alignment), + s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); - etBody.setText(t); - etBody.setSelection(start, end); + etBody.setText(edit); + etBody.setSelection(s, e); return true; } @@ -261,54 +259,54 @@ public class StyleHelper { int message_zoom = prefs.getInt("message_zoom", 100); float textSize = Helper.getTextSize(context, 0) * message_zoom / 100f; - Pair paragraph = ensureParagraph(t, s, e); - int start = paragraph.first; - int end = paragraph.second; + Pair paragraph = ensureParagraph(edit, start, end); + int s = paragraph.first; + int e = paragraph.second; // Remove existing bullets - BulletSpan[] spans = t.getSpans(start, end, BulletSpan.class); + BulletSpan[] spans = edit.getSpans(s, e, BulletSpan.class); for (BulletSpan span : spans) - t.removeSpan(span); + edit.removeSpan(span); - int i = start; - int j = start + 1; + int i = s; + int j = s + 1; int index = 1; - while (j < end) { - if (i > 0 && t.charAt(i - 1) == '\n' && t.charAt(j) == '\n') { - Log.i("Insert " + i + "..." + (j + 1) + " size=" + end); + while (j < e) { + if (i > 0 && edit.charAt(i - 1) == '\n' && edit.charAt(j) == '\n') { + Log.i("Insert " + i + "..." + (j + 1) + " size=" + e); if (item.getItemId() == R.id.menu_style_list_bullets) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) - t.setSpan(new BulletSpan(dp6, colorAccent), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); + edit.setSpan(new BulletSpan(dp6, colorAccent), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); else - t.setSpan(new BulletSpan(dp6, colorAccent, dp3), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); + edit.setSpan(new BulletSpan(dp6, colorAccent, dp3), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); else - t.setSpan(new NumberSpan(dp6, colorAccent, textSize, index++), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); + edit.setSpan(new NumberSpan(dp6, colorAccent, textSize, index++), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); i = j + 1; } j++; } - etBody.setText(t); - etBody.setSelection(start, end); + etBody.setText(edit); + etBody.setSelection(s, e); return true; } private boolean setFont(MenuItem item) { - TypefaceSpan[] spans = t.getSpans(s, e, TypefaceSpan.class); + TypefaceSpan[] spans = edit.getSpans(start, end, TypefaceSpan.class); for (TypefaceSpan span : spans) - t.removeSpan(span); + edit.removeSpan(span); int id = item.getItemId(); String[] names = anchor.getResources().getStringArray(R.array.fontNameValues); String face = (id < names.length ? names[id] : null); if (face != null) - t.setSpan(new TypefaceSpan(face), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new TypefaceSpan(face), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(t); - etBody.setSelection(s, e); + etBody.setText(edit); + etBody.setSelection(start, end); return true; } @@ -320,66 +318,65 @@ public class StyleHelper { int dp3 = Helper.dp2pixels(context, 3); int dp6 = Helper.dp2pixels(context, 6); - Pair paragraph = ensureParagraph(t, s, e); - int start = paragraph.first; - int end = paragraph.second; + Pair paragraph = ensureParagraph(edit, start, end); + int s = paragraph.first; + int e = paragraph.second; - QuoteSpan[] spans = t.getSpans(s, e, QuoteSpan.class); + QuoteSpan[] spans = edit.getSpans(s, e, QuoteSpan.class); for (QuoteSpan span : spans) - t.removeSpan(span); + edit.removeSpan(span); QuoteSpan q; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) q = new QuoteSpan(colorPrimary); else q = new QuoteSpan(colorPrimary, dp3, dp6); - t.setSpan(q, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + edit.setSpan(q, s, e, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - etBody.setText(t); - etBody.setSelection(start, end); + etBody.setText(edit); + etBody.setSelection(s, e); return true; } private boolean setStrikeThrough(MenuItem item) { boolean has = false; - for (StrikethroughSpan span : t.getSpans(s, e, StrikethroughSpan.class)) { + for (StrikethroughSpan span : edit.getSpans(start, end, StrikethroughSpan.class)) { has = true; - t.removeSpan(span); + edit.removeSpan(span); } if (!has) - t.setSpan(new StrikethroughSpan(), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(t); - etBody.setSelection(s, e); + etBody.setText(edit); + etBody.setSelection(start, end); return true; } private boolean clear(MenuItem item) { - int start = s; - int end = e; + int e = end; // Expand to paragraph (block quotes) - if (end + 1 < t.length() && t.charAt(end) == '\n') - end++; + if (e + 1 < edit.length() && edit.charAt(e) == '\n') + e++; - for (Object span : t.getSpans(start, end, Object.class)) + for (Object span : edit.getSpans(start, e, Object.class)) if (!(span instanceof ImageSpan)) { - int sstart = t.getSpanStart(span); - int send = t.getSpanEnd(span); - int flags = t.getSpanFlags(span); + int sstart = edit.getSpanStart(span); + int send = edit.getSpanEnd(span); + int flags = edit.getSpanFlags(span); if (sstart < start && send > start) - setSpan(t, span, sstart, start, flags, etBody.getContext()); + setSpan(edit, span, sstart, start, flags, etBody.getContext()); if (sstart < end && send > end) - setSpan(t, span, end, send, flags, etBody.getContext()); + setSpan(edit, span, e, send, flags, etBody.getContext()); - t.removeSpan(span); + edit.removeSpan(span); } - etBody.setText(t); - etBody.setSelection(s, e); + etBody.setText(edit); + etBody.setSelection(start, e); return true; } @@ -392,36 +389,36 @@ public class StyleHelper { String url = (String) args[0]; List spans = new ArrayList<>(); - for (Object span : ss.getSpans(start, end, Object.class)) { + for (Object span : edit.getSpans(start, end, Object.class)) { if (!(span instanceof URLSpan)) spans.add(span); - ss.removeSpan(span); + edit.removeSpan(span); } if (url != null) { + int e = end; if (start == end) { etBody.getText().insert(start, url); - end += url.length(); - ss = new SpannableString(etBody.getText()); + e += url.length(); } - ss.setSpan(new URLSpan(url), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(new URLSpan(url), start, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } // Restore other spans for (Object span : spans) - ss.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + edit.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - etBody.setText(ss); + etBody.setText(edit); etBody.setSelection(end, end); return true; } else if (action == R.id.menu_clear) { - for (Object span : ss.getSpans(0, etBody.length(), Object.class)) + for (Object span : edit.getSpans(0, etBody.length(), Object.class)) if (!(span instanceof ImageSpan)) - ss.removeSpan(span); + edit.removeSpan(span); - etBody.setText(ss); + etBody.setText(edit); etBody.setSelection(start, end); return true; @@ -433,26 +430,26 @@ public class StyleHelper { } } - static void setSpan(SpannableStringBuilder ss, Object span, int start, int end, int flags, Context context) { + static void setSpan(Editable edit, Object span, int start, int end, int flags, Context context) { if (span instanceof CharacterStyle) - ss.setSpan(CharacterStyle.wrap((CharacterStyle) span), start, end, flags); + edit.setSpan(CharacterStyle.wrap((CharacterStyle) span), start, end, flags); else if (span instanceof QuoteSpan) { ParagraphStyle ps = (ParagraphStyle) span; - Pair p = ensureParagraph(ss, start, end); - ss.setSpan(clone(span, ps.getClass(), context), p.first, p.second, flags); + Pair p = ensureParagraph(edit, start, end); + edit.setSpan(clone(span, ps.getClass(), context), p.first, p.second, flags); } } - static private Pair ensureParagraph(SpannableStringBuilder t, int s, int e) { + static private Pair ensureParagraph(Editable edit, int s, int e) { int start = s; int end = e; // Expand selection at start - while (start > 0 && t.charAt(start - 1) != '\n') + while (start > 0 && edit.charAt(start - 1) != '\n') start--; // Expand selection at end - while (end > 0 && end < t.length() && t.charAt(end - 1) != '\n') + while (end > 0 && end < edit.length() && edit.charAt(end - 1) != '\n') end++; // Nothing to do @@ -460,20 +457,20 @@ public class StyleHelper { return null; // Create paragraph at start - if (start == 0 && t.charAt(start) != '\n') { - t.insert(0, "\n"); + if (start == 0 && edit.charAt(start) != '\n') { + edit.insert(0, "\n"); start++; end++; } // Create paragraph at end - if (end == t.length() && t.charAt(end - 1) != '\n') { - t.append("\n"); + if (end == edit.length() && edit.charAt(end - 1) != '\n') { + edit.append("\n"); end++; } - if (end == t.length()) - t.append("\n"); // workaround Android bug + if (end == edit.length()) + edit.append("\n"); // workaround Android bug return new Pair(start, end); }