diff --git a/src/ui/android/AndroidManifest.xml b/src/ui/android/AndroidManifest.xml index d52722356..d8f7cfc2b 100644 --- a/src/ui/android/AndroidManifest.xml +++ b/src/ui/android/AndroidManifest.xml @@ -44,6 +44,9 @@ + + + diff --git a/src/ui/android/assets/phrases.xml b/src/ui/android/assets/phrases.xml new file mode 100644 index 000000000..e3ab5006b --- /dev/null +++ b/src/ui/android/assets/phrases.xml @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ui/android/res/drawable/grid_phrasebook_button.xml b/src/ui/android/res/drawable/grid_phrasebook_button.xml new file mode 100644 index 000000000..21a6ac127 --- /dev/null +++ b/src/ui/android/res/drawable/grid_phrasebook_button.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/src/ui/android/res/drawable/ic_volume_up_black_18dp.png b/src/ui/android/res/drawable/ic_volume_up_black_18dp.png new file mode 100644 index 000000000..992d2c452 Binary files /dev/null and b/src/ui/android/res/drawable/ic_volume_up_black_18dp.png differ diff --git a/src/ui/android/res/layout/activity_navigation.xml b/src/ui/android/res/layout/activity_navigation.xml new file mode 100644 index 000000000..44311b90d --- /dev/null +++ b/src/ui/android/res/layout/activity_navigation.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/src/ui/android/res/layout/advanced_options_button.xml b/src/ui/android/res/layout/advanced_options_button.xml new file mode 100644 index 000000000..140dfebb9 --- /dev/null +++ b/src/ui/android/res/layout/advanced_options_button.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/src/ui/android/res/layout/fragment_translator.xml b/src/ui/android/res/layout/fragment_translator.xml new file mode 100644 index 000000000..5cf529846 --- /dev/null +++ b/src/ui/android/res/layout/fragment_translator.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/ui/android/res/layout/phrase_list_item.xml b/src/ui/android/res/layout/phrase_list_item.xml new file mode 100644 index 000000000..f812ad871 --- /dev/null +++ b/src/ui/android/res/layout/phrase_list_item.xml @@ -0,0 +1,13 @@ + + + + diff --git a/src/ui/android/res/layout/small_fragment_input_holder.xml b/src/ui/android/res/layout/small_fragment_input_holder.xml new file mode 100644 index 000000000..a197ffbf5 --- /dev/null +++ b/src/ui/android/res/layout/small_fragment_input_holder.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/src/ui/android/res/layout/small_fragment_number.xml b/src/ui/android/res/layout/small_fragment_number.xml new file mode 100644 index 000000000..a3cfa07e5 --- /dev/null +++ b/src/ui/android/res/layout/small_fragment_number.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/ui/android/res/layout/small_fragment_options.xml b/src/ui/android/res/layout/small_fragment_options.xml new file mode 100644 index 000000000..bfd69e16f --- /dev/null +++ b/src/ui/android/res/layout/small_fragment_options.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ui/android/res/layout/small_fragment_spinner.xml b/src/ui/android/res/layout/small_fragment_spinner.xml new file mode 100644 index 000000000..acb7caac2 --- /dev/null +++ b/src/ui/android/res/layout/small_fragment_spinner.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/src/ui/android/res/layout/small_fragment_swipe.xml b/src/ui/android/res/layout/small_fragment_swipe.xml new file mode 100644 index 000000000..25fd8113d --- /dev/null +++ b/src/ui/android/res/layout/small_fragment_swipe.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/src/ui/android/res/layout/small_fragment_translation.xml b/src/ui/android/res/layout/small_fragment_translation.xml new file mode 100644 index 000000000..cc4d49053 --- /dev/null +++ b/src/ui/android/res/layout/small_fragment_translation.xml @@ -0,0 +1,45 @@ + + + + + + + + + + diff --git a/src/ui/android/res/menu/main.xml b/src/ui/android/res/menu/main.xml index b1ba5f268..f36095749 100644 --- a/src/ui/android/res/menu/main.xml +++ b/src/ui/android/res/menu/main.xml @@ -1,6 +1,8 @@ + #FFE35900 #ff808080 #ffffffff - \ No newline at end of file + #009688 + #616161 + diff --git a/src/ui/android/res/values/strings.xml b/src/ui/android/res/values/strings.xml index 3b9f828b2..8281a1353 100644 --- a/src/ui/android/res/values/strings.xml +++ b/src/ui/android/res/values/strings.xml @@ -33,4 +33,6 @@ Topics Open topics Close topics + + Phrasebook diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/GFTranslator.java b/src/ui/android/src/org/grammaticalframework/ui/android/GFTranslator.java index ee4cbc092..8900b2415 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/GFTranslator.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/GFTranslator.java @@ -4,13 +4,19 @@ import android.app.Application; public class GFTranslator extends Application { private Translator mTranslator; + private static GFTranslator instance; @Override public void onCreate() { mTranslator = new Translator(this); + instance = this; } public Translator getTranslator() { return mTranslator; } + + public static GFTranslator get() { + return instance; + } } 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 1d1bd481d..369e542e9 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java @@ -191,6 +191,11 @@ public class MainActivity extends Activity { editor.commit(); return true; + case R.id.phrasebook: { + Intent myIntent = new Intent(MainActivity.this, se.chalmers.phrasebook.gui.activities.NavigationActivity.class); + MainActivity.this.startActivity(myIntent); + return true; + } case R.id.topics: { Intent myIntent = new Intent(MainActivity.this, AlternativesActivity.class); MainActivity.this.startActivity(myIntent); diff --git a/src/ui/android/src/se/chalmers/phrasebook/backend/Model.java b/src/ui/android/src/se/chalmers/phrasebook/backend/Model.java new file mode 100644 index 000000000..7ee95064e --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/backend/Model.java @@ -0,0 +1,87 @@ +package se.chalmers.phrasebook.backend; + + +import android.content.Context; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + +import org.grammaticalframework.pgf.Expr; +import org.grammaticalframework.ui.android.R; +import org.grammaticalframework.ui.android.GFTranslator; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; +import se.chalmers.phrasebook.backend.syntax.SyntaxTree; + +/** + * Created by Björn on 2016-03-03. + */ +public class Model { + private static Model model; + + private XMLParser parser; + private ArrayList phrases; + private SyntaxTree currentPhrase; + + private Model() { + try { + InputStream phrases = GFTranslator.get().getAssets().open("phrases.xml"); + parser = new XMLParser(phrases); + phrases.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + phrases = new ArrayList(); + for (String s : parser.getSentencesData().keySet()) { + phrases.add(parser.getSyntaxTree(s)); + } + } + + public static Model getInstance() { + if (model == null) model = getSync(); + return model; + } + + private synchronized static Model getSync() { + if (model == null) model = new Model(); + return model; + } + + public void update(int optionIndex, SyntaxNodeList target, int childIndex, boolean isAdvanced) { + currentPhrase.setSelectedChild(optionIndex, target, childIndex, isAdvanced); + } + + public ArrayList getSentencesInCurrentPhrasebook() { + ArrayList sentences = new ArrayList(); + for (int i = 0; i < phrases.size(); i++) { + sentences.add(parser.getSentencesData().get(phrases.get(i).getId())); + } + return sentences; + } + + public void setCurrentPhrase(int position) { + SyntaxTree choosenPhrase = phrases.get(position); + currentPhrase = parser.getSyntaxTree(choosenPhrase.getId()); + boolean status = currentPhrase.replicate(choosenPhrase); + } + + public String getDescFromPos(int pos) { + return parser.getSentencesData() + .get((String) (parser.getSentencesData().keySet().toArray()[pos])); + } + + public void setNumeralCurrentPhrase() { + for (int i = 0; i < parser.getSentencesData().values().size(); i++) { + if ((parser.getSentencesData().keySet().toArray()[i]).equals("NNumeral")) { + currentPhrase = parser.getSyntaxTree((String) parser.getSentencesData() + .keySet().toArray()[i]); + } + } + } + + public SyntaxTree getCurrentPhrase() { + return currentPhrase; + } +} + diff --git a/src/ui/android/src/se/chalmers/phrasebook/backend/XMLParser.java b/src/ui/android/src/se/chalmers/phrasebook/backend/XMLParser.java new file mode 100644 index 000000000..fd69208c2 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/backend/XMLParser.java @@ -0,0 +1,196 @@ +package se.chalmers.phrasebook.backend; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import se.chalmers.phrasebook.backend.syntax.NumeralSyntaxNode; +import se.chalmers.phrasebook.backend.syntax.SyntaxNode; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; +import se.chalmers.phrasebook.backend.syntax.SyntaxTree; + +/** + * Created by David on 2016-02-19. + */ +public class XMLParser { + + private DocumentBuilder documentBuilder; + private Document document; + private String currentId; + + public XMLParser(InputStream is) { + try { + documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + document = documentBuilder.parse(is); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (SAXException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public HashMap getSentencesData() { + String[] result; + HashMap sentenceMap = new HashMap(); + + NodeList sentences = document.getElementsByTagName("sentence"); + int nbrOfSentences = sentences.getLength(); + + for (int i = 0; i < nbrOfSentences; i++) { + String desc = sentences.item(i).getAttributes().getNamedItem("desc").getNodeValue(); + String id = sentences.item(i).getAttributes().getNamedItem("id").getNodeValue(); + if (desc != null && id != null) + sentenceMap.put(id, desc); + } + return sentenceMap; + } + + public SyntaxTree getAdvancedOptionSyntaxTree() { + NodeList advSentence = document.getElementsByTagName("advanced"); + advSentence = advSentence.item(0).getChildNodes(); + SyntaxTree s = new SyntaxTree(constructSyntaxNodeList(advSentence, new SyntaxNode("Root"), new SyntaxNodeList(), null, 1)); + return s; + } + + + public SyntaxTree getSyntaxTree(String sentenceTitle) { + NodeList result = null; + NodeList nl = document.getElementsByTagName("sentence"); + boolean isAdvanced = false; + String id = ""; + for (int i = 0; i < nl.getLength(); i++) { + NamedNodeMap attr = nl.item(i).getAttributes(); + id = attr.getNamedItem("id").getNodeValue(); + if (nl.item(i).getNodeType() == Node.ELEMENT_NODE && sentenceTitle.equals(id)) { + result = nl.item(i).getChildNodes(); + if (attr.getNamedItem("advanced") != null) isAdvanced = true; + break; + } + } + SyntaxTree s = buildSyntaxTree(result); + s.setId(id); + if (isAdvanced) { + s.setAdvancedTree(getAdvancedOptionSyntaxTree()); + } + return s; + } + + private SyntaxTree buildSyntaxTree(NodeList currentRoot) { + SyntaxTree s = new SyntaxTree(constructSyntaxNodeList(currentRoot, new SyntaxNode("Root"), new SyntaxNodeList(), null, 1)); + return s; + } + + /* + "Abandon all hope, ye who enter here + */ + private SyntaxNode constructSyntaxNodeList(NodeList nl, SyntaxNode parent, SyntaxNodeList list, SyntaxNode nextSequence, int nbrOfArgs) { + if (nl == null || nl.getLength() < 1) { + if (nextSequence != null && !(parent.getData().isEmpty() && nextSequence.getSyntaxNodes().isEmpty())) { + list.add(nextSequence); + parent.getSyntaxNodes().add(list); + } + return null; + } + int length = nl.getLength(); + + //CurrentArgs counts the number of arguments for the current NodeList + int currentArgs = 0; + + //If the parent node, or previous "recursion", calls for multiple args + if (nbrOfArgs > 1) { + currentArgs = nbrOfArgs;////Update currentArgs + nbrOfArgs = 0;//Reset nbrOfArgs, important due to the other recursive calls which will happen before nbrOfArgs is actually used. + } + + int args = 0; + + for (int i = 0; i < length; i++) { + if (nl.item(i) != null && (nl.item(i).getNodeType() == Node.ELEMENT_NODE) && nl.item(i).getAttributes() != null) { + String syntax = "", desc = "", option = "", question = ""; + + NamedNodeMap attributes = nl.item(i).getAttributes(); + if (attributes.getNamedItem("syntax") != null) { + syntax = attributes.getNamedItem("syntax").getNodeValue(); + } + + if (attributes.getNamedItem("desc") != null) { + desc = attributes.getNamedItem("desc").getNodeValue(); + } + + if (attributes.getNamedItem("args") != null) { + args = Integer.parseInt(attributes.getNamedItem("args").getNodeValue()); + nbrOfArgs = args; + } + + if (attributes.getNamedItem("option") != null) { + question = attributes.getNamedItem("option").getNodeValue(); + list.setQuestion(question); + constructSyntaxNodeList(nl.item(i).getChildNodes(), parent, list, nextSequence, nbrOfArgs); + } + + if (attributes.getNamedItem("child") != null) { + option = attributes.getNamedItem("child").getNodeValue(); + + SyntaxNode mNextSequence = new SyntaxNode("");//This is a "holder" node to bind the child function calls nodes, contains no useful information in its syntax. + SyntaxNodeList mList = new SyntaxNodeList(); + constructSyntaxNodeList(nl.item(i).getChildNodes(), mNextSequence, mList, nextSequence, nbrOfArgs); + + constructSyntaxNodeList(jumpToChild("child", option), parent, list, mNextSequence, nbrOfArgs); + } + if (!syntax.isEmpty()) { + SyntaxNode node; + + if (syntax.equals("NNumeral")) { + node = new NumeralSyntaxNode(); + } else { + node = new SyntaxNode(syntax); + node.setDesc(desc); + } + + + list.add(node); + SyntaxNodeList mList = new SyntaxNodeList(); + + constructSyntaxNodeList(nl.item(i).getChildNodes(), node, mList, nextSequence, nbrOfArgs); + } + + //Add the list to the current parent node list + if (!list.getChildren().isEmpty() && !parent.getSyntaxNodes().contains(list)) { + parent.getSyntaxNodes().add(list); + } + //Check if current node is multiple arg nodes i.e. add another list to its syntaxNodes. + if (currentArgs > 1 && parent.getSyntaxNodes().size() < currentArgs) { + list = new SyntaxNodeList(); + } + + } + } + return parent; + + } + + public NodeList jumpToChild(String tag, String id) { + NodeList result = null; + NodeList nl = document.getElementsByTagName(tag); + + for (int i = 0; i < nl.getLength(); i++) { + String s = nl.item(i).getFirstChild().getNodeValue(); + if (nl.item(i).getNodeType() == Node.ELEMENT_NODE && nl.item(i).getAttributes().getNamedItem("id").getNodeValue().equals(id)) { + result = nl.item(i).getChildNodes(); + } + } + return result; + } +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/NumeralSyntaxNode.java b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/NumeralSyntaxNode.java new file mode 100644 index 000000000..812488751 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/NumeralSyntaxNode.java @@ -0,0 +1,104 @@ +package se.chalmers.phrasebook.backend.syntax; + +/** + * Created by Björn on 2016-04-04. + */ +public class NumeralSyntaxNode extends SyntaxNode { + + private int number = 1; + + public NumeralSyntaxNode() { + super("NNumeral"); + } + + @Override + public boolean isModular() { + return true; + } + + public String getData() { + try { + return nbrToSyntax(number); + }catch(IllegalArgumentException e) { + //Returns the syntax for "1" in case of erroneous input. + return "(NNumeral(num (pot2as3 (pot1as2 (pot0as1 pot01)))))"; + } + } + + public String getDesc() { + return Integer.toString(number); + } + + public void setDesc(String number) { + this.number = Integer.parseInt(number); + } + + + @Override + public void setSelectedChild(int listIndex, SyntaxNode newChild) { + setDesc(Integer.toString(listIndex)); + } + + private String nbrToSyntax(int nbr) throws IllegalArgumentException { + String syntax = ""; + if(nbr < 1000000 && nbr > 0) { + if (nbr <=999) { + syntax = "(NNumeral(num(pot2as3 " + subs1000(nbr) + ")))"; + } else if(nbr % 1000 == 0) { + syntax = "(NNumeral(num(pot3 " + subs1000(nbr/1000) + ")))"; + } else if(nbr > 1000 && nbr%1000 != 0) { + syntax = "(NNumeral(num(pot3plus " + subs1000(nbr/1000) + " " + + subs1000(nbr%1000) + ")))"; + } + } else { + throw new IllegalArgumentException("Input must be between 1 and 999999"); + } + return syntax; + } + + private String subs1000(int nbr) { + String syntax = ""; + if(nbr < 100) { + syntax = "(pot1as2 " + subs100(nbr) + ")"; + } else if(nbr % 100 == 0) { + syntax = "(pot2 " + subs10(nbr/100) + ")"; + } else if(nbr > 100 && nbr%100 != 0) { + syntax = "(pot2plus " + subs10(nbr/100) + " " + subs100(nbr%100) + ")"; + } + return syntax; + } + + private String subs100(int nbr) { + String syntax = ""; + if(nbr < 10) { + syntax = "(pot0as1 " + subs10(nbr) + ")"; + } else if(nbr == 10 || nbr == 11) { + syntax = "pot1" + nbr; + } else if(nbr >= 12 && nbr <= 19) { + syntax = "(pot1to19 n" + nbr%10 + ")"; + } else if(nbr >= 20 && nbr%10 == 0) { + syntax = "(pot1 n" + Integer.toString(nbr/10) + ")"; + } else if(nbr%10 != 0) { + syntax = "(pot1plus n" + nbr/10 + " " + subs10(nbr%10) + ")"; + } + return syntax; + } + + private String subs10(int nbr) { + String syntax = ""; + if (nbr == 1) { + syntax = "pot01"; + } else if (nbr >= 2 && nbr <= 9) { + syntax = "(pot0 n" + nbr + ")"; + } + return syntax; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxNode.java b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxNode.java new file mode 100644 index 000000000..d0ec2db3b --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxNode.java @@ -0,0 +1,66 @@ +package se.chalmers.phrasebook.backend.syntax; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * Created by Björn on 2016-03-03. + */ + +public class SyntaxNode implements Serializable { + private String data; + private String desc; + private ArrayList syntaxNodes; + + public SyntaxNode(String data) { + syntaxNodes = new ArrayList(); + this.data = data; + } + + public void setSelectedChild(int listIndex, SyntaxNode newChild) { + syntaxNodes.get(listIndex).setSelectedChild(newChild); + } + + public ArrayList getSyntaxNodes() { + return syntaxNodes; + } + + public ArrayList getModularSyntaxNodes() { + ArrayList result = new ArrayList(); + for (SyntaxNodeList snl : syntaxNodes) { + if (snl.getChildren().size() > 1) result.add(snl); + } + return result; + } + + public String getData() { + return data; + } + + public boolean isModular() { + for (SyntaxNodeList s : syntaxNodes) if (s.getChildren().size() > 1) return true; + return false; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public boolean equals(Object o) { + + if (this == o) { + return true; + } + if (!(o instanceof SyntaxNode)) { + return false; + } + SyntaxNode n = (SyntaxNode) o; + + return this.data.equals(n.data); + } + +} \ No newline at end of file diff --git a/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxNodeList.java b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxNodeList.java new file mode 100644 index 000000000..d759f72f2 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxNodeList.java @@ -0,0 +1,62 @@ +package se.chalmers.phrasebook.backend.syntax; + +import java.io.Serializable; +import java.util.ArrayList; + +import se.chalmers.phrasebook.backend.syntax.SyntaxNode; + +/** + * Created by David on 2016-04-01. + */ +public class SyntaxNodeList implements Serializable { + private SyntaxNode selectedChild; + private ArrayList children; + private String question; + + + public SyntaxNodeList() { + children = new ArrayList(); + } + + public ArrayList getChildren() { + return children; + } + + public boolean add(SyntaxNode object) { + if (selectedChild == null) + selectedChild = object; + return children.add(object); + } + + public SyntaxNode getSelectedChild() { + return selectedChild; + } + + public boolean setSelectedChild(SyntaxNode selectedChild) { + if(children.contains(selectedChild)) { + this.selectedChild = selectedChild; + return true; + } + return false; + } + + public boolean selectChild(String description) { + for (SyntaxNode s : children) { + if (s.getDesc().equals(description)) { + setSelectedChild(s); + return true; + } + } + return false; + } + + public String getQuestion() { + return question; + } + + public void setQuestion(String question) { + this.question = question; + } + + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxTree.java b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxTree.java new file mode 100644 index 000000000..26d57dc64 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/backend/syntax/SyntaxTree.java @@ -0,0 +1,224 @@ +package se.chalmers.phrasebook.backend.syntax; + + +import java.io.Serializable; +import java.util.ArrayList; +import org.grammaticalframework.pgf.Expr; + +/** + * Created by Björn on 2016-02-26. + */ +public class SyntaxTree implements Serializable { + + private String id; + private SyntaxNode root; + private boolean advActivated; + private boolean favorite; + + private ArrayList options = new ArrayList(); + + private SyntaxTree advancedTree; //Realy ugly solution but the only one we can think of as + + public SyntaxTree getAdvancedTree() { + return advancedTree; + } + + public void setAdvancedTree(SyntaxTree advancedTree) { + this.advancedTree = advancedTree; + } + + public SyntaxTree(SyntaxNode root) { + this.root = root; + advActivated = false; + initializeOptions(this.root); + } + + public ArrayList getOptions() { + return options; + } + + public ArrayList getAdvOptions() { + if(advancedTree != null) { + return advancedTree.getOptions(); + } + return new ArrayList(); + } + + public boolean getFavorite() { + return favorite; + } + + public void setFavorite(boolean fav) { + favorite = fav; + } + public boolean replicate(SyntaxTree tree) { + if(this.getOptions() == null) { + return false; + } else { + for(int i = 0; i < getOptions().size(); i++) { + for(SyntaxNode n: getOptions().get(i).getChildren()) { + + if(n.getData().equals(tree.getOptions().get(i).getSelectedChild().getData())) { + this.setSelectedChild(getOptions().get(i), n); + } else if(n instanceof NumeralSyntaxNode) { + ((NumeralSyntaxNode) n).setDesc + (Integer.toString(((NumeralSyntaxNode)tree.getOptions() + .get(i).getSelectedChild()).getNumber())); + } + } + } + } + tree.setFavorite(getFavorite()); + if(advancedTree != null) { + if(tree.advActivated) { + setAdvActivated(true); + return advancedTree.replicate(tree.getAdvancedTree()); + } + } + return false; + } + + public void setId(String id) { + this.id = id; + } + + //creates an ArrayList of LinkedHashMaps, each representing + //a currently available option to be customized. + private void initializeOptions(SyntaxNode currentRoot) { + if (currentRoot == null) return; + if (currentRoot.isModular()) { + for (SyntaxNodeList l : currentRoot.getSyntaxNodes()) { + if (l.getQuestion() != null && !options.contains(l)) { + options.add(l); + } + initializeOptions(l.getSelectedChild()); + } + } else if (currentRoot.getSyntaxNodes() != null && currentRoot.getSyntaxNodes().size() > 0) { + for (SyntaxNodeList n : currentRoot.getSyntaxNodes()) { + initializeOptions(n.getSelectedChild()); + } + } + } + + public String getId() { + return id; + } + + public boolean setSelectedChild(int optionIndex, int childIndex, boolean isAdvanced) { + boolean status = false; + if(!isAdvanced) { + if (options.get(optionIndex).getSelectedChild() instanceof NumeralSyntaxNode) { + options.get(optionIndex).getSelectedChild().setSelectedChild(childIndex, null); + status = true; + } else if (options.get(optionIndex) != null) { + if (options.get(optionIndex).getChildren().get(childIndex) != null) + status = options.get(optionIndex).setSelectedChild(options.get(optionIndex).getChildren().get(childIndex)); + } + } else { + advancedTree.setSelectedChild(optionIndex,childIndex,false); + } + + return status; + } + + + public void setSelectedChild(int optionIndex, SyntaxNodeList snl, int childIndex, boolean isAdvanced) { + if(!isAdvanced) { + if (snl != null) { + SyntaxNodeList nodeList = options.get(optionIndex); + setRecursiveSelectedChild(nodeList, snl, childIndex); + } else { + setSelectedChild(optionIndex, childIndex,false); + } + } else { + advancedTree.setSelectedChild(optionIndex,snl,childIndex,false); + } + + } + + + private void setRecursiveSelectedChild(SyntaxNodeList nodeList, SyntaxNodeList optionTarget, int childIndex) { + + if (nodeList.equals(optionTarget)) { + setSelectedChild(nodeList, nodeList.getChildren().get(childIndex)); + } else { + for (SyntaxNodeList list : nodeList.getSelectedChild().getSyntaxNodes()) + setRecursiveSelectedChild(list, optionTarget, childIndex); + } + + } + + + public boolean setSelectedChild(SyntaxNodeList l, SyntaxNode s) { + return l.setSelectedChild(s); + } + + /** + * Parses the selected children into a text syntax usable by the grammar to + * generate a translation. Builds recursivly. + * + * @return The syntax usable by the GF-grammar to generate a translation + */ + public String getSyntax() { + return parseSentenceSyntax(getSentenceHead()); + } + + //TODO A really ugly hack, should DEFINITIVELY BE FIXED + public Expr getAdvSyntax() { + String syntax = getSyntax(); + if(advActivated) { + String advSyntax = advancedTree.getSyntax(); + if(!advSyntax.isEmpty()) { + String test = advSyntax.substring(0, advSyntax.indexOf("AKnow") + 5) + + syntax.substring(1, 9) + "(" + + advSyntax.substring(advSyntax.indexOf("AKnow") + 6, advSyntax.length() - 3) + + syntax.substring(9, syntax.length()) + ")))"; + return Expr.readExpr(test); + } + } + return Expr.readExpr(syntax); + } + + public boolean hasAdvOptions() { + return (advancedTree != null); + } + + // Builds recursively from root node to parse syntax + //the getSyntax() method acts as a wrapper + private String parseSentenceSyntax(SyntaxNode node) { + + if (node.getSyntaxNodes().size() < 1) { + return node.getData(); + } else { + String syntax = node.getData(); + for (int i = 0; i < node.getSyntaxNodes().size(); i++) { + if (node.getSyntaxNodes().get(i).getSelectedChild().getData().isEmpty()) { + syntax += parseSentenceSyntax(node.getSyntaxNodes().get(i).getSelectedChild()); + } else { + syntax = syntax + "(" + parseSentenceSyntax(node.getSyntaxNodes().get(i).getSelectedChild()) + ")"; + if (node.getSyntaxNodes().size() > 1) { + syntax += " "; + } + + } + } + return syntax; + } + } + + private SyntaxNode getSentenceHead() { + if (root.getSyntaxNodes().get(0) != null) + return root.getSyntaxNodes().get(0).getSelectedChild();//TODO Might cause bugs + return null; + } + + public boolean isAdvActivated(){ + return advActivated; + } + + public void setAdvActivated(boolean activated){ + advActivated = activated; + + } + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/FragmentCommunicator.java b/src/ui/android/src/se/chalmers/phrasebook/gui/FragmentCommunicator.java new file mode 100644 index 000000000..3b45f4175 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/FragmentCommunicator.java @@ -0,0 +1,17 @@ +package se.chalmers.phrasebook.gui; + +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; + +/** + * An interface for communication between a fragment to an activity + * Created by David on 2016-04-13. + */ +public interface FragmentCommunicator { + void updateSyntax(int optionIndex, SyntaxNodeList l, int childIndex, boolean isAdvanced); + + void pageChanged(); + + void setToTranslationFragment(int id); + + void updateTranslation(); +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/activities/NavigationActivity.java b/src/ui/android/src/se/chalmers/phrasebook/gui/activities/NavigationActivity.java new file mode 100644 index 000000000..fc48a9cb1 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/activities/NavigationActivity.java @@ -0,0 +1,100 @@ +package se.chalmers.phrasebook.gui.activities; + +import android.app.ActionBar; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.LocalBroadcastManager; +import android.support.v4.widget.DrawerLayout; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; +import se.chalmers.phrasebook.gui.FragmentCommunicator; +import se.chalmers.phrasebook.gui.fragments.NumeralTranslatorFragment; +import se.chalmers.phrasebook.gui.fragments.PhraseListFragment; +import se.chalmers.phrasebook.gui.fragments.TranslatorFragment; + +public class NavigationActivity extends FragmentActivity implements FragmentCommunicator { + + /** + * Used to store the last screen title. For use in {@link #restoreActionBar()}. + */ + private CharSequence mTitle; + private Fragment mContent; + + public void pageChanged() { + if (mContent instanceof TranslatorFragment) + ((TranslatorFragment) mContent).displayDots(); + } + + private Model mModel; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_navigation); + + mModel = Model.getInstance(); + mTitle = getTitle(); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.container, PhraseListFragment.newInstance("Phrasebook")); + transaction.commit(); + } + + public void switchContent(Fragment fragment, String message) { + mContent = fragment; + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.container, fragment); + transaction.addToBackStack(message); + transaction.commit(); + } + + public void restoreActionBar() { + ActionBar actionBar = getActionBar(); + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setTitle(mTitle); + } + + @Override + public void onBackPressed() { + if (getSupportFragmentManager().getBackStackEntryCount() > 1) { + getSupportFragmentManager().popBackStack(); + getSupportFragmentManager().beginTransaction().commit(); + //Switches to the previous entry on the stack to ensure + //that mContent is preserved + mContent = getSupportFragmentManager().getFragments() + .get(getSupportFragmentManager().getFragments().size() - 2); + } else { + super.onBackPressed(); + } + } + + @Override + public void updateSyntax(int optionIndex, SyntaxNodeList l, int childIndex, boolean isAdvanced) { + mModel.update(optionIndex, l, childIndex, isAdvanced); + updateTranslation(); + } + + public void updateTranslation() { + if (mContent instanceof TranslatorFragment) { + TranslatorFragment fragment = (TranslatorFragment) mContent; + fragment.updateTranslation(); + } + } + + @Override + public void setToTranslationFragment(int id) { + switchContent(TranslatorFragment.newInstance(id + ""), ""); + } +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/adapters/SwipeAdapter.java b/src/ui/android/src/se/chalmers/phrasebook/gui/adapters/SwipeAdapter.java new file mode 100644 index 000000000..957c0848b --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/adapters/SwipeAdapter.java @@ -0,0 +1,59 @@ +package se.chalmers.phrasebook.gui.adapters; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.view.ViewGroup; + +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.gui.FragmentCommunicator; +import se.chalmers.phrasebook.gui.smallFragments.OptionsFragment; + +/** + * Created by matilda on 15/03/16. + */ +public class SwipeAdapter extends FragmentPagerAdapter { + + private int pages; + private FragmentCommunicator mCallback; + private Model model; + + public SwipeAdapter(FragmentManager fragmentManager, FragmentCommunicator mCallback) { + super(fragmentManager); + this.mCallback = mCallback; + model = Model.getInstance(); + if(model.getCurrentPhrase().hasAdvOptions()) { + pages = 2; + } else { + pages = 1; + } + } + + + // Returns total number of pages + @Override + public int getCount() { + return pages; + } + + @Override + public void startUpdate(ViewGroup container) { + mCallback.pageChanged(); + } + + // Returns the fragment to display for that page + @Override + public Fragment getItem(int position) { + + switch(position){ + case 0: + return OptionsFragment.newInstance(1, false); + case 1: + return OptionsFragment.newInstance(2, model.getCurrentPhrase().isAdvActivated()); + default: + return null; + } + + } + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/NumeralTranslatorFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/NumeralTranslatorFragment.java new file mode 100644 index 000000000..59d0646ff --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/NumeralTranslatorFragment.java @@ -0,0 +1,36 @@ +package se.chalmers.phrasebook.gui.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import se.chalmers.phrasebook.backend.Model; + +/** + * Created by Björn on 2016-04-25. + */ +public class NumeralTranslatorFragment extends TranslatorFragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + model = Model.getInstance(); + } + + public static NumeralTranslatorFragment newInstance() { + NumeralTranslatorFragment fragment = new NumeralTranslatorFragment(); + Bundle args = new Bundle(); + args.putString("phrase", "NNumeral"); + fragment.setArguments(args); + return fragment; + } + + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceStat) { + View view = super.onCreateView(inflater, container, + savedInstanceStat); + super.model.setNumeralCurrentPhrase(); + return view; + } +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/PhraseListFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/PhraseListFragment.java new file mode 100644 index 000000000..ef426fb8d --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/PhraseListFragment.java @@ -0,0 +1,111 @@ +package se.chalmers.phrasebook.gui.fragments; + +import android.os.Bundle; + +import java.util.ArrayList; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.content.LocalBroadcastManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import java.util.ArrayList; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.backend.syntax.SyntaxTree; +import se.chalmers.phrasebook.gui.FragmentCommunicator; +import se.chalmers.phrasebook.gui.activities.NavigationActivity; + + +/** + * Created by Björn on 2016-04-25. + */ +public class PhraseListFragment extends Fragment { + + protected Model model; + protected ArrayList phrases; + Context context; + private String title; + + private FragmentCommunicator mCallback; + + public static PhraseListFragment newInstance(String title) { + PhraseListFragment fragment = new PhraseListFragment(); + Bundle args = new Bundle(); + args.putString("title", title); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mCallback = (FragmentCommunicator) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnHeadlineSelectedListener"); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + model = Model.getInstance(); + context = getActivity().getApplicationContext(); + + title = getArguments().getString("title"); + phrases = new ArrayList(); + phrases.addAll(model.getSentencesInCurrentPhrasebook()); + } + + @Override + public void onResume() { + super.onResume(); + phrases.clear(); + phrases.addAll(model.getSentencesInCurrentPhrasebook()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.fragment_phrase_list, container, false); + getActivity().getActionBar().setTitle(title); + ArrayAdapter adapter = new ArrayAdapter(context, R.layout.phrase_list_item, phrases); + + final ListView phraseListView = (ListView) view.findViewById(R.id.phrase_listView); + phraseListView.setAdapter(adapter); + + phraseListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + + model.setCurrentPhrase(position); + ((NavigationActivity)mCallback).getActionBar().setTitle(model.getDescFromPos(position)); + sendMessage(position); + } + }); + + + return view; + } + + + private void sendMessage(int position) { + mCallback.setToTranslationFragment(position); + } +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/TranslatorFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/TranslatorFragment.java new file mode 100644 index 000000000..c1a1903a3 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/fragments/TranslatorFragment.java @@ -0,0 +1,81 @@ +package se.chalmers.phrasebook.gui.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import org.grammaticalframework.ui.android.GFTranslator; +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.gui.smallFragments.SwipeFragment; +import se.chalmers.phrasebook.gui.smallFragments.TranslationFragment; + +/** + * Created by matilda on 04/04/16. + */ +public class TranslatorFragment extends Fragment { + protected Model model; + private SwipeFragment swiper; + private View view; + + public static TranslatorFragment newInstance(String phrase) { + TranslatorFragment fragment = new TranslatorFragment(); + Bundle args = new Bundle(); + args.putString("phrase", phrase); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + model = Model.getInstance(); + } + + public void displayDots() { + if(swiper.isAdvanced(view.findViewById(R.id.containerfor_options))) { + ((ImageView)view.findViewById(R.id.firstDot)) + .setImageResource(R.drawable.ic_dictionary); + ((ImageView)view.findViewById(R.id.secondDot)) + .setImageResource(R.drawable.ic_dictionary); + } else { + ((ImageView)view.findViewById(R.id.secondDot)) + .setImageResource(R.drawable.ic_dictionary); + ((ImageView)view.findViewById(R.id.firstDot)) + .setImageResource(R.drawable.ic_dictionary); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + view = inflater.inflate(R.layout.fragment_translator, container, false); + + final FragmentTransaction fm = getChildFragmentManager().beginTransaction(); + + swiper = new SwipeFragment(); + fm.replace(R.id.containerfor_translation, new TranslationFragment()); + fm.replace(R.id.containerfor_options, new SwipeFragment()); + + fm.commit(); + + if(!model.getCurrentPhrase().hasAdvOptions()) { + view.findViewById(R.id.firstDot).setVisibility(View.GONE); + view.findViewById(R.id.secondDot).setVisibility(View.GONE); + } + + return view; + } + + public void updateTranslation() { + TranslationFragment translationFragment = (TranslationFragment) getChildFragmentManager().findFragmentById(R.id.containerfor_translation); + if (translationFragment != null) + translationFragment.updateData(); + } +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/AdvancedOptionsButtonFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/AdvancedOptionsButtonFragment.java new file mode 100644 index 000000000..309c96351 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/AdvancedOptionsButtonFragment.java @@ -0,0 +1,91 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.app.Activity; +import android.support.v4.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.gui.FragmentCommunicator; +import se.chalmers.phrasebook.gui.fragments.TranslatorFragment; + + +/** + * Created by matilda on 02/05/16. + */ +public class AdvancedOptionsButtonFragment extends Fragment{ + + private boolean active; + private Model model; + private FragmentCommunicator mCallback; + + public static Fragment newInstance(boolean active) { + AdvancedOptionsButtonFragment advancedOptionsButtonFragment = new AdvancedOptionsButtonFragment(); + + + Bundle args = new Bundle(); + args.putBoolean("active", active); + advancedOptionsButtonFragment.setArguments(args); + + return advancedOptionsButtonFragment; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + model = Model.getInstance(); + this.active = getArguments().getBoolean("active"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.advanced_options_button, container, false); + + CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox); + + checkBox.setText("Use reported speech ('I Know that...')"); + + if(active != true){ + checkBox.setChecked(false); + } else { + checkBox.setChecked(true); + } + + checkBox.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (((CheckBox) v).isChecked()) { + model.getCurrentPhrase().setAdvActivated(true); + ((OptionsFragment)getParentFragment()).update(true); + } else { + model.getCurrentPhrase().setAdvActivated(false); + ((OptionsFragment)getParentFragment()).update(false); + } + mCallback.updateTranslation(); + } + }); + + return view; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mCallback = (FragmentCommunicator) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnHeadlineSelectedListener"); + } + } + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/InputHolderFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/InputHolderFragment.java new file mode 100644 index 000000000..e7a375ce1 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/InputHolderFragment.java @@ -0,0 +1,167 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.ArrayList; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.backend.syntax.NumeralSyntaxNode; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; +import se.chalmers.phrasebook.gui.FragmentCommunicator; + +/** + * Created by David on 2016-04-13. + */ + + +public class InputHolderFragment extends Fragment { + private Model model; + + private int optionIndex; + private SyntaxNodeList guiOptions; + private FragmentManager fragmentManager; + private ArrayList fragmentTags; + private boolean isAdvanced; + + private FragmentCommunicator mCallback; + + public static Fragment newInstance(int optionIndex, boolean isAdvanced) { + InputHolderFragment inputHolderFragment = new InputHolderFragment(); + + Bundle args = new Bundle(); + args.putInt("index", optionIndex); + args.putBoolean("advanced", isAdvanced); + inputHolderFragment.setArguments(args); + + return inputHolderFragment; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mCallback = (FragmentCommunicator) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnHeadlineSelectedListener"); + } + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.isAdvanced = getArguments().getBoolean("advanced"); + model = Model.getInstance(); + optionIndex = getArguments().getInt("index"); + fragmentManager = getChildFragmentManager(); + if (isAdvanced) { + guiOptions = model.getCurrentPhrase().getAdvOptions().get(optionIndex); + } else { + guiOptions = model.getCurrentPhrase().getOptions().get(optionIndex); + } + fragmentTags = new ArrayList(); + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.small_fragment_input_holder, container, false); + TextView textView = (TextView) view.findViewById(R.id.holderOptionText); + + textView.setText(guiOptions.getQuestion()); + addInputFragments(guiOptions); + + return view; + } + + private void addListInputFragment(SyntaxNodeList l) { + FragmentTransaction transaction = fragmentManager.beginTransaction(); + String s = "Input: " + l.toString(); + + transaction.add(R.id.input_holder, SpinnerInputFragment.newInstance(optionIndex, null, l), s); + fragmentTags.add(s); + transaction.commit(); + } + + private void addNumberInputFragment(SyntaxNodeList l) { + FragmentTransaction transaction = fragmentManager.beginTransaction(); + String s = "Input: " + l.toString(); + + int defaultIntValue = 0; + // String title = l.getQuestion(); + String title = ""; + NumeralSyntaxNode nsn = (NumeralSyntaxNode) l.getSelectedChild(); + defaultIntValue = nsn.getNumber(); + + transaction.replace(R.id.input_holder, NumberInputFragment.newInstance(optionIndex, title, defaultIntValue), s); + + fragmentTags.add(s); + transaction.commit(); + + } + + private void redrawInputGUI() { + clearInputs(); + addInputFragments(guiOptions); + } + + private void addInputFragments(SyntaxNodeList snl) { + if (snl != null) { + + if (snl.getSelectedChild() instanceof NumeralSyntaxNode) { + addNumberInputFragment(snl); + } else { + addListInputFragment(snl); + } + + + if (snl.getSelectedChild().isModular()) { + ArrayList modularLists = snl.getSelectedChild().getModularSyntaxNodes(); + for (SyntaxNodeList nodeList : modularLists) + addInputFragments(nodeList); + } + } + } + + private void clearInputs() { + FragmentTransaction transaction = fragmentManager.beginTransaction(); + for (String tag : fragmentTags) { + Fragment fragment = fragmentManager.findFragmentByTag(tag); + transaction.remove(fragment); + } + fragmentTags.clear(); + transaction.commit(); + } + + + public void updateSyntax(int optionIndex, SyntaxNodeList l, int childIndex) { + if (this.optionIndex == optionIndex) { + mCallback.updateSyntax(optionIndex, l, childIndex, isAdvanced); + this.redrawInputGUI(); + } + } + + public void updateNumeralSyntax(int optionIndex, int childIndex) { + + if (this.optionIndex == optionIndex) { + mCallback.updateSyntax(optionIndex, null, childIndex, false); + } + this.redrawInputGUI(); + } + + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/NumberInputFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/NumberInputFragment.java new file mode 100644 index 000000000..4a596c15e --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/NumberInputFragment.java @@ -0,0 +1,147 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.content.Context; +import android.support.v4.app.Fragment; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.text.method.PasswordTransformationMethod; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.SeekBar; +import android.widget.Spinner; +import android.widget.TextView; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.backend.syntax.SyntaxNode; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; + +/** + * Created by David on 2016-04-07. + */ +public class NumberInputFragment extends Fragment { + + private Model model; + private int spinnerIndex; + private SyntaxNodeList options; + + private int optionIndex; + private String label; + private int defaultInt; + private EditText editNumber; + private int currentNumber; + + public static NumberInputFragment newInstance(int optionIndex, String title, int defaultInt) { + NumberInputFragment numberInputFragment = new NumberInputFragment(); + Bundle args = new Bundle(); + + args.putInt("optionIndex", optionIndex); + args.putString("title", title); + args.putInt("defaultInt", defaultInt); + + numberInputFragment.setArguments(args); + return numberInputFragment; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + model = Model.getInstance(); + optionIndex = getArguments().getInt("optionIndex"); + label = getArguments().getString("title"); + defaultInt = getArguments().getInt("defaultInt"); + } + + private class NumericKeyBoardTransformationMethod extends PasswordTransformationMethod { + @Override + public CharSequence getTransformation(CharSequence source, View view) { + return source; + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.small_fragment_number, container, false); + TextView viewLabel = (TextView) view.findViewById(R.id.textView_number); + final SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekBar); + editNumber = (EditText) view.findViewById(R.id.editNumber); + + viewLabel.setText(label); + seekBar.setProgress(defaultInt); + editNumber.setText(""+defaultInt); + + editNumber.setTransformationMethod(new NumericKeyBoardTransformationMethod()); + editNumber.requestFocus(); + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + currentNumber = progress; + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + editNumber.setText(""+currentNumber); + + sendMessage(optionIndex, currentNumber); + + } + }); + + + editNumber.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if(editNumber.getText().toString().equals("")) { + currentNumber = 0; + } else if(editNumber.getText().toString().length() < 7){ + currentNumber = Integer.parseInt(editNumber.getText().toString()); + editNumber.setInputType(0); + sendMessage(optionIndex, currentNumber); + + } else { + editNumber.setText(""+editNumber.getText().toString() + .substring(0,editNumber.getText().toString().length()-1)); + } + seekBar.setProgress(currentNumber); + + } + }); + + return view; + } + + private void sendMessage(int optionIndex, int childIndex) { + + InputHolderFragment fragment = (InputHolderFragment) getParentFragment(); + //options är ju tom i det här fragmentet, hur ska vi lösa det? + fragment.updateSyntax(optionIndex, options,childIndex); + + } + + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/OptionsFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/OptionsFragment.java new file mode 100644 index 000000000..3c00b5a93 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/OptionsFragment.java @@ -0,0 +1,107 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; + +public class OptionsFragment extends Fragment { + + private Model model; + + private ArrayList options; + private ArrayList advancedOptions; + + private int type; + private boolean advActive; + + private int[] containers; + + public static OptionsFragment newInstance(int type, boolean advActive) { + OptionsFragment optionsFragment = new OptionsFragment(); + Bundle args = new Bundle(); + args.putInt("index", type); + args.putBoolean("advActive", advActive); + optionsFragment.setArguments(args); + return optionsFragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + model = Model.getInstance(); + + type = getArguments().getInt("index"); + advActive = getArguments().getBoolean("advActive"); + + options = model.getCurrentPhrase().getOptions(); + advancedOptions = model.getCurrentPhrase().getAdvOptions(); + containers = new int[6]; + + addContainers(); + addFragments(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + return inflater.inflate(R.layout.small_fragment_options, container, false); + + } + + private void addFragments() { + + FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); + + if (type == 1) { + for (int i = 0; i < options.size(); i++) { + if (options.get(i) != null) { + transaction.replace(containers[i], (InputHolderFragment.newInstance(i, false))); + } + } + } else if (type == 2) { + transaction.replace(containers[0], AdvancedOptionsButtonFragment.newInstance(advActive)); + if (advActive) { + for (int i = 0; i < advancedOptions.size(); i++) { + if (advancedOptions.get(i) != null) + transaction.replace(containers[i + 1], InputHolderFragment.newInstance(i, true)); + } + } else { + //Replaces with empty fragments + for (int i = 0; i < advancedOptions.size(); i++) { + if (advancedOptions.get(i) != null) + transaction.replace(containers[i + 1], new Fragment()); + } + } + } + transaction.commit(); + } + + public void update(boolean advActive) { + this.advActive = advActive; + addFragments(); + } + + private void addContainers() { + + containers[0] = R.id.child_fragment1; + containers[1] = R.id.child_fragment2; + containers[2] = R.id.child_fragment3; + containers[3] = R.id.child_fragment4; + containers[4] = R.id.child_fragment5; + containers[5] = R.id.child_fragment6; + + } + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/SpinnerInputFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/SpinnerInputFragment.java new file mode 100644 index 000000000..53aecccda --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/SpinnerInputFragment.java @@ -0,0 +1,124 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.backend.Model; +import se.chalmers.phrasebook.backend.syntax.SyntaxNode; +import se.chalmers.phrasebook.backend.syntax.SyntaxNodeList; +import se.chalmers.phrasebook.gui.FragmentCommunicator; + +/** + * Created by matilda on 14/03/16. + */ +public class SpinnerInputFragment extends Fragment { + + private Model model; + private int spinnerIndex; + private SyntaxNodeList options; + + private String label; + private String currentChoice; + private Spinner spinner; + + + public static SpinnerInputFragment newInstance(int optionIndex, String title, SyntaxNodeList options) { + SpinnerInputFragment spinnerInputFragment = new SpinnerInputFragment(); + Bundle args = new Bundle(); + + args.putInt("index", optionIndex); + args.putString("title", title); + args.putSerializable("spinner_options", options); + + spinnerInputFragment.setArguments(args); + return spinnerInputFragment; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + model = Model.getInstance(); + + label = getArguments().getString("title"); + spinnerIndex = getArguments().getInt("index"); + options = (SyntaxNodeList) getArguments().getSerializable("spinner_options"); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.small_fragment_spinner, container, false); + TextView viewLabel = (TextView) view.findViewById(R.id.text_view_spinner); + spinner = (Spinner) view.findViewById(R.id.choice_spinner); + + + if (label == null) { + viewLabel.setVisibility(View.GONE); + } else { + viewLabel.setText(label); + } + + String[] children = new String[options.getChildren().size()]; + + int i = 0; + int selectedIndex = 0; + for (SyntaxNode s : options.getChildren()) { + if(s == options.getSelectedChild()) + selectedIndex = i; + children[i] = s.getDesc(); + i++; + } + + final ArrayAdapter adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1,children); + adapter.setDropDownViewResource(android.R.layout.simple_list_item_1); + spinner.setAdapter(adapter); + spinner.setSelection(selectedIndex); + currentChoice = spinner.getSelectedItem().toString(); + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (!spinner.getSelectedItem().toString().equals(currentChoice)) { + sendMessage(spinnerIndex, spinner.getSelectedItemPosition()); + currentChoice = spinner.getSelectedItem().toString(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + return view; + + } + + private void sendMessage(int optionIndex, int childIndex) { + + InputHolderFragment fragment = (InputHolderFragment) getParentFragment(); + + fragment.updateSyntax(optionIndex, options,childIndex); + + +// Intent intent = new Intent(); +// intent.setAction("gui_update"); +// intent.putExtra("optionIndex", optionIndex); +// intent.putExtra("childIndex", childIndex); +// +// LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).sendBroadcast(intent); + + } + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/SwipeFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/SwipeFragment.java new file mode 100644 index 000000000..46feaaad0 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/SwipeFragment.java @@ -0,0 +1,59 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.grammaticalframework.ui.android.R; +import se.chalmers.phrasebook.gui.FragmentCommunicator; +import se.chalmers.phrasebook.gui.adapters.SwipeAdapter; + + +public class SwipeFragment extends Fragment { + + private ViewPager pager; + private SwipeAdapter swipeAdapter; + private FragmentCommunicator mCallback; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.small_fragment_swipe, container, false); + + pager = (ViewPager)view.findViewById(R.id.vpPager); + + swipeAdapter = new SwipeAdapter(getChildFragmentManager(), mCallback); + + pager.setAdapter(swipeAdapter); + return view; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + System.out.println("Attaching"); + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mCallback = (FragmentCommunicator) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnHeadlineSelectedListener"); + } + } + + public boolean isAdvanced(View view) { + pager = (ViewPager)view.findViewById(R.id.vpPager); + try { + return ((ViewPager)view.findViewById(R.id.vpPager)).getCurrentItem() == 1; + }catch(NullPointerException e) { + return false; + } + } + +} diff --git a/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/TranslationFragment.java b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/TranslationFragment.java new file mode 100644 index 000000000..933277379 --- /dev/null +++ b/src/ui/android/src/se/chalmers/phrasebook/gui/smallFragments/TranslationFragment.java @@ -0,0 +1,83 @@ +package se.chalmers.phrasebook.gui.smallFragments; + +import android.support.v4.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.ImageView; + +import org.grammaticalframework.pgf.Expr; +import org.grammaticalframework.ui.android.Translator; +import org.grammaticalframework.ui.android.GFTranslator; +import org.grammaticalframework.ui.android.TTS; +import org.grammaticalframework.ui.android.R; + +import se.chalmers.phrasebook.backend.Model; + +/** + * Created by matilda on 10/03/16. + */ +public class TranslationFragment extends Fragment { + + private View translateView; + private Model mModel; + private TextView origin,target; + Translator mTranslator; + + private TTS mTts; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mModel = Model.getInstance(); + mTranslator = ((GFTranslator) getContext().getApplicationContext()).getTranslator(); + mTts = new TTS(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + translateView = inflater.inflate(R.layout.small_fragment_translation, container, false); + + origin = (TextView) translateView.findViewById(R.id.origin_phrase); + target = (TextView) translateView.findViewById(R.id.target_phrase); + + ImageView button = (ImageView) translateView.findViewById(R.id.button3); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mTts.speak(mTranslator.getTargetLanguage().getLangCode(), getTargetTranslation()); + } + }); + + return translateView; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + updateData(); + } + + @Override + public void onDestroy() { + if (mTts != null) { + mTts.destroy(); + mTts = null; + } + super.onDestroy(); + } + + public String getTargetTranslation() { + return target.getText().toString(); + } + + public void updateData() { + Expr expr = mModel.getCurrentPhrase().getAdvSyntax(); + origin.setText(mTranslator.linearizeSource(expr)); + target.setText(mTranslator.linearize(expr)); + } +}