show alternative translations on Android

This commit is contained in:
kr.angelov
2014-04-07 09:14:14 +00:00
parent 0df4d4bef6
commit e8303dc761
8 changed files with 192 additions and 200 deletions

View File

@@ -25,7 +25,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="LexicalEntryActivity"></activity>
<activity android:name="AlternativesActivity"></activity>
<activity android:name="HelpActivity"></activity>
<service android:name="TranslatorInputMethodService"
android:permission="android.permission.BIND_INPUT_METHOD">

View File

@@ -1,29 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/first_person_utterance_bg" >
<EditText
android:id="@+id/input_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginBottom="16dp"
android:layout_gravity="left"
android:padding="8dp"
android:textSize="20sp"
android:inputType="text"
android:background="@drawable/first_person_utterance_bg"
/>
<ImageView
android:id="@+id/show_word"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_toRightOf="@id/input_text"
android:padding="8dp"
android:src="@drawable/ic_dictionary"
android:visibility="gone"
/>
</RelativeLayout>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/input_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginBottom="16dp"
android:layout_gravity="left"
android:padding="8dp"
android:textSize="20sp"
android:inputType="textNoSuggestions"
android:background="@drawable/first_person_utterance_bg"
/>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="@+id/arrow"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/open_image"
android:src="@drawable/open_arrow"
android:paddingLeft="10dp"
android:paddingRight="5dp" />
<TextView
android:id="@+id/lexical_desc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/arrow"
android:paddingLeft="10dp"
android:textSize="25sp"/>
<WebView
android:id="@+id/lexical_inflection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lexical_desc"/>
</RelativeLayout>

View File

@@ -1,6 +1,5 @@
package org.grammaticalframework.ui.android;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
@@ -8,17 +7,13 @@ import android.app.ListActivity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewParent;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -26,7 +21,7 @@ import android.widget.TextView;
import org.grammaticalframework.pgf.*;
import org.grammaticalframework.ui.android.LanguageSelector.OnLanguageSelectedListener;
public class LexicalEntryActivity extends ListActivity {
public class AlternativesActivity extends ListActivity {
private Translator mTranslator;
private LanguageSelector mShowLanguageView;
@@ -94,17 +89,10 @@ public class LexicalEntryActivity extends ListActivity {
private void updateTranslations() {
@SuppressWarnings("unchecked")
List<MorphoAnalysis> list = (List<MorphoAnalysis>)
List<Object> list = (List<Object>)
getIntent().getExtras().getSerializable("analyses");
List<String> data = new ArrayList<String>();
for (MorphoAnalysis a : list) {
if (!data.contains(a.getLemma())) {
data.add(a.getLemma());
}
}
setListAdapter(new LexicalAdapter(this, data));
setListAdapter(new AlternativesAdapter(this, list));
expandedView = null;
}
@@ -144,39 +132,86 @@ public class LexicalEntryActivity extends ListActivity {
expandedView = view;
}
private class LexicalAdapter extends ArrayAdapter<String> {
public LexicalAdapter(Context context, List<String> data) {
private void expandExpr(View view, Expr expr) {
ImageView arrow = (ImageView) view.findViewById(R.id.arrow);
arrow.setImageResource(R.drawable.close_arrow);
WebView inflectionView = (WebView) view.findViewById(R.id.lexical_inflection);
if (inflectionView == null) {
inflectionView = new WebView(this);
inflectionView.setId(R.id.lexical_inflection);
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, R.id.lexical_desc);
((RelativeLayout) view).addView(inflectionView, params);
}
inflectionView.loadData(expr.toString(), "text/plain; charset=UTF-8", null);
expandedView = view;
}
private class AlternativesAdapter extends ArrayAdapter<Object> {
public AlternativesAdapter(Context context, List<Object> data) {
super(context, android.R.layout.simple_list_item_1, data);
}
public View getView(int position, View convertView, ViewGroup parent) {
final String lemma = getItem(position);
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.lexical_item, null);
Object item = getItem(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)
getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.alternative_item, null);
}
TextView descView =
(TextView) convertView.findViewById(R.id.lexical_desc);
TextView descView = (TextView)
convertView.findViewById(R.id.lexical_desc);
String phrase = mTranslator.generateLexiconEntry(lemma);
descView.setText(phrase);
if (item instanceof MorphoAnalysis) {
final String lemma = ((MorphoAnalysis) item).getLemma();
String phrase = mTranslator.generateLexiconEntry(lemma);
descView.setText(phrase);
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (expandedView == view)
collapse();
else if (expandedView == null)
expand(view, lemma);
else {
collapse();
expand(view, lemma);
}
}
});
} else {
if (item instanceof ExprProb) {
final Expr expr = ((ExprProb) item).getExpr();
String phrase = mTranslator.linearize(expr);
if (phrase.startsWith("% ") || phrase.startsWith("* ") || phrase.startsWith("+ "))
phrase = phrase.substring(2);
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (expandedView == view)
collapse();
else if (expandedView == null)
expand(view, lemma);
else {
collapse();
expand(view, lemma);
}
descView.setText(phrase);
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (expandedView == view)
collapse();
else if (expandedView == null)
expandExpr(view, expr);
else {
collapse();
expandExpr(view, expr);
}
}
});
}
});
}
return convertView;
}

View File

@@ -1,6 +1,7 @@
package org.grammaticalframework.ui.android;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -21,7 +22,7 @@ public class ConversationView extends ScrollView {
private ViewGroup mContent;
private OnWordSelectedListener mWordListener;
private OnAlternativesListener mAlternativesListener;
private ASR.Listener mSpeechListener;
public ConversationView(Context context, AttributeSet attrs, int defStyle) {
@@ -53,7 +54,7 @@ public class ConversationView extends ScrollView {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
}
v.setFocusable(false);
mLastUtterance = (View) v.getParent();
mLastUtterance = v;
if (mSpeechListener != null)
mSpeechListener.onSpeechInput(text.toString().trim());
return true;
@@ -71,18 +72,17 @@ public class ConversationView extends ScrollView {
};
private EditorListener mEditorListener = new EditorListener();
private View mLastUtterance = null;
private TextView mLastUtterance = null;
public void addFirstPersonUtterance(CharSequence text, boolean focused) {
View view =
EditText edittext = (EditText)
mInflater.inflate(R.layout.first_person_utterance, mContent, false);
EditText edittext = (EditText) view.findViewById(R.id.input_text);
edittext.setText(text);
edittext.setOnEditorActionListener(mEditorListener);
edittext.setOnClickListener(mEditorListener);
Bundle extras = edittext.getInputExtras(true);
extras.putBoolean("show_language_toggle", false);
mContent.addView(view);
mContent.addView(edittext);
if (focused) {
edittext.requestFocus();
@@ -98,16 +98,16 @@ public class ConversationView extends ScrollView {
}
});
mLastUtterance = view;
mLastUtterance = edittext;
}
@SuppressWarnings("deprecation")
public CharSequence addSecondPersonUtterance(CharSequence text) {
public CharSequence addSecondPersonUtterance(final CharSequence source, CharSequence target, final Object alternatives) {
TextView view;
if (mLastUtterance != null && mLastUtterance.getTag() != null)
view = (TextView) mLastUtterance.getTag();
else {
view = (TextView)
view = (TextView)
mInflater.inflate(R.layout.second_person_utterance, mContent, false);
mContent.addView(view);
post(new Runnable() {
@@ -119,71 +119,55 @@ public class ConversationView extends ScrollView {
mLastUtterance.setTag(view);
}
// parse by words, marked by %, darkest red colour
if (text.charAt(0) == '%') {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mAlternativesListener != null)
mAlternativesListener.onAlternativesSelected(source, alternatives);
}
});
// parse by words, marked by %, darkest red color
if (target.charAt(0) == '%') {
view.setBackgroundDrawable(getResources().getDrawable(R.drawable.second_person_worst_utterance_bg));
text = text.subSequence(2, text.length()) ;
target = target.subSequence(2, target.length()) ;
}
// parse error or unknown translations (in []) present, darkest red colour
else if (text.toString().contains("parse error:") || text.toString().contains("[")) {
// parse error or unknown translations (in []) present, darkest red color
else if (target.toString().contains("parse error:") || target.toString().contains("[")) {
view.setBackgroundDrawable(getResources().getDrawable(R.drawable.second_person_worst_utterance_bg));
}
// parse by chunks, marked by *, red colour
else if (text.charAt(0) == '*') {
// parse by chunks, marked by *, red color
else if (target.charAt(0) == '*') {
view.setBackgroundDrawable(getResources().getDrawable(R.drawable.second_person_chunk_utterance_bg));
text = text.subSequence(2, text.length()) ;
target = target.subSequence(2, target.length()) ;
}
// parse by domain grammar, marked by +, green colour
else if (text.charAt(0) == '+') {
// parse by domain grammar, marked by +, green color
else if (target.charAt(0) == '+') {
view.setBackgroundDrawable(getResources().getDrawable(R.drawable.second_person_best_utterance_bg));
text = text.subSequence(2, text.length()) ;
target = target.subSequence(2, target.length()) ;
}
view.setText(text);
return text ;
view.setText(target);
return target;
}
public void updateLastUtterance(CharSequence text, Object lexicon) {
if (mLastUtterance == null)
return;
EditText textview = (EditText) mLastUtterance.findViewById(R.id.input_text);
if (textview == null)
return;
textview.setText(text);
if (lexicon != null && mWordListener != null) {
ImageView showWordButton = (ImageView) mLastUtterance.findViewById(R.id.show_word);
showWordButton.setVisibility(VISIBLE);
final Object lexicon2 = lexicon;
showWordButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mWordListener != null) {
TextView textview = (TextView)
((View) v.getParent()).findViewById(R.id.input_text);
CharSequence text = textview.getText();
mWordListener.onWordSelected(text, lexicon2);
}
}
});
}
public void updateLastUtterance(CharSequence text) {
if (mLastUtterance != null)
mLastUtterance.setText(text);
}
public void setOnWordSelectedListener(OnWordSelectedListener listener) {
mWordListener = listener;
public void setOnAlternativesListener(OnAlternativesListener listener) {
mAlternativesListener = listener;
}
public void setSpeechInputListener(ASR.Listener listener) {
mSpeechListener = listener;
}
public interface OnWordSelectedListener {
public void onWordSelected(CharSequence word, Object lexicon);
public interface OnAlternativesListener {
public void onAlternativesSelected(CharSequence word, Object lexicon);
}
}

View File

@@ -10,6 +10,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.speech.SpeechRecognizer;
import android.util.Log;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -27,7 +28,8 @@ import android.widget.TextView;
import org.grammaticalframework.ui.android.ASR.State;
import org.grammaticalframework.ui.android.LanguageSelector.OnLanguageSelectedListener;
import org.grammaticalframework.ui.android.ConversationView.OnWordSelectedListener;
import org.grammaticalframework.ui.android.ConversationView.OnAlternativesListener;
import org.grammaticalframework.pgf.ExprProb;
import org.grammaticalframework.pgf.MorphoAnalysis;
public class MainActivity extends Activity {
@@ -115,11 +117,11 @@ public class MainActivity extends Activity {
mSpeechListener = new SpeechInputListener();
mConversationView.setOnWordSelectedListener(new OnWordSelectedListener() {
mConversationView.setOnAlternativesListener(new OnAlternativesListener() {
@Override
public void onWordSelected(CharSequence word, Object lexicon) {
Intent myIntent = new Intent(MainActivity.this, LexicalEntryActivity.class);
myIntent.putExtra("source", word);
public void onAlternativesSelected(CharSequence input, Object lexicon) {
Intent myIntent = new Intent(MainActivity.this, AlternativesActivity.class);
myIntent.putExtra("source", input);
myIntent.putExtra("analyses", (Serializable) lexicon);
MainActivity.this.startActivity(myIntent);
}
@@ -282,40 +284,38 @@ public class MainActivity extends Activity {
}
private void handlePartialSpeechInput(String input) {
mConversationView.updateLastUtterance(input, null);
mConversationView.updateLastUtterance(input);
}
private void handleSpeechInput(final String input) {
List<MorphoAnalysis> list = mTranslator.lookupMorpho(input);
if (list.size() == 0)
list = null;
final List<MorphoAnalysis> list = mTranslator.lookupMorpho(input);
mConversationView.updateLastUtterance(input, list);
new AsyncTask<Void,Void,String>() {
mConversationView.updateLastUtterance(input);
new AsyncTask<Void,Void,Pair<String,List<ExprProb>>>() {
@Override
protected void onPreExecute() {
showProgressBar();
}
@Override
protected String doInBackground(Void... params) {
protected Pair<String,List<ExprProb>> doInBackground(Void... params) {
return mTranslator.translate(input);
}
@Override
protected void onPostExecute(String result) {
outputText(result);
protected void onPostExecute(Pair<String,List<ExprProb>> res) {
String text = res.first;
List<ExprProb> alts = res.second;
if (DBG) Log.d(TAG, "Speaking: " + res.first);
CharSequence text2 =
mConversationView.addSecondPersonUtterance(input, text, (list.size() == 0) ? alts : list);
mTts.speak(getTargetLanguageCode(), text2.toString());
hideProgressBar();
}
}.execute();
}
private void outputText(String text) {
if (DBG) Log.d(TAG, "Speaking: " + text);
CharSequence text2 = mConversationView.addSecondPersonUtterance(text);
mTts.speak(getTargetLanguageCode(), text2.toString());
}
private class SpeechInputListener implements ASR.Listener {
@Override

View File

@@ -3,10 +3,12 @@ package org.grammaticalframework.ui.android;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.util.Pair;
import android.view.inputmethod.CompletionInfo;
import org.grammaticalframework.pgf.Concr;
import org.grammaticalframework.pgf.Expr;
import org.grammaticalframework.pgf.ExprProb;
import org.grammaticalframework.pgf.FullFormEntry;
import org.grammaticalframework.pgf.MorphoAnalysis;
import org.grammaticalframework.pgf.PGF;
@@ -15,6 +17,7 @@ import org.grammaticalframework.pgf.ParseError;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -75,6 +78,8 @@ public class Translator {
private static final String SOURCE_LANG_KEY = "source_lang";
private static final String TARGET_LANG_KEY = "target_lang";
private static final int NUM_ALT_TRANSLATIONS = 10;
private SharedPreferences mSharedPref;
private Language getPrefLang(String key, int def) {
@@ -258,67 +263,79 @@ public class Translator {
return out;
}
public String translateWord(String input) {
private String translateWord(String input) {
String output = "[" + input + "]" ; // if all else fails, return the word itself in brackets
Concr sourceLang = getSourceConcr() ;
Concr targetLang = getTargetConcr() ;
String output = "[" + input + "]" ; // if all else fails, return the word itself in brackets
Concr sourceLang = getSourceConcr() ;
Concr targetLang = getTargetConcr() ;
String lowerinput = input.toLowerCase() ; // also consider lower-cased versions of the word
String lowerinput = input.toLowerCase() ; // also consider lower-cased versions of the word
try {
Expr expr = sourceLang.parseBest("Chunk", input) ; // try parse as chunk
Expr expr = sourceLang.parseBest("Chunk", input) ; // try parse as chunk
output = targetLang.linearize(expr);
return output ;
} catch (ParseError e) { // if this fails
} catch (ParseError e) { // if this fails
List<MorphoAnalysis> morphos = lookupMorpho(input) ; // lookup morphological analyses
List<MorphoAnalysis> morphos = lookupMorpho(input) ; // lookup morphological analyses
morphos.addAll(lookupMorpho(lowerinput)) ; // including the analyses of the lower-cased word
morphos.addAll(lookupMorpho(lowerinput)) ; // including the analyses of the lower-cased word
for (MorphoAnalysis ana : morphos) {
if (targetLang.hasLinearization(ana.getLemma())) { // check that the word has linearization in target
output = targetLang.linearize(Expr.readExpr(ana.getLemma())) ;
break ; // if yes, don't search any more
}
}
return output ;
}
for (MorphoAnalysis ana : morphos) {
if (targetLang.hasLinearization(ana.getLemma())) { // check that the word has linearization in target
output = targetLang.linearize(Expr.readExpr(ana.getLemma())) ;
break ; // if yes, don't search any more
}
}
return output ;
}
}
public String parseByLookup(String input) {
String[] words = input.split(" ") ;
private String translateByLookup(String input) {
String[] words = input.split(" ") ;
String output = "%" ;
for (String w : words) {
output = output + " " + translateWord(w) ;
output = output + " " + translateWord(w) ;
}
return output ;
return output ;
}
/**
* Takes a lot of time. Must not be called on the main thread.
*/
public String translate(String input) {
public Pair<String, List<ExprProb>> translate(String input) {
if (getSourceLanguage().getLangCode().equals("cmn-Hans-CN")) {
// for Chinese we need to put space after every character
input = explode(input);
}
String output = null;
List<ExprProb> exprs = new ArrayList<ExprProb>();
try {
Concr sourceLang = getSourceConcr();
Expr expr = sourceLang.parseBest(getGrammar().getStartCat(), input);
Concr targetLang = getTargetConcr();
String output = targetLang.linearize(expr);
return output;
Expr expr = sourceLang.parseBest(getGrammar().getStartCat(), input);
int count = NUM_ALT_TRANSLATIONS;
for (ExprProb ep : sourceLang.parse(getGrammar().getStartCat(), input)) {
if (count-- <= 0)
break;
exprs.add(ep);
output = targetLang.linearize(expr);
}
} catch (ParseError e) {
// Log.e(TAG, "Parse error: " + e); //lookupMorpho
// return "parse error: " + e.getMessage();
String output = parseByLookup(input) ;
return output ;
output = translateByLookup(input);
}
return new Pair<String,List<ExprProb>>(output, exprs);
}
public String linearize(Expr expr) {
Concr targetLang = getTargetConcr();
return targetLang.linearize(expr);
}
public String generateLexiconEntry(String lemma) {

View File

@@ -374,7 +374,7 @@ public class TranslatorInputMethodService extends InputMethodService
mTranslator.setSourceLanguage(newSource);
handleChangeSourceLanguage(newSource);
} else if (primaryCode == TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE) {
String translation = mTranslator.translate(getComposingString());
String translation = mTranslator.translate(getComposingString()).first;
getCurrentInputConnection().commitText(translation, 1);
return;
} else if (primaryCode < TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE &&