I'm working on a simple translation app as part of a university project. For the translation process, I query MyMemory's Translate API using Retrofit and I retrieve the translated output as a String. This is working fine for the most part, but it's causing some issues in other areas of my program.
When I make a call to retrieve a translation from the library, subsequent methods begin to run before the translation is fully recieved - this then prevents those methods from working fully since they rely on the received translation.
Here are some relevant snippets of my code that might explain the question better:
TranslateAPI: (Interface that i use to retrieve a translation)
public class TranslateAPI {
private static final String ENDPOINT = "http://api.mymemory.translated.net";
public final static String FRENCH = "FR";
public final static String ENGLISH = "EN";
public final static String ITALIAN = "IT";
public final static String GREEK = "EL";
public final static String SPANISH = "ES";
private final TranslateService mService;
String translation = "";
public interface TranslateService {
#GET("/get")
Call<TranslatedData> getTranslation(
#Query("q") String textToTranslate,
#Query(value = "langpair", encoded = true)
String languagePair);
}
public TranslateAPI() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
mService = retrofit.create(TranslateService.class);
}
public String translate(final String textToTranslate, final String fromLanguage, final String toLanguage) {
mService.getTranslation(textToTranslate, URLEncoder.encode(fromLanguage + "|" + toLanguage))
.enqueue(new Callback<TranslatedData>() {
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output =
String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate,
fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
}
#Override
public void onFailure(Throwable t) {
System.out.println("[DEBUG]" + " RestApi onFailure - " + "");
}
});
return translation;
}
}
In the code above, the translate(final String textToTranslate, final String fromLanguage, final String toLanguage) method successfully returns the translated output as a string.
Now, to demonstrate exactly what goes wrong, assume the following code snippet for my main activity:
private void runChatBot() {
translateOutput(input, targetLanguage); //calls the translate method of the TranslateAPI class
System.out.println("translatedOutput value in MainActivity: " + translatedOutput);
//Use translated output here
}
What happens here is that the print statement in runChatbot() executes before the call to the translation API. This is not the desired behaviour, as I would like the translateOutput() method to execute fully before any following instructions.
Any help is much appreciated. Thanks in advance :)
UPDATE: Current code after initial answers
TranslateAPI - Declarations
public interface Callbacks {
void onTranslationReceived(String result);
void onTranslationFailed();
}
TranslateAPI - translate()
public void translate(final String textToTranslate, final String fromLanguage, final String toLanguage) {
mService.getTranslation(textToTranslate, URLEncoder.encode(fromLanguage + "|" + toLanguage))
.enqueue(new Callback<TranslatedData>() {
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output =
String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate,
fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
}
#Override
public void onFailure(Throwable t) {
System.out.println("[DEBUG]" + " RestApi onFailure - " + "");
}
});
}
MainActivity:
#Override
public void onTranslationReceived(String result) {
runChatBot();
}
#Override
public void onTranslationFailed() {
//Handle failure here
}
public void runChatBot() {
translatedOutput = translateAPI.getTranslation();
System.out.println("translatedOutput value in MainActivity: " + translatedOutput);
userOutputView.setText(translatedOutput);
ttsResponse(translatedOutput, TTSLanguage);
setVisualCue(chatBot.getVisualMatch());
chatBot.clearResults();
}
Since your translate() method is asynchronous, you should define a callback in TranslateAPI to send the result back to your Activity when the result is received. By doing this, you would then only perform work on the translation result once you know you've received a response from TranslateAPI.
So in TranslateAPI you would define a callback interface:
public interface Callbacks {
void onTranslationReceived(String result);
}
Then you would have your Activity implement TranslateAPI.Callbacks and implement the callback like this:
public void onTranslationReceived(String result) {
//do something with the result
runChatBot(result); //or something similar
}
Then, once you receive the response in the callback, you do whatever it is you have to do with the translation result. This way, you know you will never be executing anything on the translated result until the translation is complete.
EDIT IN RESPONSE TO COMMENTS
So in order to actually send the response to your Activity once the translate response is received, you need to pass a reference to your Activity into TranslateAPI. Since your Activity implements the callbacks, you can simply pass this in: TranslateAPI translateApi = new TranslateAPI(this);
Then in your TranslateAPI, you'll need to take this reference and use it as the "listener" of your callbacks. So in TranslateAPI you'll want to define a variable like this private Callbacks listener; and you'll assign this in your TranslateAPI constructor the value that's passed in from the Activity. So your TranslateAPI constructor might look like this:
public TranslateAPI(Callbacks listener) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
mService = retrofit.create(TranslateService.class);
//this is the line you would add...
this.listener = listener;
}
And then in your onResponse() callback in TranslateAPI, you simply pass the value to the listener, which passes it back to the implemented method in your Activity. Like this:
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output = String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate, fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
//this is the line you would add...
listener.onTranslateReceived(translation);
}
Hope this helps clarify things. Let me know if you have any more questions!
this happens because code is executed asynchronous. Your retrofit network request takes some time to complete, so by default, java will execute the next line of code before it concludes. To solve this you must use the retrofit callback onResponse and onFailure.
I sugest you to create a new interface and pass it on constructor os method of your TranslateApiCode. Something like:
public interface OnTranslate {
void onSuccess(); // here you can pass any object you need later
void onError(); // here you can pass any object you need later
}
public String translate(final String textToTranslate, final String fromLanguage, final String toLanguage) {
mService.getTranslation(textToTranslate, URLEncoder.encode(fromLanguage + "|" + toLanguage))
.enqueue(new Callback<TranslatedData>() {
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output =
String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate,
fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
myOnTranslateVariable.onSuccess();
}
#Override
public void onFailure(Throwable t) {
System.out.println("[DEBUG]" + " RestApi onFailure - " + "");
myOnTranslateVariable.onError();
}
});
return translation;
}
private void runChatBot() {
translateOutput(input, targetLanguage, new OnTranslate() {
void onSucess() {
System.out.println("translatedOutput value in MainActivity: " + translatedOutput);
}
void onError() {
System.out.println("some error happened");
}
}); //calls the translate method of the TranslateAPI class
//Use translated output here
}
Try Using Handler to notify when translation is completed and then perform the required operations. Thanks
Related
I am using Volley to make Http requests to my Web Api.
However I am having trouble getting the values from my api calls due to the asynchronous nature of Volley.
I have read that using a callback function could help with this issue, however I do not know how to implement such a solution.
How would I go about implementing a callback function in the following scenario?
public class Main
{
String name;
WebServiceConnections wsc = new WebServiceConnections();
name = wsc.getNameFromWeb();
System.out.println("Name: " + name);
}
public class WebServiceConnections
{
public String getNameFromWeb()
{
String url = "http://nameservice.net/GetName";
JsonArrayRequest req = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
return response.getString("Name");
}
catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
}
});
}
}
The problem with this code is that the variable "name" in Main will be null when it is called by the print statement as the asynchronous method in the WebServiceConnections class will not be finished by time the print statement is called.
Is a callback a good way to solve this problem?
Your code doesn't compile - you can't return a value in a method with void return type.
Your onResponse method is the callback. Perform the print within the method itself.
Hi i have 2 interfaces:
public interface SpendAction extends OpenGraphAction{
#PropertyName("namespace:transport") // I tried #PropertyName("transport") or without property
public void setTransport(OGObject object);
public OGObject getTransport();
}
And
public interface OGObject extends OpenGraphObject{
#PropertyName("namespace:hours")
public void setHours(int hours);
#PropertyName("namespace:minutes")
public void setMinutes(int minutes);
#PropertyName("namespace:seconds")
public void setSeconds(int seconds);
public int getHours();
public int getMinutes();
public int getSeconds();
public void setUrl(String url);
public String getUrl();
public void setTitle(String title);
public String getTitle();
public void setDescription(String message);
public String getDescription();
}
1st is an OpenGraphAction and 2nd one is object. I'm creating interfaces this way:
SpendAction action = GraphObject.Factory.create(SpendAction.class);
OGObject object = GraphObject.Factory.create(OGObject.class);
Then I'm setting object after filling it with data, url, title description etc.
action.setTransport(object);
// I've tried to replace this with action.addProperty("transport", object) and action.addProperty("namespace:transport", object")
//also action.setType("transport") and action.setType("namespace:transport)
action.setExplicitlyShared(true);
Facebook request creation:
Request request = new Request(
Session.getActiveSession(),
"me/namespace:spent",
null,
HttpMethod.POST,
new Request.Callback() {
#Override
public void onCompleted(Response response) {
if(response != null && response.getError() != null) Log.d("Facebook Share", "Error message:" + response.getError().getErrorCode() + ", " + response.getError().getErrorMessage());
if(response != null && response.getGraphObject() != null){
Toast.makeText(context, "Success!", Toast.LENGTH_SHORT).show();
}
}
}
);
request.setGraphObject(action);
request.executeAsync();
And I'm getting following error:
Error message:1611072, The action you're trying to publish is invalid because it does not specify any reference objects. At least one of the following properties must be specified: tv, transport, friends, studying, reading, shopping, cooking, cleaning, work, internet, personal_care, sport, sleeping, social_media, childcare, petcare, loo, games, sex, fun, hangover, smoking, eating.
I've already tried to set type for action, messed around with propertyname and nothing worked.
On facebook I have my story and there I have my custom object with some custom fields (hours, minutes, seconds).
Sharing custom story is working for me when I'm creating json object (String). But not with these interfaces.
My ContentObserver for observing the history in the browser is not being called. I don't understand why it isn't. I'm not doing anything different or bizarre, I'm following the API specs exactly, but to no avail! Below is my code:
In my service:
public class MonitorService extends Service {
//some global variables declared here
private ContentObserver historyObserver, searchObserver, chromeObserver;
public void onCreate() {
super.onCreate();
isRunning = false;
this.preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
//this.historyObserver = new HistoryObserver();
this.historyObserver = new HistoryObserver(new Handler());
this.searchObserver = new HistoryObserver(new Handler());
this.chromeObserver = new HistoryObserver(new Handler());
getApplicationContext().getContentResolver().registerContentObserver(Uri.parse("content://com.android.chrome.browser/history"), false, this.chromeObserver);
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Browser.BOOKMARKS_URI, false, this.historyObserver);
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Browser.SEARCHES_URI, false, this.searchObserver);
}
//Other required methods in class
}//end of class
Then in my HistoryObserver Class we have:
public class HistoryObserver extends ContentObserver {
public final String TAG = "HistoryObserver";
public HistoryObserver(Handler handler) {
super(handler);
Log.d(TAG, "Creating new HistoryObserver");
}
public HistoryObserver() {
super(null);
Log.d(TAG, "Creating a new HistoryObserver without a Handler");
}
#Override
public boolean deliverSelfNotifications() {
Log.d(TAG, "delivering self notifications");
return true;
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(TAG, "onChange without uri: " + selfChange);
//onChange(selfChange, null);
}
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Log.d(TAG, "onChange: " + selfChange + "\t " + uri.toString());
}
}
Like I said there is nothing special or unique about this implementation. Yet, when I go go to a new website or search for something in Chrome, the onChange method is never fired.
I figured out the problem. The /history content provider isn't an observable. The observables come through the /bookmark uri. Once I discovered that, things got working very quickly and very well.
i use asmack buddycloud 2010 for a chat app and im trying to send chat messages from my servie to activity using handler here is the code part of my service:
public void processPacket(Packet packet) {
Message message = (Message) packet;
if (message.getBody() != null) {
String fromName = StringUtils.parseBareAddress(message.getFrom());
Log.i("XMPPChatDemoActivity ", " Text Recieved " + message.getBody() + " from "
+ fromName);
messages.add(fromName + ":");
messages.add(message.getBody());
Log.i("XMPPChatDemoActivity",message.getBody());
mHandler.obtainMessage(123, "Message or data");
mHandler.sendMessage(message);
here on the mhanlder.sendmessage i have an error saying the method sendMessage(Message message) in the type handler is not applicable for arguments (org.jivesoft.smack.packet.message)
and here in the activity :
Public CLass XMPPActivity extends Activity {
.
.
.
private class getmessage extends Handler{
public void handleMessage(Message msg) {
setListAdapter();
}
}
here i cant override the handlemessage, it says i must override a supertype method !!!! do you guys know what's up? am i missing some thing? thanks
also when i replace it with
mHandler.obtainMessage(123, message).sendToTarget();
i get nothing, because i can not override the handleMessage()
You can do it by following way.
Step 1
Declare Handler inside your activity say inside onCreate method.
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case PASS:
String obj = msg.obj.toString();
Toast.makeText(YourActivity.this, "PASS",
Toast.LENGTH_SHORT).show();
break;
case FAIL:
Toast.makeText(YourActivity.this, "FAIL",
Toast.LENGTH_SHORT).show();
break;
}
}
};
Step 2
Declare 2 constants which are used in Handler
public static final int PASS = 1;
public static final int FAIL = 2;
Step 3
To pass message, use following code.
Message msg = new Message();
msg.what = PASS;
msg.obj = fromName;
Here, you can change msg.what to any integer constant. And you can set any type of object to msg.obj. But make sure that what type of object you are setting to it, you need to type cast it based on that only. Otherwise exception will be thrown.
I'm trying to implement search autocompletion with android-query library
I have callback instance in my activity:
Callback:
class SearchCompleteCallback extends AjaxCallback<ItemSearchResult> {
public void callback(String url, ItemSearchResult searchResult, AjaxStatus status) {
Log.d("SCB", String.format("Url:%s\n Msg: %s\n Code: %s\n Error: %s",
url,
status.getMessage(),
status.getCode(),
status.getError()));
if (searchResult != null) {
Log.d("SCB", String.format("Status: %s\n Val: %s",
searchResult.getStatus(),
searchResult.getInnGroup().getItems()));
updateSearchResult(searchResult);
}
else {
Log.w("SCB", "Ajax failed");
}
}
}
Search routine, that called on text change:
private void doSearch(String query) {
ppApi.getSearchResult(query, searchCompleteListener);
}
and
APIClass
public class PPServerApi {
private AQuery aq;
private GsonTransformer transformer;
private static class GsonTransformer implements Transformer{
public <T> T transform(String url, Class<T> type, String encoding, byte[] data, AjaxStatus status) {
Gson g = new Gson();
return g.fromJson(new String(data), type);
}
}
public PPServerApi(AQuery newAq){
aq = newAq;
transformer = new GsonTransformer();
AQUtility.setDebug(true);
AjaxCallback.setTransformer(transformer);
}
public void getSearchResult(String itemName, AjaxCallback<ItemSearchResult> cb){
String url = "http://my.api.server/search?q=" + itemName;
aq.ajax(url, ItemSearchResult.class, cb.header("content-type", "application/json"));
}
}
So, the question is how to abort old queries before sending new one ?
(I don't need result of old queries if text in search field changed)
I've tried to call searchCompleteListener.abort() in doSearch(), but it causes exception in next going query:
08-09 20:59:10.551: W/AQuery(6854): get:http://my.api.server/search?q=abc
08-09 20:59:10.551: W/AQuery(6854): creating http client
08-09 20:59:10.561: W/AQuery(6854): java.io.IOException: Aborted
08-09 20:59:10.561: W/AQuery(6854): at com.androidquery.callback.AbstractAjaxCallback.httpDo(AbstractAjaxCallback.java:1569)
...
so, i can't perform even single query in this case.
There is no way of making android-query cancel an AJAX request once it has been started.
You'll have to use another library, sorry.
What you can do is to check if the request has become obsolete when it finishes.
You could do that by checking if the URL matches the latest URL you requested for
if (searchResult != null && url.equals(latestRequestUrl)) {
(note, you'd have to let getSearchResult return the URL)
You can use the droidQuery library instead. Using droidQuery, you can cancel all Ajax tasks using the call:
$.ajaxKillAll();
You can also perform your request with this:
$.ajax(new AjaxOptions().url(url).header("content-type", "application/json").type("json").dataType("GET").dataType("json").success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
JSONObject json = (JSONObject) params[0];
//TODO handle json
}
}).error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = (AjaxError) params[0];
Log.e("Ajax", "Error " + error.status + ": " + error.reason);
}
}));
You can abort any aquery processing using this.
private AjaxCallback<String> ajaxCallback = new AjaxCallback<String>(){
#Override
public void callback(String url, String object, AjaxStatus status) {
//do your processing with server response
processInformation(result);
};
};
//on our previous code
query.ajax(remoteUrl,String.class,ajaxCallback);
public void cancelAquery(){
//this statement does cancel the request i.e. we won't receive any information on callback method
//ajaxCallback.async(null);
ajaxCallback.abort();
}
For more info, you can see this link https://laaptu.wordpress.com/tag/android-cancelling-aquery/
just call aq.ajaxCancel() for more details refer to the API docs
http://android-query.googlecode.com/svn/trunk/javadoc/com/androidquery/AbstractAQuery.html#ajaxCancel()