diff --git a/FAQ.md b/FAQ.md index 230b282a55..8ebad4e40d 100644 --- a/FAQ.md +++ b/FAQ.md @@ -3691,7 +3691,7 @@ Unfortunately, it is not possible to hide messages on the email server too. 1. [Subscribe to](https://www.deepl.com/pro#developer) the DeepL API Free or Pro plan (credit card required) 1. [Copy](https://www.deepl.com/pro-account/plan) the authentication key -1. In the message composer, enter some text, and in the overflow menu select *Translate*, *Configure* and paste the key +1. In the message composer, enter some text, tap on the faint translate button, select *Configure* and paste the key You might want to read the [privacy policy](https://www.deepl.com/privacy/) of DeepL. diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index a58be7b781..63df29d81f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -701,16 +701,13 @@ public class FragmentCompose extends FragmentBase { ibTranslate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (getParagraph() == null) - return; - - List languages = DeepL.getTargetLanguages(v.getContext()); + List languages = DeepL.getTargetLanguages(getContext()); if (languages == null) return; - boolean canTranslate = DeepL.canTranslate(v.getContext()); + boolean canTranslate = (DeepL.canTranslate(getContext()) && getParagraph() != null); - PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(v.getContext(), getViewLifecycleOwner(), v); + PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(getContext(), getViewLifecycleOwner(), v); popupMenu.getMenu().add(Menu.NONE, 1, 1, R.string.title_translate_configure); @@ -726,15 +723,124 @@ public class FragmentCompose extends FragmentBase { popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - if (item.getItemId() == 1) - onMenuTranslateConfigure(); - else - onMenuTranslate(item.getIntent().getStringExtra("target")); + if (item.getItemId() == 1) { + FragmentDialogDeepL fragment = new FragmentDialogDeepL(); + fragment.show(getParentFragmentManager(), "deepl:configure"); + } else { + String target = item.getIntent().getStringExtra("target"); + onMenuTranslate(target); + } return true; } }); - popupMenu.showWithIcons(v.getContext(), v); + popupMenu.showWithIcons(getContext(), v); + } + + private Pair getParagraph() { + int start = etBody.getSelectionStart(); + int end = etBody.getSelectionEnd(); + Editable edit = etBody.getText(); + + if (start < 0 || end < 0) + return null; + + if (start > end) { + int tmp = start; + start = end; + end = tmp; + } + + // Expand selection at start + while (start > 0 && edit.charAt(start - 1) != '\n') + start--; + + if (start == end && end < edit.length()) + end++; + + // Expand selection at end + while (end > 0 && end < edit.length() && edit.charAt(end - 1) != '\n') + end++; + + // Trim start + while (start < edit.length() - 1 && edit.charAt(start) == '\n') + start++; + + // Trim end + while (end > 0 && edit.charAt(end - 1) == '\n') + end--; + + if (start < end) + return new Pair(start, end); + + return null; + } + + private void onMenuTranslate(String target) { + final Pair paragraph = getParagraph(); + if (paragraph == null) + return; + + Editable edit = etBody.getText(); + String text = edit.subSequence(paragraph.first, paragraph.second).toString(); + + Bundle args = new Bundle(); + args.putString("target", target); + args.putString("text", text); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + ToastEx.makeText(getContext(), R.string.title_translating, Toast.LENGTH_SHORT).show(); + } + + @Override + protected String onExecute(Context context, Bundle args) throws Throwable { + String target = args.getString("target"); + String text = args.getString("text"); + return DeepL.translate(text, target, context); + } + + @Override + protected void onExecuted(Bundle args, String translated) { + if (paragraph.second > edit.length()) + return; + + FragmentActivity activity = getActivity(); + if (activity == null) + return; + + Context context = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + // Insert translated text + edit.insert(paragraph.second, "\n\n" + translated); + etBody.setSelection(paragraph.second + 2 + translated.length()); + + boolean small = prefs.getBoolean("deepl_small", false); + if (small) { + RelativeSizeSpan[] spans = edit.getSpans( + paragraph.first, paragraph.second, RelativeSizeSpan.class); + for (RelativeSizeSpan span : spans) + edit.removeSpan(span); + edit.setSpan(new RelativeSizeSpan(HtmlHelper.FONT_SMALL), + paragraph.first, paragraph.second, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + // Updated frequency + String key = "translated_" + args.getString("target"); + int count = prefs.getInt(key, 0); + prefs.edit().putInt(key, count + 1).apply(); + + activity.invalidateOptionsMenu(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex, false); + } + }.execute(FragmentCompose.this, args, "compose:translate"); } }); @@ -921,9 +1027,7 @@ public class FragmentCompose extends FragmentBase { grpAttachments.setVisibility(View.GONE); tvNoInternet.setVisibility(View.GONE); grpBody.setVisibility(View.GONE); - ibTranslate.setVisibility( - BuildConfig.DEBUG && !BuildConfig.PLAY_STORE_RELEASE - ? View.VISIBLE : View.GONE); + ibTranslate.setVisibility(BuildConfig.PLAY_STORE_RELEASE ? View.GONE : View.VISIBLE); grpSignature.setVisibility(View.GONE); grpReferenceHint.setVisibility(View.GONE); ibReferenceEdit.setVisibility(View.GONE); @@ -1485,18 +1589,6 @@ public class FragmentCompose extends FragmentBase { } }); - List languages = DeepL.getTargetLanguages(getContext()); - if (languages != null) { - SubMenu smenu = menu.findItem(R.id.menu_translate).getSubMenu(); - for (int i = 0; i < languages.size(); i++) { - DeepL.Language lang = languages.get(i); - MenuItem item = smenu.add(R.id.group_translate, i + 1, i + 1, lang.name) - .setIntent(new Intent().putExtra("target", lang.target)); - if (lang.icon != null) - item.setIcon(lang.icon); - } - } - super.onCreateOptionsMenu(menu, inflater); } @@ -1512,8 +1604,6 @@ public class FragmentCompose extends FragmentBase { state == State.LOADED && hasPermission(Manifest.permission.READ_CONTACTS)); menu.findItem(R.id.menu_answer_insert).setEnabled(state == State.LOADED); menu.findItem(R.id.menu_answer_create).setEnabled(state == State.LOADED); - menu.findItem(R.id.menu_translate).setEnabled(state == State.LOADED); - menu.findItem(R.id.menu_translate).setVisible(getParagraph() != null && !BuildConfig.PLAY_STORE_RELEASE); menu.findItem(R.id.menu_clear).setEnabled(state == State.LOADED); int colorEncrypt = Helper.resolveColor(getContext(), R.attr.colorEncrypt); @@ -1550,11 +1640,6 @@ public class FragmentCompose extends FragmentBase { menu.findItem(R.id.menu_media).setChecked(media); menu.findItem(R.id.menu_compact).setChecked(compact); - boolean canTranslate = DeepL.canTranslate(getContext()); - SubMenu smenu = menu.findItem(R.id.menu_translate).getSubMenu(); - for (int i = 1; i < smenu.size(); i++) - smenu.getItem(i).setEnabled(canTranslate); - if (EntityMessage.PGP_SIGNONLY.equals(encrypt) || EntityMessage.SMIME_SIGNONLY.equals(encrypt)) bottom_navigation.getMenu().findItem(R.id.action_send).setTitle(R.string.title_sign); @@ -1608,12 +1693,6 @@ public class FragmentCompose extends FragmentBase { } else if (itemId == R.id.menu_answer_create) { onMenuAnswerCreate(); return true; - } else if (itemId == R.id.menu_translate_configure) { - onMenuTranslateConfigure(); - return true; - } else if (item.getGroupId() == R.id.group_translate) { - onMenuTranslate(item.getIntent().getStringExtra("target")); - return true; } else if (itemId == R.id.menu_clear) { StyleHelper.apply(R.id.menu_clear, getViewLifecycleOwner(), null, etBody); return true; @@ -1983,117 +2062,6 @@ public class FragmentCompose extends FragmentBase { fragmentTransaction.commit(); } - private void onMenuTranslateConfigure() { - FragmentDialogDeepL fragment = new FragmentDialogDeepL(); - fragment.show(getParentFragmentManager(), "deepl:configure"); - } - - private Pair getParagraph() { - int start = etBody.getSelectionStart(); - int end = etBody.getSelectionEnd(); - Editable edit = etBody.getText(); - - if (start < 0 || end < 0) - return null; - - if (start > end) { - int tmp = start; - start = end; - end = tmp; - } - - // Expand selection at start - while (start > 0 && edit.charAt(start - 1) != '\n') - start--; - - if (start == end && end < edit.length()) - end++; - - // Expand selection at end - while (end > 0 && end < edit.length() && edit.charAt(end - 1) != '\n') - end++; - - // Trim start - while (start < edit.length() - 1 && edit.charAt(start) == '\n') - start++; - - // Trim end - while (end > 0 && edit.charAt(end - 1) == '\n') - end--; - - if (start < end) - return new Pair(start, end); - - return null; - } - - private void onMenuTranslate(String target) { - final Pair paragraph = getParagraph(); - if (paragraph == null) - return; - - Editable edit = etBody.getText(); - String text = edit.subSequence(paragraph.first, paragraph.second).toString(); - - Bundle args = new Bundle(); - args.putString("target", target); - args.putString("text", text); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - ToastEx.makeText(getContext(), R.string.title_translating, Toast.LENGTH_SHORT).show(); - } - - @Override - protected String onExecute(Context context, Bundle args) throws Throwable { - String target = args.getString("target"); - String text = args.getString("text"); - return DeepL.translate(text, target, context); - } - - @Override - protected void onExecuted(Bundle args, String translated) { - if (paragraph.second > edit.length()) - return; - - FragmentActivity activity = getActivity(); - if (activity == null) - return; - - Context context = getContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - // Insert translated text - edit.insert(paragraph.second, "\n\n" + translated); - etBody.setSelection(paragraph.second + 2 + translated.length()); - - boolean small = prefs.getBoolean("deepl_small", false); - if (small) { - RelativeSizeSpan[] spans = edit.getSpans( - paragraph.first, paragraph.second, RelativeSizeSpan.class); - for (RelativeSizeSpan span : spans) - edit.removeSpan(span); - edit.setSpan(new RelativeSizeSpan(HtmlHelper.FONT_SMALL), - paragraph.first, paragraph.second, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - // Updated frequency - String key = "translated_" + args.getString("target"); - int count = prefs.getInt(key, 0); - prefs.edit().putInt(key, count + 1).apply(); - - activity.invalidateOptionsMenu(); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex, false); - } - }.execute(this, args, "compose:translate"); - } - private boolean onActionStyle(int action, View anchor) { Log.i("Style action=" + action); return StyleHelper.apply(action, getViewLifecycleOwner(), anchor, etBody); diff --git a/app/src/main/res/menu/menu_compose.xml b/app/src/main/res/menu/menu_compose.xml index 6f091b8a75..c6bf86857e 100644 --- a/app/src/main/res/menu/menu_compose.xml +++ b/app/src/main/res/menu/menu_compose.xml @@ -76,20 +76,6 @@ - - - - - - - -