// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2014 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.runtime;
import android.app.Activity;
import com.google.appinventor.components.annotations.UsesPermissions;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.AsynchUtil;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
@DesignerComponent(version = YaVersion.YANDEX_COMPONENT_VERSION,
description = "Use this component to translate words and sentences between different " +
"languages. This component needs Internet access, as it will request " +
"translations to the Yandex.Translate service. Specify the source and " +
"target language in the form source-target using two letter language " +
"codes. So\"en-es\" will translate from English to Spanish while " +
"\"es-ru\" will translate from Spanish to Russian. If you leave " +
"out the source language, the service will attempt to detect the source " +
"language. So providing just \"es\" will attempt to detect the source " +
"language and translate it to Spanish.<p /> This component is powered by the " +
"Yandex translation service. See http://api.yandex.com/translate/ " +
"for more information, including the list of available languages and the " +
"meanings of the language codes and status codes. " +
"<p />Note: Translation happens asynchronously in the background. When the " +
"translation is complete, the \"GotTranslation\" event is triggered.",
category = ComponentCategory.MEDIA,
nonVisible = true,
iconName = "images/yandex.png")
@UsesPermissions(permissionNames = "android.permission.INTERNET")
@SimpleObject
public final class YandexTranslate extends AndroidNonvisibleComponent {
public static final String YANDEX_TRANSLATE_SERVICE_URL =
"https://translate.yandex.net/api/v1.5/tr.json/translate?key=";
private final String yandexKey;
private final Activity activity;
/**
* Creates a new component.
*
* @param container container, component will be placed in
*/
public YandexTranslate(ComponentContainer container) {
super(container.$form());
// Set up the Yandex.Translate Tagline in the 'About' screen
form.setYandexTranslateTagline();
// TODO (user) To provide users with this component you will need to obtain a key with the
// Yandex.Translate service at http://api.yandex.com/translate/
yandexKey = "";
activity = container.$context();
}
/**
* Performs an HTTP GET request to the Yandex.Translate service
*/
@SimpleFunction(description = "By providing a target language to translate to (for instance, " +
"'es' for Spanish, 'en' for English, or 'ru' for Russian), and a word or sentence to " +
"translate, this method will request a translation to the Yandex.Translate service.\n" +
"Once the text is translated by the external service, the event GotTranslation will be " +
"executed.\nNote: Yandex.Translate will attempt to detect the source language. You can " +
"also specify prepending it to the language translation. I.e., es-ru will specify Spanish " +
"to Russian translation.")
public void RequestTranslation(final String languageToTranslateTo,
final String textToTranslate) {
if (yandexKey.equals("")){
form.dispatchErrorOccurredEvent(YandexTranslate.this, "RequestTranslation",
ErrorMessages.ERROR_TRANSLATE_NO_KEY_FOUND);
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
@Override
public void run() {
try {
performRequest(languageToTranslateTo, textToTranslate);
} catch (IOException e) {
form.dispatchErrorOccurredEvent(YandexTranslate.this, "RequestTranslation",
ErrorMessages.ERROR_TRANSLATE_SERVICE_NOT_AVAILABLE);
} catch (JSONException je) {
form.dispatchErrorOccurredEvent(YandexTranslate.this, "RequestTranslation",
ErrorMessages.ERROR_TRANSLATE_JSON_RESPONSE);
}
}
});
}
/**
* This is the actual request to the Yandex.Translate service. It opens a https connection to
* their web based API and parses the JSON response.
* @param languageToTranslateTo the language that the text will be translated into.
* @param textToTranslate the word or sentence to translate.
* @throws IOException if the connection is not successful.
* @throws JSONException if the JSON response cannot be parsed.
*/
private void performRequest(String languageToTranslateTo, String textToTranslate)
throws IOException, JSONException {
final String finalURL = YANDEX_TRANSLATE_SERVICE_URL +
this.yandexKey +
"&lang=" + languageToTranslateTo +
"&text=" + URLEncoder.encode(textToTranslate, "UTF-8");
URL url = new URL(finalURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection != null) {
try {
final String responseContent = getResponseContent(connection);
JSONObject jsonResponse = new JSONObject(responseContent);
final String responseCode = jsonResponse.getString("code");
// The translation will be in position Zero of the array returned with key 'text'
org.json.JSONArray response = jsonResponse.getJSONArray("text");
final String translation = (String)response.get(0);
// Dispatch the event.
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
GotTranslation(responseCode, translation);
}
});
} finally {
connection.disconnect();
}
}
}
/**
* This method reads from a stream based on the passed connection
* @param connection the connection to read from
* @return the contents of the stream
* @throws IOException if it cannot read from the http connection
*/
private static String getResponseContent(HttpURLConnection connection) throws IOException {
// Use the content encoding to convert bytes to characters.
String encoding = connection.getContentEncoding();
if (encoding == null) {
encoding = "UTF-8";
}
InputStreamReader reader = new InputStreamReader(connection.getInputStream(), encoding);
try {
int contentLength = connection.getContentLength();
StringBuilder sb = (contentLength != -1)
? new StringBuilder(contentLength)
: new StringBuilder();
char[] buf = new char[1024];
int read;
while ((read = reader.read(buf)) != -1) {
sb.append(buf, 0, read);
}
return sb.toString();
} finally {
reader.close();
}
}
/**
* Event indicating that a request has finished and has returned data (translation).
*
* @param responseCode the response code from the server
* @param translation the response content from the server
*/
@SimpleEvent(description = "Event triggered when the Yandex.Translate service returns the " +
"translated text. This event also provides a response code for error handling. If the " +
"responseCode is not 200, then something went wrong with the call, and the translation will " +
"not be available.")
public void GotTranslation(String responseCode, String translation) {
EventDispatcher.dispatchEvent(this, "GotTranslation", responseCode, translation);
}
}