DeepL integration

This commit is contained in:
M66B 2021-05-17 20:53:24 +02:00
parent 7da2935408
commit ffd197db8b
3 changed files with 150 additions and 0 deletions

View File

@ -142,6 +142,8 @@ import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
@ -162,6 +164,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
@ -205,6 +209,7 @@ import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.internet.ParseException;
import javax.mail.util.ByteArrayDataSource;
import javax.net.ssl.HttpsURLConnection;
import biweekly.Biweekly;
import biweekly.ICalendar;
@ -310,6 +315,8 @@ public class FragmentCompose extends FragmentBase {
private static final int REQUEST_SEND = 14;
private static final int REQUEST_PERMISSION = 15;
private static final int DEEPL_TIMEOUT = 20; // seconds
private static ExecutorService executor = Helper.getBackgroundExecutor(1, "encrypt");
@Override
@ -1452,6 +1459,8 @@ 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(etBody.hasSelection() && BuildConfig.DEBUG);
menu.findItem(R.id.menu_clear).setEnabled(state == State.LOADED);
int colorEncrypt = Helper.resolveColor(getContext(), R.attr.colorEncrypt);
@ -1481,12 +1490,18 @@ public class FragmentCompose extends FragmentBase {
boolean save_drafts = prefs.getBoolean("save_drafts", true);
boolean send_dialog = prefs.getBoolean("send_dialog", true);
boolean image_dialog = prefs.getBoolean("image_dialog", true);
String deepl = prefs.getString("deepl", null);
menu.findItem(R.id.menu_save_drafts).setChecked(save_drafts);
menu.findItem(R.id.menu_send_dialog).setChecked(send_dialog);
menu.findItem(R.id.menu_image_dialog).setChecked(image_dialog);
menu.findItem(R.id.menu_media).setChecked(media);
menu.findItem(R.id.menu_compact).setChecked(compact);
menu.findItem(R.id.menu_translate_english).setEnabled(deepl != null);
menu.findItem(R.id.menu_translate_french).setEnabled(deepl != null);
menu.findItem(R.id.menu_translate_german).setEnabled(deepl != null);
menu.findItem(R.id.menu_translate_italian).setEnabled(deepl != null);
menu.findItem(R.id.menu_translate_spanish).setEnabled(deepl != null);
if (EntityMessage.PGP_SIGNONLY.equals(encrypt) ||
EntityMessage.SMIME_SIGNONLY.equals(encrypt))
@ -1541,6 +1556,24 @@ public class FragmentCompose extends FragmentBase {
} else if (itemId == R.id.menu_answer_create) {
onMenuAnswerCreate();
return true;
} else if (itemId == R.id.menu_translate_key) {
onMenuTranslateKey();
return true;
} else if (itemId == R.id.menu_translate_english) {
onMenuTranslate("EN");
return true;
} else if (itemId == R.id.menu_translate_french) {
onMenuTranslate("FR");
return true;
} else if (itemId == R.id.menu_translate_german) {
onMenuTranslate("DE");
return true;
} else if (itemId == R.id.menu_translate_italian) {
onMenuTranslate("IT");
return true;
} else if (itemId == R.id.menu_translate_spanish) {
onMenuTranslate("ES");
return true;
} else if (itemId == R.id.menu_clear) {
StyleHelper.apply(R.id.menu_clear, getViewLifecycleOwner(), null, etBody);
return true;
@ -1905,6 +1938,87 @@ public class FragmentCompose extends FragmentBase {
fragmentTransaction.commit();
}
private void onMenuTranslateKey() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
prefs.edit().putString("deepl", "").apply();
}
private void onMenuTranslate(String target) {
int start = etBody.getSelectionStart();
int end = etBody.getSelectionEnd();
Editable edit = etBody.getText();
if (start > end) {
int tmp = start;
start = end;
end = tmp;
}
while (start > 0 && edit.charAt(start - 1) != '\n')
start--;
// Expand selection at end
while (end > 0 && end < edit.length() && edit.charAt(end - 1) != '\n')
end++;
final int insert = end;
String text = edit.subSequence(start, end).toString();
Bundle args = new Bundle();
args.putString("target", target);
args.putString("text", text);
new SimpleTask<String>() {
@Override
protected String onExecute(Context context, Bundle args) throws Throwable {
String target = args.getString("target");
String text = args.getString("text");
String request =
"text=" + URLEncoder.encode(text, StandardCharsets.UTF_8.name()) +
"&target_lang=" + URLEncoder.encode(target, StandardCharsets.UTF_8.name());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String deepl = prefs.getString("deepl", null);
URL url = new URL("https://api-free.deepl.com/v2/translate?auth_key=" + deepl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setReadTimeout(DEEPL_TIMEOUT * 1000);
connection.setConnectTimeout(DEEPL_TIMEOUT * 1000);
connection.setRequestProperty("User-Agent", WebViewEx.getUserAgent(context));
connection.setRequestProperty("Accept", "*/*");
connection.setRequestProperty("Content-Length", Integer.toString(request.length()));
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.connect();
try {
connection.getOutputStream().write(request.getBytes());
String response = Helper.readStream(connection.getInputStream());
JSONObject jroot = new JSONObject(response);
JSONArray jtranslations = jroot.getJSONArray("translations");
JSONObject jtranslation = (JSONObject) jtranslations.get(0);
return jtranslation.getString("text");
} finally {
connection.disconnect();
}
}
@Override
protected void onExecuted(Bundle args, String translated) {
edit.insert(insert, "\n" + translated);
etBody.setSelection(insert + 1 + translated.length());
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(getParentFragmentManager(), ex);
}
}.execute(this, args, "compose:translate");
}
private boolean onActionStyle(int action, View anchor) {
Log.i("Style action=" + action);
return StyleHelper.apply(action, getViewLifecycleOwner(), anchor, etBody);

View File

@ -66,6 +66,35 @@
android:title="@string/title_create_template"
app:showAsAction="never" />
<item
android:id="@+id/menu_translate"
android:title="@string/title_translate"
app:showAsAction="never">
<menu>
<group>
<item
android:id="@+id/menu_translate_key"
android:title="@string/title_translate_key" />
<item
android:id="@+id/menu_translate_english"
android:title="@string/title_translate_english" />
<item
android:id="@+id/menu_translate_french"
android:title="@string/title_translate_french" />
<item
android:id="@+id/menu_translate_german"
android:title="@string/title_translate_german" />
<item
android:id="@+id/menu_translate_italian"
android:title="@string/title_translate_italian" />
<item
android:id="@+id/menu_translate_spanish"
android:title="@string/title_translate_spanish" />
</group>
</menu>
</item>
<item
android:id="@+id/menu_clear"
android:title="@string/title_style_clear"

View File

@ -1107,6 +1107,13 @@
<string name="title_insert_contact_group">Insert contact group</string>
<string name="title_insert_template">Insert template</string>
<string name="title_create_template">Create template</string>
<string name="title_translate" translatable="false">Translate</string>
<string name="title_translate_key" translatable="false">Set key</string>
<string name="title_translate_english" translatable="false">English</string>
<string name="title_translate_french" translatable="false">French</string>
<string name="title_translate_german" translatable="false">German</string>
<string name="title_translate_italian" translatable="false">Italian</string>
<string name="title_translate_spanish" translatable="false">Spanish</string>
<string name="title_edit_plain_text">Edit as plain text</string>
<string name="title_edit_formatted_text">Edit as reformatted text</string>
<string name="title_select_certificate">Select public key</string>