Use text watcher for answer/signature editor

This commit is contained in:
M66B 2022-12-08 19:47:18 +01:00
parent 4ddc6f2130
commit bf67eb6d38
4 changed files with 216 additions and 169 deletions

View File

@ -104,6 +104,8 @@ public class ActivitySignature extends ActivityBase {
}
});
etText.addTextChangedListener(StyleHelper.getTextWatcher(etText));
etText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -124,6 +126,8 @@ public class ActivitySignature extends ActivityBase {
}
});
StyleHelper.wire(this, view, etText);
ibFull.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -139,8 +143,6 @@ public class ActivitySignature extends ActivityBase {
}
});
StyleHelper.wire(this, view, etText);
bottom_navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {

View File

@ -158,6 +158,8 @@ public class FragmentAnswer extends FragmentBase {
}
});
etText.addTextChangedListener(StyleHelper.getTextWatcher(etText));
StyleHelper.wire(getViewLifecycleOwner(), view, etText);
bottom_navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

View File

@ -639,7 +639,8 @@ public class FragmentCompose extends FragmentBase {
}
});
// https://developer.android.com/reference/android/text/TextWatcher
etBody.addTextChangedListener(StyleHelper.getTextWatcher(etBody));
etBody.addTextChangedListener(new TextWatcher() {
private boolean save = false;
private Integer added = null;
@ -649,25 +650,7 @@ public class FragmentCompose extends FragmentBase {
@Override
public void beforeTextChanged(CharSequence text, int start, int count, int after) {
if (count == 1 && after == 0 && (start == 0 || text.charAt(start) == '\n')) {
Log.i("Removed=" + start);
removed = start;
}
if (BuildConfig.DEBUG && count - after == 1 && start + after > 0) {
int replaced = start + after;
Spanned spanned = ((Spanned) text);
StyleHelper.InsertedSpan[] spans =
spanned.getSpans(replaced, replaced, StyleHelper.InsertedSpan.class);
for (StyleHelper.InsertedSpan span : spans) {
int end = spanned.getSpanEnd(span);
Log.i("Replaced=" + replaced);
if (end - 1 == replaced) {
inserted = end - 1;
break;
}
}
}
// Do nothing
}
@Override
@ -681,6 +664,7 @@ public class FragmentCompose extends FragmentBase {
if (count - before == 1 && index > 0) {
char c = text.charAt(index);
char b = text.charAt(index - 1);
save = (auto_save_paragraph && c == '\n' && b != '\n') ||
(auto_save_dot && Helper.isDot(c) && !Helper.isDot(b));
if (save)
@ -698,119 +682,8 @@ public class FragmentCompose extends FragmentBase {
if (etBody == null)
return;
LogPrinter lp = null;
if (BuildConfig.DEBUG &&
(added != null || removed != null))
lp = new LogPrinter(android.util.Log.INFO, "FairEmail");
if (lp != null)
TextUtils.dumpSpans(text, new LogPrinter(android.util.Log.INFO, "FairEmail"), "---before>");
if (added != null)
try {
// break block quotes
boolean broken = false;
QuoteSpan[] spans = text.getSpans(added + 1, added + 1, QuoteSpan.class);
for (QuoteSpan span : spans) {
int s = text.getSpanStart(span);
int e = text.getSpanEnd(span);
int f = text.getSpanFlags(span);
Log.i(span + " " + s + "..." + e + " added=" + added);
if (s > 0 && added - s > 0 && e - (added + 1) > 0 &&
text.charAt(s - 1) == '\n' && text.charAt(added - 1) == '\n' &&
text.charAt(added) == '\n' && text.charAt(e - 1) == '\n') {
broken = true;
QuoteSpan q1 = StyleHelper.clone(span, QuoteSpan.class, etBody.getContext());
text.setSpan(q1, s, added, f);
Log.i(span + " " + s + "..." + added);
QuoteSpan q2 = StyleHelper.clone(span, QuoteSpan.class, etBody.getContext());
text.setSpan(q2, added + 1, e, f);
Log.i(span + " " + (added + 1) + "..." + e);
text.removeSpan(span);
}
}
if (broken) {
CharacterStyle[] sspan = text.getSpans(added + 1, added + 1, CharacterStyle.class);
for (CharacterStyle span : sspan) {
int s = text.getSpanStart(span);
int e = text.getSpanEnd(span);
int f = text.getSpanFlags(span);
Log.i(span + " " + s + "..." + e + " start=" + added);
if (s <= added && added + 1 <= e) {
CharacterStyle s1 = CharacterStyle.wrap(span);
text.setSpan(s1, s, added, f);
Log.i(span + " " + s + "..." + added);
CharacterStyle s2 = CharacterStyle.wrap(span);
text.setSpan(s2, added + 1, e, f);
Log.i(span + " " + (added + 1) + "..." + e);
text.removeSpan(span);
}
}
etBody.setSelection(added);
}
// Escape indent at end
IndentSpan[] indents = text.getSpans(added + 1, added + 1, IndentSpan.class);
for (IndentSpan indent : indents) {
int s = text.getSpanStart(indent);
int e = text.getSpanEnd(indent);
int f = text.getSpanFlags(indent);
if (e - 1 > s && added + 1 == e) {
text.removeSpan(indent);
text.setSpan(new IndentSpan(indent.getLeadingMargin(true)), s, e - 1, f);
}
}
boolean renum = false;
BulletSpan[] bullets = text.getSpans(added + 1, added + 1, BulletSpan.class);
int len = 0;
BulletSpan shortest = null;
for (BulletSpan span : bullets) {
int s = text.getSpanStart(span);
int e = text.getSpanEnd(span);
if (shortest == null || e - s < len) {
shortest = span;
len = e - s;
}
}
if (shortest != null) {
int s = text.getSpanStart(shortest);
int e = text.getSpanEnd(shortest);
int f = text.getSpanFlags(shortest) | Spanned.SPAN_PARAGRAPH;
Log.i(shortest + " " + s + "..." + e + " added=" + added);
if (s > 0 &&
added + 1 > s && e > added + 1 &&
text.charAt(s - 1) == '\n' && text.charAt(e - 1) == '\n') {
if (e - s > 2) {
BulletSpan b1 = StyleHelper.clone(shortest, shortest.getClass(), etBody.getContext());
text.setSpan(b1, s, added + 1, f);
Log.i(shortest + " " + s + "..." + (added + 1));
BulletSpan b2 = StyleHelper.clone(b1, shortest.getClass(), etBody.getContext());
text.setSpan(b2, added + 1, e, f);
Log.i(shortest + " " + (added + 1) + "..." + e);
}
renum = true;
text.removeSpan(shortest);
}
}
if (renum)
StyleHelper.renumber(text, false, etBody.getContext());
if (lt_auto) {
int start = added;
while (start > 0 && text.charAt(start - 1) != '\n')
@ -824,38 +697,6 @@ public class FragmentCompose extends FragmentBase {
added = null;
}
if (removed != null)
try {
ParagraphStyle[] ps = text.getSpans(removed, removed + 1, ParagraphStyle.class);
if (ps != null)
for (ParagraphStyle p : ps) {
int start = text.getSpanStart(p);
int end = text.getSpanEnd(p);
if (start >= removed && end <= removed + 1)
text.removeSpan(p);
}
StyleHelper.renumber(text, true, etBody.getContext());
} finally {
removed = null;
}
if (inserted != null)
try {
StyleHelper.InsertedSpan[] spans =
text.getSpans(inserted, inserted, StyleHelper.InsertedSpan.class);
for (StyleHelper.InsertedSpan span : spans) {
int start = text.getSpanStart(span);
int end = text.getSpanEnd(span);
if (end == inserted) {
text.delete(start, end);
text.removeSpan(span);
}
}
} finally {
inserted = null;
}
if (save)
try {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
@ -873,10 +714,6 @@ public class FragmentCompose extends FragmentBase {
} finally {
lt = null;
}
if (lp != null)
TextUtils.dumpSpans(text, lp, "---after>");
}
});

View File

@ -34,6 +34,7 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
@ -47,6 +48,7 @@ import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.util.LogPrinter;
import android.util.Pair;
import android.view.MenuItem;
import android.view.SubMenu;
@ -139,6 +141,210 @@ public class StyleHelper {
}
}
static TextWatcher getTextWatcher(EditText etBody) {
// https://developer.android.com/reference/android/text/TextWatcher
return new TextWatcher() {
private Integer added = null;
private Integer removed = null;
private Integer inserted = null;
@Override
public void beforeTextChanged(CharSequence text, int start, int count, int after) {
if (count == 1 && after == 0 && (start == 0 || text.charAt(start) == '\n')) {
Log.i("Removed=" + start);
removed = start;
}
if (BuildConfig.DEBUG && count - after == 1 && start + after > 0) {
int replaced = start + after;
Spanned spanned = ((Spanned) text);
StyleHelper.InsertedSpan[] spans =
spanned.getSpans(replaced, replaced, StyleHelper.InsertedSpan.class);
for (StyleHelper.InsertedSpan span : spans) {
int end = spanned.getSpanEnd(span);
Log.i("Replaced=" + replaced);
if (end - 1 == replaced) {
inserted = end - 1;
break;
}
}
}
}
@Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
int index = start + before;
if (count - before == 1 && index > 0) {
char c = text.charAt(index);
if (c == '\n') {
Log.i("Added=" + index);
added = index;
}
}
}
@Override
public void afterTextChanged(Editable text) {
if (etBody == null)
return;
LogPrinter lp = null;
if (BuildConfig.DEBUG &&
(added != null || removed != null))
lp = new LogPrinter(android.util.Log.INFO, "FairEmail");
if (lp != null)
TextUtils.dumpSpans(text, new LogPrinter(android.util.Log.INFO, "FairEmail"), "---before>");
if (added != null)
try {
// break block quotes
boolean broken = false;
QuoteSpan[] spans = text.getSpans(added + 1, added + 1, QuoteSpan.class);
for (QuoteSpan span : spans) {
int s = text.getSpanStart(span);
int e = text.getSpanEnd(span);
int f = text.getSpanFlags(span);
Log.i(span + " " + s + "..." + e + " added=" + added);
if (s > 0 && added - s > 0 && e - (added + 1) > 0 &&
text.charAt(s - 1) == '\n' && text.charAt(added - 1) == '\n' &&
text.charAt(added) == '\n' && text.charAt(e - 1) == '\n') {
broken = true;
QuoteSpan q1 = StyleHelper.clone(span, QuoteSpan.class, etBody.getContext());
text.setSpan(q1, s, added, f);
Log.i(span + " " + s + "..." + added);
QuoteSpan q2 = StyleHelper.clone(span, QuoteSpan.class, etBody.getContext());
text.setSpan(q2, added + 1, e, f);
Log.i(span + " " + (added + 1) + "..." + e);
text.removeSpan(span);
}
}
if (broken) {
CharacterStyle[] sspan = text.getSpans(added + 1, added + 1, CharacterStyle.class);
for (CharacterStyle span : sspan) {
int s = text.getSpanStart(span);
int e = text.getSpanEnd(span);
int f = text.getSpanFlags(span);
Log.i(span + " " + s + "..." + e + " start=" + added);
if (s <= added && added + 1 <= e) {
CharacterStyle s1 = CharacterStyle.wrap(span);
text.setSpan(s1, s, added, f);
Log.i(span + " " + s + "..." + added);
CharacterStyle s2 = CharacterStyle.wrap(span);
text.setSpan(s2, added + 1, e, f);
Log.i(span + " " + (added + 1) + "..." + e);
text.removeSpan(span);
}
}
etBody.setSelection(added);
}
// Escape indent at end
IndentSpan[] indents = text.getSpans(added + 1, added + 1, IndentSpan.class);
for (IndentSpan indent : indents) {
int s = text.getSpanStart(indent);
int e = text.getSpanEnd(indent);
int f = text.getSpanFlags(indent);
if (e - 1 > s && added + 1 == e) {
text.removeSpan(indent);
text.setSpan(new IndentSpan(indent.getLeadingMargin(true)), s, e - 1, f);
}
}
boolean renum = false;
BulletSpan[] bullets = text.getSpans(added + 1, added + 1, BulletSpan.class);
int len = 0;
BulletSpan shortest = null;
for (BulletSpan span : bullets) {
int s = text.getSpanStart(span);
int e = text.getSpanEnd(span);
if (shortest == null || e - s < len) {
shortest = span;
len = e - s;
}
}
if (shortest != null) {
int s = text.getSpanStart(shortest);
int e = text.getSpanEnd(shortest);
int f = text.getSpanFlags(shortest) | Spanned.SPAN_PARAGRAPH;
Log.i(shortest + " " + s + "..." + e + " added=" + added);
if (s > 0 &&
added + 1 > s && e > added + 1 &&
text.charAt(s - 1) == '\n' && text.charAt(e - 1) == '\n') {
if (e - s > 2) {
BulletSpan b1 = StyleHelper.clone(shortest, shortest.getClass(), etBody.getContext());
text.setSpan(b1, s, added + 1, f);
Log.i(shortest + " " + s + "..." + (added + 1));
BulletSpan b2 = StyleHelper.clone(b1, shortest.getClass(), etBody.getContext());
text.setSpan(b2, added + 1, e, f);
Log.i(shortest + " " + (added + 1) + "..." + e);
}
renum = true;
text.removeSpan(shortest);
}
}
if (renum)
StyleHelper.renumber(text, false, etBody.getContext());
} catch (Throwable ex) {
Log.e(ex);
} finally {
added = null;
}
if (removed != null)
try {
ParagraphStyle[] ps = text.getSpans(removed, removed + 1, ParagraphStyle.class);
if (ps != null)
for (ParagraphStyle p : ps) {
int start = text.getSpanStart(p);
int end = text.getSpanEnd(p);
if (start >= removed && end <= removed + 1)
text.removeSpan(p);
}
StyleHelper.renumber(text, true, etBody.getContext());
} finally {
removed = null;
}
if (inserted != null)
try {
StyleHelper.InsertedSpan[] spans =
text.getSpans(inserted, inserted, StyleHelper.InsertedSpan.class);
for (StyleHelper.InsertedSpan span : spans) {
int start = text.getSpanStart(span);
int end = text.getSpanEnd(span);
if (end == inserted) {
text.delete(start, end);
text.removeSpan(span);
}
}
} finally {
inserted = null;
}
if (lp != null)
TextUtils.dumpSpans(text, lp, "---after>");
}
};
}
static boolean apply(int groupId, int itemId, LifecycleOwner owner, View anchor, EditText etBody, Object... args) {
Log.i("Style action=" + groupId + ":" + itemId);