diff --git a/app/src/main/java/eu/faircode/email/EditTextCompose.java b/app/src/main/java/eu/faircode/email/EditTextCompose.java index 63a60295ce..acde3d5c62 100644 --- a/app/src/main/java/eu/faircode/email/EditTextCompose.java +++ b/app/src/main/java/eu/faircode/email/EditTextCompose.java @@ -114,6 +114,8 @@ public class EditTextCompose extends FixedEditText { order++, context.getString(R.string.title_insert_brackets)); menu.add(Menu.CATEGORY_SECONDARY, R.string.title_insert_quotes, order++, context.getString(R.string.title_insert_quotes)); + menu.add(Menu.CATEGORY_SECONDARY, R.string.title_setup_help, + order++, context.getString(R.string.title_setup_help)); menu.add(Menu.CATEGORY_SECONDARY, R.string.title_lt_add, order++, context.getString(R.string.title_lt_add)); menu.add(Menu.CATEGORY_SECONDARY, R.string.title_lt_delete, @@ -131,6 +133,10 @@ public class EditTextCompose extends FixedEditText { boolean selection = (start >= 0 && start < end); Context context = getContext(); Editable edit = getText(); + SuggestionSpanEx[] suggestion = (edit == null ? null + : edit.getSpans(start, end, SuggestionSpanEx.class)); + boolean hasSuggestions = (suggestion != null && suggestion.length > 0 && + !TextUtils.isEmpty(suggestion[0].getDescription())); boolean dictionary = (selection && context instanceof AppCompatActivity && LanguageTool.isPremium(context) && @@ -138,6 +144,7 @@ public class EditTextCompose extends FixedEditText { edit.subSequence(start, end).toString().indexOf(' ') < 0); menu.findItem(R.string.title_insert_brackets).setVisible(selection); menu.findItem(R.string.title_insert_quotes).setVisible(selection); + menu.findItem(R.string.title_setup_help).setVisible(hasSuggestions); menu.findItem(R.string.title_lt_add).setVisible(dictionary); menu.findItem(R.string.title_lt_delete).setVisible(dictionary); return false; @@ -151,7 +158,12 @@ public class EditTextCompose extends FixedEditText { return surround("(", ")"); else if (id == R.string.title_insert_quotes) return surround("\"", "\""); - else if (id == R.string.title_lt_add) + else if (id == R.string.title_setup_help) { + if (showSuggestion()) { + mode.finish(); + return true; + } + } else if (id == R.string.title_lt_add) return modifyDictionary(true); else if (id == R.string.title_lt_delete) return modifyDictionary(false); @@ -228,6 +240,21 @@ public class EditTextCompose extends FixedEditText { return true; } + + private boolean showSuggestion() { + Editable edit = getText(); + if (edit == null) + return false; + int start = getSelectionStart(); + int end = getSelectionEnd(); + if (end <= start) + return false; + SuggestionSpanEx[] suggestions = edit.getSpans(start, end, SuggestionSpanEx.class); + if (suggestions == null || suggestions.length == 0) + return false; + ToastEx.makeText(getContext(), suggestions[0].getDescription(), Toast.LENGTH_LONG).show(); + return true; + } }); setCustomInsertionActionModeCallback(new ActionMode.Callback() { diff --git a/app/src/main/java/eu/faircode/email/LanguageTool.java b/app/src/main/java/eu/faircode/email/LanguageTool.java index ce11be8bed..d5a188501f 100644 --- a/app/src/main/java/eu/faircode/email/LanguageTool.java +++ b/app/src/main/java/eu/faircode/email/LanguageTool.java @@ -22,13 +22,12 @@ package eu.faircode.email; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; -import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.LocaleList; import android.text.Editable; +import android.text.SpannableStringBuilder; import android.text.Spanned; -import android.text.TextPaint; import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.util.Pair; @@ -43,7 +42,6 @@ import org.json.JSONObject; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; @@ -425,7 +423,24 @@ public class LanguageTool { boolean misspelled = ("misspelling".equals(suggestion.issueType) || "typographical".equals(suggestion.issueType) || "whitespace".equals(suggestion.issueType)); + SpannableStringBuilder ssb = new SpannableStringBuilderEx(); + if (!TextUtils.isEmpty(suggestion.title)) + ssb.append(suggestion.title); + if (!TextUtils.isEmpty(suggestion.description)) { + if (ssb.length() > 0) + ssb.append(": "); + ssb.append(suggestion.description); + } + if (!TextUtils.isEmpty(suggestion.issueType)) { + int len = ssb.length(); + if (len > 0) + ssb.append(" ("); + ssb.append(suggestion.issueType); + if (len > 0) + ssb.append(")"); + } SuggestionSpan span = new SuggestionSpanEx(etBody.getContext(), + ssb.toString(), suggestion.replacements.toArray(new String[0]), misspelled); int s = start + suggestion.offset; int e = s + suggestion.length; @@ -474,40 +489,4 @@ public class LanguageTool { return issueType + " " + title + " " + description; } } - - private static class SuggestionSpanEx extends SuggestionSpan { - private final int highlightColor; - private final int underlineColor; - private final int underlineThickness; - - public SuggestionSpanEx(Context context, String[] suggestions, boolean misspelled) { - super(context, suggestions, - misspelled || Build.VERSION.SDK_INT < Build.VERSION_CODES.S - ? SuggestionSpan.FLAG_MISSPELLED | SuggestionSpan.FLAG_EASY_CORRECT - : SuggestionSpan.FLAG_GRAMMAR_ERROR); - highlightColor = Helper.resolveColor(context, android.R.attr.textColorHighlight); - underlineColor = (misspelled ? Color.RED : highlightColor); - underlineThickness = Helper.dp2pixels(context, misspelled ? 2 : 2); - } - - @Override - public void updateDrawState(TextPaint tp) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) - try { - Field fUnderlineColor = tp.getClass().getDeclaredField("underlineColor"); - Field fUnderlineThickness = tp.getClass().getDeclaredField("underlineThickness"); - fUnderlineColor.setAccessible(true); - fUnderlineThickness.setAccessible(true); - fUnderlineColor.set(tp, underlineColor); - fUnderlineThickness.set(tp, underlineThickness); - } catch (Throwable ex) { - Log.i(ex); - tp.bgColor = highlightColor; - } - else { - tp.underlineColor = underlineColor; - tp.underlineThickness = underlineThickness; - } - } - } } diff --git a/app/src/main/java/eu/faircode/email/SuggestionSpanEx.java b/app/src/main/java/eu/faircode/email/SuggestionSpanEx.java new file mode 100644 index 0000000000..3081c6f724 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/SuggestionSpanEx.java @@ -0,0 +1,70 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.text.TextPaint; +import android.text.style.SuggestionSpan; + +import java.lang.reflect.Field; + +public class SuggestionSpanEx extends SuggestionSpan { + private String description; + private final int highlightColor; + private final int underlineColor; + private final int underlineThickness; + + public SuggestionSpanEx(Context context, String description, String[] suggestions, boolean misspelled) { + super(context, suggestions, + misspelled || Build.VERSION.SDK_INT < Build.VERSION_CODES.S + ? SuggestionSpan.FLAG_MISSPELLED | SuggestionSpan.FLAG_EASY_CORRECT + : SuggestionSpan.FLAG_GRAMMAR_ERROR); + this.description = description; + highlightColor = Helper.resolveColor(context, android.R.attr.textColorHighlight); + underlineColor = (misspelled ? Color.RED : highlightColor); + underlineThickness = Helper.dp2pixels(context, misspelled ? 2 : 2); + } + + public String getDescription() { + return this.description; + } + + @Override + public void updateDrawState(TextPaint tp) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) + try { + Field fUnderlineColor = tp.getClass().getDeclaredField("underlineColor"); + Field fUnderlineThickness = tp.getClass().getDeclaredField("underlineThickness"); + fUnderlineColor.setAccessible(true); + fUnderlineThickness.setAccessible(true); + fUnderlineColor.set(tp, underlineColor); + fUnderlineThickness.set(tp, underlineThickness); + } catch (Throwable ex) { + Log.i(ex); + tp.bgColor = highlightColor; + } + else { + tp.underlineColor = underlineColor; + tp.underlineThickness = underlineThickness; + } + } +} \ No newline at end of file