diff --git a/src/ui/android/res/drawable-hdpi/ic_drawer.png b/src/ui/android/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 000000000..c59f601ca
Binary files /dev/null and b/src/ui/android/res/drawable-hdpi/ic_drawer.png differ
diff --git a/src/ui/android/res/drawable-mdpi/ic_drawer.png b/src/ui/android/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 000000000..1ed2c56ee
Binary files /dev/null and b/src/ui/android/res/drawable-mdpi/ic_drawer.png differ
diff --git a/src/ui/android/res/drawable-xhdpi/ic_drawer.png b/src/ui/android/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 000000000..a5fa74def
Binary files /dev/null and b/src/ui/android/res/drawable-xhdpi/ic_drawer.png differ
diff --git a/src/ui/android/res/drawable-xxhdpi/ic_drawer.png b/src/ui/android/res/drawable-xxhdpi/ic_drawer.png
new file mode 100644
index 000000000..9c4685d6e
Binary files /dev/null and b/src/ui/android/res/drawable-xxhdpi/ic_drawer.png differ
diff --git a/src/ui/android/res/layout/activity_lexical_entry.xml b/src/ui/android/res/layout/activity_lexical_entry.xml
index 0dc7fc7be..fa49f252b 100644
--- a/src/ui/android/res/layout/activity_lexical_entry.xml
+++ b/src/ui/android/res/layout/activity_lexical_entry.xml
@@ -4,49 +4,66 @@
android:layout_height="match_parent"
android:layout_width="match_parent">
-
+
+
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
-
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:padding="8dp"
+ android:background="#C0C0C0">
+
+
+
+
+
+
+
+
-
-
-
+
-
-
+ android:layout_gravity="start"
+ android:choiceMode="singleChoice"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"
+ android:background="#FFFFE0"/>
+
-
\ No newline at end of file
+
diff --git a/src/ui/android/res/layout/lexical_item.xml b/src/ui/android/res/layout/lexical_item.xml
index e7f551491..1d3835e54 100644
--- a/src/ui/android/res/layout/lexical_item.xml
+++ b/src/ui/android/res/layout/lexical_item.xml
@@ -18,10 +18,10 @@
android:layout_toRightOf="@id/arrow"
android:paddingLeft="10dp"
android:textSize="25sp"/>
-
+
-
\ No newline at end of file
+
diff --git a/src/ui/android/res/menu/main.xml b/src/ui/android/res/menu/main.xml
index 7f5a4c4f7..b1ba5f268 100644
--- a/src/ui/android/res/menu/main.xml
+++ b/src/ui/android/res/menu/main.xml
@@ -1,6 +1,8 @@
diff --git a/src/ui/android/res/values/strings.xml b/src/ui/android/res/values/strings.xml
index a4d02295d..3b9f828b2 100644
--- a/src/ui/android/res/values/strings.xml
+++ b/src/ui/android/res/values/strings.xml
@@ -29,4 +29,8 @@
Search word:
Search for words in the lexicon
+
+ Topics
+ Open topics
+ Close topics
diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/AlternativesActivity.java b/src/ui/android/src/org/grammaticalframework/ui/android/AlternativesActivity.java
index 2cb8f9da4..8027441b0 100644
--- a/src/ui/android/src/org/grammaticalframework/ui/android/AlternativesActivity.java
+++ b/src/ui/android/src/org/grammaticalframework/ui/android/AlternativesActivity.java
@@ -5,19 +5,30 @@ import java.util.*;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.view.LayoutInflater;
+import android.view.*;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.MenuItem;
+import android.view.LayoutInflater;
import android.webkit.WebView;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ListView;
+import android.widget.AdapterView;
import android.util.Log;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v4.app.ActionBarDrawerToggle;
import org.grammaticalframework.pgf.*;
import org.grammaticalframework.ui.android.LanguageSelector.OnLanguageSelectedListener;
@@ -28,6 +39,9 @@ public class AlternativesActivity extends ListActivity {
private LanguageSelector mShowLanguageView;
private View mProgressBarView = null;
private AlternativesAdapter mAdapter = null;
+ private DrawerLayout mDrawerLayout;
+ private ListView mDrawerList;
+ private ActionBarDrawerToggle mDrawerToggle;
/** Called when the activity is first created. */
@Override
@@ -64,23 +78,44 @@ public class AlternativesActivity extends ListActivity {
}.execute();
}
});
-
+
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
+ R.drawable.ic_drawer,
+ R.string.topics_open,
+ R.string.topics_close);
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setHomeButtonEnabled(true);
+
TextView descrView = (TextView) findViewById(R.id.lexical_desc);
- String authority = getIntent().getData().getAuthority();
- String source = getIntent().getData().getQueryParameter("source");
- List analyses = new ArrayList();
- for (String an : getIntent().getData().getQueryParameters("alternative")) {
- analyses.add(Expr.readExpr(an));
- }
- descrView.setText(source);
+ if (getIntent().getData() != null) {
+ String authority = getIntent().getData().getAuthority();
+ String source = getIntent().getData().getQueryParameter("source");
- mAdapter = new AlternativesAdapter(this, authority, analyses);
+ List analyses = new ArrayList();
+ for (String an : getIntent().getData().getQueryParameters("alternative")) {
+ analyses.add(Expr.readExpr(an));
+ }
+ descrView.setText(source);
+
+ mAdapter = new AlternativesAdapter(this, authority, analyses);
+ } else {
+ mDrawerLayout.openDrawer(Gravity.LEFT);
+
+ mAdapter = new AlternativesAdapter(this, Translator.WORDS);
+ }
+
+ expandedView = null;
setListAdapter(mAdapter);
- expandedView = null;
+
+ mDrawerList = (ListView) findViewById(R.id.topics_list);
+ mDrawerList.setAdapter(mAdapter.getTopicsAdapter());
mProgressBarView = findViewById(R.id.progressBarView);
- }
+ }
@Override
protected void onResume() {
@@ -98,7 +133,7 @@ public class AlternativesActivity extends ListActivity {
}
private View expandedView;
-
+
private void collapse() {
if (expandedView == null)
return;
@@ -182,22 +217,183 @@ public class AlternativesActivity extends ListActivity {
expandedView = view;
}
- private class AlternativesAdapter extends ArrayAdapter {
- private String mAuthority;
+ private class Topic {
+ public String name;
+ public Expr expr;
+ public boolean isChecked;
+ public boolean isAvailable;
+
+ public Topic(String name, Expr expr) {
+ this.name = name;
+ this.expr = expr;
+ this.isChecked = false;
+ this.isAvailable = true;
+ }
+ }
- public AlternativesAdapter(Context context, String authority, List data) {
- super(context, android.R.layout.simple_list_item_1, data);
- mAuthority = authority;
- }
+ private class AlternativesAdapter extends BaseAdapter implements ListAdapter {
+ /**
+ * Contains the list of objects that represent the alternatives
+ */
+ private List mAlternatives;
+
+ private Context mContext;
+
+ // A copy of the original mAlternatives array, initialized from and then used instead as soon as
+ // a topic filtering is applied. mAlternatives will then only contain the filtered values.
+ private ArrayList mOriginalAlternatives;
+
+ private LayoutInflater mInflater;
+
+ private String mAuthority;
+
+ /**
+ * A list of lists of topics. Each element in this list contains
+ * the list of topics for the correponding item in
+ * mAlternatives/mOriginalAlternatives
+ */
+ private List> mTopics;
+
+ private Map mTopicMap;
+ private Topic[] mAllTopics;
+ private Topic[] mOriginalAllTopics;
+
+ private Topic mOtherTopic;
+ private Topic mSourceTopic;
+
+ private TopicsAdapter mTopicsAdapter;
+
+ public AlternativesAdapter(Context context, String authority, List alternatives) {
+ mContext = context;
+ mAuthority = authority;
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAlternatives = alternatives;
+
+ boolean addOther = false;
+ mTopics = new ArrayList>();
+ mTopicMap = new TreeMap();
+ for (Expr e : mAlternatives) {
+ List topics = new ArrayList();
+ for (Expr topicExpr : mTranslator.getTopicsOf(e)) {
+ String name = mTranslator.linearizeSource(topicExpr);
+ String key = name.toLowerCase();
+ Topic topic = mTopicMap.get(key);
+ if (topic == null) {
+ topic = new Topic(name, topicExpr);
+ mTopicMap.put(key,topic);
+ }
+ topics.add(topic);
+ }
+ mTopics.add(topics);
+
+ if (topics.size() == 0)
+ addOther = true;
+ }
+
+ int i = 0;
+ mAllTopics = new Topic[mTopicMap.size() + (addOther ? 1 : 0)];
+ for (Map.Entry entry : mTopicMap.entrySet()) {
+ mAllTopics[i++] = entry.getValue();
+ }
+ if (addOther) {
+ Expr topicExpr = Expr.readExpr("other_1_A");
+ String name = mTranslator.linearizeSource(topicExpr);
+ mOtherTopic = new Topic(name, topicExpr);
+ mAllTopics[i++] = mOtherTopic;
+ }
+
+ mOriginalAllTopics = mAllTopics;
+
+ mTopicsAdapter = new TopicsAdapter();
+ }
+
+ public AlternativesAdapter(Context context, String authority) {
+ mContext = context;
+ mAuthority = authority;
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAlternatives = null;
+
+ mTopics = null;
+
+ int i = 0;
+ mTopicMap = new TreeMap();
+ for (Expr topicExpr : mTranslator.getTopicsOf(null)) {
+ String name = mTranslator.linearizeSource(topicExpr);
+ String key = name.toLowerCase();
+ Topic topic = mTopicMap.get(key);
+ if (topic == null) {
+ topic = new Topic(name, topicExpr);
+ mTopicMap.put(key,topic);
+ }
+ }
+ mAllTopics = new Topic[mTopicMap.size()];
+ for (Map.Entry entry : mTopicMap.entrySet()) {
+ mAllTopics[i++] = entry.getValue();
+ }
+
+ mOriginalAllTopics = mAllTopics;
+
+ mTopicsAdapter = new TopicsAdapter();
+ }
+
+ /**
+ * Returns the context associated with this array adapter. The context is used
+ * to create views from the resource passed to the constructor.
+ *
+ * @return The Context associated with this adapter.
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
+ public String getAuthority() {
+ return mAuthority;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getCount() {
+ if (mAlternatives == null)
+ return 0;
+ else
+ return mAlternatives.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Expr getItem(int position) {
+ return mAlternatives.get(position);
+ }
+
+ /**
+ * Returns the position of the specified item in the array.
+ *
+ * @param item The item to retrieve the position of.
+ *
+ * @return The position of the specified item.
+ */
+ public int getPosition(Expr item) {
+ if (mAlternatives == null)
+ return -1;
+ else
+ return mAlternatives.indexOf(item);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getItemId(int position) {
+ return position;
+ }
public View getView(int position, View convertView, ViewGroup parent) {
final Expr expr = getItem(position);
if (mAuthority.equals(Translator.WORDS)) {
if (convertView == null) {
- LayoutInflater inflater = (LayoutInflater)
- getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.lexical_item, null);
+ convertView = mInflater.inflate(R.layout.lexical_item, null);
}
TextView descView = (TextView)
@@ -221,9 +417,7 @@ public class AlternativesActivity extends ListActivity {
});
} else if (mAuthority.equals(Translator.SENTENCES)) {
if (convertView == null) {
- LayoutInflater inflater = (LayoutInflater)
- getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.alternative_item, null);
+ convertView = mInflater.inflate(R.layout.alternative_item, null);
View treeView = (View) convertView.findViewById(R.id.desc_details);
((RelativeLayout) convertView).removeView(treeView);
@@ -283,5 +477,192 @@ public class AlternativesActivity extends ListActivity {
return convertView;
}
+
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ public TopicsAdapter getTopicsAdapter() {
+ return mTopicsAdapter;
+ }
+
+ void filterOnTopics(List selected_topics) {
+ if (mSourceTopic != null && !selected_topics.contains(mSourceTopic))
+ mAlternatives = null;
+
+ if (mAlternatives == null) {
+ if (selected_topics.size() == 0)
+ return;
+
+ mSourceTopic = selected_topics.get(0);
+ mAlternatives = mTranslator.getTopicWords(mSourceTopic.expr);
+
+ mTopics = new ArrayList>();
+ for (Expr e : mAlternatives) {
+ List topics = new ArrayList();
+ for (Expr topicExpr : mTranslator.getTopicsOf(e)) {
+ String name = mTranslator.linearizeSource(topicExpr);
+ String key = name.toLowerCase();
+ topics.add(mTopicMap.get(key));
+ }
+ mTopics.add(topics);
+ }
+ }
+
+ if (mOriginalAlternatives == null) {
+ mOriginalAlternatives = new ArrayList(mAlternatives);
+ }
+
+ mAlternatives = new ArrayList();
+ List> topics = new ArrayList>();
+
+ for (Topic topic : mOriginalAllTopics) {
+ topic.isAvailable = false;
+ }
+
+ int count = 0;
+ for (int i = 0; i < mOriginalAlternatives.size(); i++) {
+ boolean match = true;
+ for (Topic topic : selected_topics) {
+ if (topic == mOtherTopic) {
+ if (mTopics.get(i).size() > 0) {
+ match = false;
+ break;
+ }
+ } else if (!mTopics.get(i).contains(topic)) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ mAlternatives.add(mOriginalAlternatives.get(i));
+ if (mTopics.get(i).size() == 0) {
+ if (!mOtherTopic.isAvailable)
+ count++;
+ mOtherTopic.isAvailable = true;
+ } else {
+ for (Topic topic : mTopics.get(i)) {
+ if (!topic.isAvailable)
+ count++;
+ topic.isAvailable = true;
+ }
+ }
+ }
+ }
+
+ int i = 0;
+ mAllTopics = new Topic[count];
+ for (Topic topic : mOriginalAllTopics) {
+ if (topic.isAvailable)
+ mAllTopics[i++] = topic;
+ }
+
+ notifyDataSetChanged();
+ }
+ }
+
+ private class TopicsAdapter extends BaseAdapter implements ListAdapter {
+ public TopicsAdapter() {
+ }
+
+ public Context getContext() {
+ return mAdapter.getContext();
+ }
+
+ public int getCount() {
+ return mAdapter.mAllTopics.length;
+ }
+
+ public Topic getItem(int position) {
+ return mAdapter.mAllTopics[position];
+ }
+
+ public int getPosition(Topic topic) {
+ for (int i = 0; i < mAdapter.mAllTopics.length; i++) {
+ if (mAdapter.mAllTopics[i] == topic)
+ return i;
+ }
+ return -1;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ // Shame on you Google this class should not have been here
+ // but unfortuantely CheckBox.setChecked doesn't work and here
+ // we need a workarround. Since the class is there now it is also
+ // used to implement OnClickListener.
+ private class TopicCheckBox extends CheckBox implements OnClickListener {
+ private Topic mTopic;
+
+ public TopicCheckBox(Context context, Topic topic) {
+ super(context);
+ mTopic = topic;
+ setOnClickListener(this);
+ }
+
+ @Override
+ public boolean isChecked() {
+ if (mTopic == null)
+ return false;
+ else
+ return mTopic.isChecked;
+ }
+
+ @Override
+ public void onClick(View view) {
+ mTopic.isChecked = !mTopic.isChecked;
+ filterOnTopics();
+ notifyDataSetChanged();
+ }
+ }
+
+ private void filterOnTopics() {
+ List selected_topics = new ArrayList();
+ for (int i = 0; i < getCount(); i++) {
+ Topic topic = getItem(i);
+ if (topic.isChecked)
+ selected_topics.add(topic);
+ }
+ mAdapter.filterOnTopics(selected_topics);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Topic entry = getItem(position);
+
+ CheckBox checkBox = new TopicCheckBox(getContext(), entry);
+ checkBox.setText(entry.name);
+ checkBox.setTextSize(25);
+ checkBox.setTextColor(android.graphics.Color.parseColor("#808080"));
+ return checkBox;
+ }
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Pass the event to ActionBarDrawerToggle, if it returns
+ // true, then it has handled the app icon touch event
+ if (mDrawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
}
}
diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java b/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java
index 9ef344997..1d1bd481d 100644
--- a/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java
+++ b/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java
@@ -191,8 +191,8 @@ public class MainActivity extends Activity {
editor.commit();
return true;
- case R.id.semantic_graph: {
- Intent myIntent = new Intent(MainActivity.this, SemanticGraphActivity.class);
+ case R.id.topics: {
+ Intent myIntent = new Intent(MainActivity.this, AlternativesActivity.class);
MainActivity.this.startActivity(myIntent);
return true;
}
diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java b/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java
index 88884f819..620d42350 100644
--- a/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java
+++ b/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java
@@ -338,11 +338,26 @@ public class Translator {
if (s == null)
s = "% "; // make sure that we return something
- if (getTargetLanguage().getLangCode().equals("cmn-Hans-CN") ||
- getTargetLanguage().getLangCode().equals("ja-JP") ||
- getTargetLanguage().getLangCode().equals("th-TH"))
- return implode(s) ;
- else return s ;
+ if (getTargetLanguage().getLangCode().equals("cmn-Hans-CN") ||
+ getTargetLanguage().getLangCode().equals("ja-JP") ||
+ getTargetLanguage().getLangCode().equals("th-TH"))
+ return implode(s);
+ else
+ return s;
+ }
+
+ public String linearizeSource(Expr expr) {
+ Concr targetLang = getSourceConcr();
+ String s = targetLang.linearize(expr);
+ if (s == null)
+ s = "% "; // make sure that we return something
+
+ if (getTargetLanguage().getLangCode().equals("cmn-Hans-CN") ||
+ getTargetLanguage().getLangCode().equals("ja-JP") ||
+ getTargetLanguage().getLangCode().equals("th-TH"))
+ return implode(s);
+ else
+ return s;
}
public Object[] bracketedLinearize(Expr expr) {
@@ -443,15 +458,34 @@ public class Translator {
}
}
- private Expr getTopicWords(Expr lemma) {
- StringBuilder sbuilder = new StringBuilder();
+ public List getTopicWords(Expr lemma) {
+ TripleResult res = null;
+ List words = new ArrayList();
try {
- TripleResult res = mSGManager.queryTriple(null, topic_pred, lemma);
+ res = mSGManager.queryTriple(null, topic_pred, lemma);
+ while (res.hasNext()) {
+ words.add(res.getSubject());
+ }
+ } catch (IOException e) {
+ // nothing
+ } catch (SGError e) {
+ // nothing
+ } finally {
+ if (res != null)
+ res.close();
+ }
+ return words;
+ }
+
+ private Expr getTopicWordsHtml(Expr lemma) {
+ StringBuilder sbuilder = new StringBuilder();
+ TripleResult res = null;
+ try {
+ res = mSGManager.queryTriple(null, topic_pred, lemma);
Map map = new TreeMap();
while (res.hasNext()) {
updateWordsMap(res.getSubject(), map);
}
- res.close();
StringBuilder builder = new StringBuilder();
buildWordsHtml(map, builder);
@@ -460,6 +494,9 @@ public class Translator {
// nothing
} catch (SGError e) {
// nothing
+ } finally {
+ if (res != null)
+ res.close();
}
return null;
}
@@ -483,9 +520,9 @@ public class Translator {
Expr e = new Expr("MkDocument",
def,
new Expr("Inflection"+cat,lemma),
- getTopicWords(lemma));
+ getTopicWordsHtml(lemma));
String html =
- "" +
+ "" +
targetLang.linearize(e) +
"";
return html;
@@ -495,7 +532,26 @@ public class Translator {
return null;
}
}
-
+
+ public List getTopicsOf(Expr lemma) {
+ TripleResult res = null;
+ List topics = new ArrayList();
+ try {
+ res = mSGManager.queryTriple(lemma, topic_pred, null);
+ while (res.hasNext()) {
+ topics.add(res.getObject());
+ }
+ } catch (IOException e) {
+ // nothing
+ } catch (SGError e) {
+ // nothing
+ } finally {
+ if (res != null)
+ res.close();
+ }
+ return topics;
+ }
+
private static String escapeHtml(CharSequence text) {
StringBuilder out = new StringBuilder();