diff options
author | Daniel Gultsch <daniel.gultsch@rwth-aachen.de> | 2014-03-21 22:16:39 +0100 |
---|---|---|
committer | Daniel Gultsch <daniel.gultsch@rwth-aachen.de> | 2014-03-21 22:16:39 +0100 |
commit | df9db8c8b61ae090309933a8dff395e690ff9ba3 (patch) | |
tree | c58a2630c4543fca8aeb191acbfc33c9a999e89f | |
parent | f1c8b0e1b8bb16cceb32b12403166775b639f53a (diff) | |
parent | a9e34d34926d7df3fb01143d6733b481c31c292d (diff) |
Merge branch 'development'
-rw-r--r-- | res/values/arrays.xml | 19 | ||||
-rw-r--r-- | res/xml/preferences.xml | 16 | ||||
-rw-r--r-- | src/eu/siacs/conversations/crypto/OtrEngine.java | 7 | ||||
-rw-r--r-- | src/eu/siacs/conversations/entities/Account.java | 6 | ||||
-rw-r--r-- | src/eu/siacs/conversations/entities/Conversation.java | 1 | ||||
-rw-r--r-- | src/eu/siacs/conversations/services/XmppConnectionService.java | 48 | ||||
-rw-r--r-- | src/eu/siacs/conversations/ui/ConversationFragment.java | 84 | ||||
-rw-r--r-- | src/eu/siacs/conversations/utils/MessageParser.java | 10 | ||||
-rw-r--r-- | src/eu/siacs/conversations/utils/UIHelper.java | 20 | ||||
-rw-r--r-- | src/eu/siacs/conversations/xml/Element.java | 17 | ||||
-rw-r--r-- | src/eu/siacs/conversations/xmpp/XmppConnection.java | 37 | ||||
-rw-r--r-- | src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java | 4 |
12 files changed, 131 insertions, 138 deletions
diff --git a/res/values/arrays.xml b/res/values/arrays.xml index b8135cde..2210fbb8 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1,17 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <array name="conversation_encryption_type_entries"> - <item>None</item> - <item>OpenPGP</item> - <item>Off the record</item> - </array> - <array name="conversation_encryption_type_values"> - <item>none</item> - <item>pgp</item> - <item>otr</item> - </array> - <array name="manage_account_options"> - <item>Delete</item> - <item>Disable</item> + <array name="resources"> + <item>Mobile</item> + <item>Phone</item> + <item>Tablet</item> + <item>Conversations</item> + <item>Android</item> </array> </resources> diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 904ea07e..49b3e580 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -1,20 +1,20 @@ <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory - android:title="Security and Privacy Options"> - <ListPreference - android:key="default_conversation_encryption_type" - android:title="Default conversation encryption" - android:dialogTitle="Default conversation encryption" - android:entries="@array/conversation_encryption_type_entries" - android:entryValues="@array/conversation_encryption_type_values" - android:defaultValue="none"/> + android:title="General"> <CheckBoxPreference android:key="grant_new_contacts" android:title="Grant presence updates" android:summary="Preemptivly grant and ask for presence subscription for contacts you created" android:defaultValue="true" /> + <ListPreference + android:key="resource" + android:title="XMPP Resource" + android:summary="The name this client identifies itself" + android:entries="@array/resources" + android:entryValues="@array/resources" + android:defaultValue="Mobile"/> </PreferenceCategory> <PreferenceCategory android:title="Notification Settings"> diff --git a/src/eu/siacs/conversations/crypto/OtrEngine.java b/src/eu/siacs/conversations/crypto/OtrEngine.java index 3aa87658..74e6036d 100644 --- a/src/eu/siacs/conversations/crypto/OtrEngine.java +++ b/src/eu/siacs/conversations/crypto/OtrEngine.java @@ -158,11 +158,10 @@ public class OtrEngine implements OtrEngineHost { packet.setFrom(account.getFullJid()); //sender packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient packet.setBody(body); - Element privateTag = new Element("private"); - privateTag.setAttribute("xmlns","urn:xmpp:carbons:2"); - packet.addChild(privateTag); + packet.addChild("private","urn:xmpp:carbons:2"); + packet.addChild("no-copy","urn:xmpp:hints"); packet.setType(MessagePacket.TYPE_CHAT); - Log.d(LOGTAG,packet.toString()); + //Log.d(LOGTAG,packet.toString()); account.getXmppConnection().sendMessagePacket(packet); } diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java index 3f9510b3..8fd9444a 100644 --- a/src/eu/siacs/conversations/entities/Account.java +++ b/src/eu/siacs/conversations/entities/Account.java @@ -52,7 +52,7 @@ public class Account extends AbstractEntity{ protected String password; protected int options = 0; protected String rosterVersion; - protected String resource; + protected String resource = "mobile"; protected int status = -1; protected JSONObject keys = new JSONObject(); @@ -137,6 +137,10 @@ public class Account extends AbstractEntity{ this.resource = resource; } + public String getResource() { + return this.resource; + } + public String getJid() { return username+"@"+server; } diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index b4ec210c..a13d4ea6 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -219,7 +219,6 @@ public class Conversation extends AbstractEntity { } public void startOtrSession(Context context, String presence) { - Log.d("xmppService", "starting otr session with " + presence); SessionID sessionId = new SessionID(this.getContactJid(), presence, "xmpp"); this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine( diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 3e964800..8bc1b389 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -7,6 +7,7 @@ import java.util.Comparator; import java.util.Date; import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Random; import org.openintents.openpgp.util.OpenPgpApi; @@ -203,6 +204,12 @@ public class XmppConnectionService extends Service { accountChangedListener.onAccountListChangedListener(); } if (account.getStatus() == Account.STATUS_ONLINE) { + List<Conversation> conversations = getConversations(); + for (int i = 0; i < conversations.size(); ++i) { + if (conversations.get(i).getAccount() == account) { + sendUnsendMessages(conversations.get(i)); + } + } scheduleWakeupCall(PING_MAX_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { if (!account.isOptionSet(Account.OPTION_DISABLED)) { @@ -544,6 +551,9 @@ public class XmppConnectionService extends Service { } public XmppConnection createConnection(Account account) { + SharedPreferences sharedPref = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + account.setResource(sharedPref.getString("resource", "mobile").toLowerCase(Locale.getDefault())); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); XmppConnection connection = new XmppConnection(account, pm); connection.setOnMessagePacketReceivedListener(this.messageListener); @@ -574,12 +584,6 @@ public class XmppConnectionService extends Service { updateRoster(account, null); } connectMultiModeConversations(account); - List<Conversation> conversations = getConversations(); - for (int i = 0; i < conversations.size(); ++i) { - if (conversations.get(i).getAccount() == account) { - sendUnsendMessages(conversations.get(i)); - } - } if (convChangedListener != null) { convChangedListener.onConversationListChanged(); } @@ -588,7 +592,7 @@ public class XmppConnectionService extends Service { return connection; } - public void sendMessage(Message message, String presence) { + synchronized public void sendMessage(Message message, String presence) { Account account = message.getConversation().getAccount(); Conversation conv = message.getConversation(); boolean saveInDb = false; @@ -619,11 +623,8 @@ public class XmppConnectionService extends Service { .getFullJid()); packet.setTo(message.getCounterpart()); packet.setBody("This is an XEP-0027 encryted message"); - Element x = new Element("x"); - x.setAttribute("xmlns", "jabber:x:encrypted"); - x.setContent(this.getPgpEngine().encrypt(keyId, + packet.addChild("x","jabber:x:encrypted").setContent(this.getPgpEngine().encrypt(keyId, message.getBody())); - packet.addChild(x); account.getXmppConnection().sendMessagePacket(packet); message.setStatus(Message.STATUS_SEND); message.setEncryption(Message.ENCRYPTION_DECRYPTED); @@ -661,7 +662,7 @@ public class XmppConnectionService extends Service { private void sendUnsendMessages(Conversation conversation) { for (int i = 0; i < conversation.getMessages().size(); ++i) { - if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) { + if ((conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND)&&(conversation.getMessages().get(i).getEncryption() == Message.ENCRYPTION_NONE)) { Message message = conversation.getMessages().get(i); MessagePacket packet = prepareMessagePacket( conversation.getAccount(), message, null); @@ -694,9 +695,8 @@ public class XmppConnectionService extends Service { + ": could not encrypt message to " + message.getCounterpart()); } - Element privateMarker = new Element("private"); - privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2"); - packet.addChild(privateMarker); + packet.addChild("private","urn:xmpp:carbons:2"); + packet.addChild("no-copy","urn:xmpp:hints"); packet.setTo(otrSession.getSessionID().getAccountID() + "/" + otrSession.getSessionID().getUserID()); packet.setFrom(account.getFullJid()); @@ -736,16 +736,13 @@ public class XmppConnectionService extends Service { public void updateRoster(final Account account, final OnRosterFetchedListener listener) { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); - Element query = new Element("query"); - query.setAttribute("xmlns", "jabber:iq:roster"); if (!"".equals(account.getRosterVersion())) { Log.d(LOGTAG, account.getJid() + ": fetching roster version " + account.getRosterVersion()); } else { Log.d(LOGTAG, account.getJid() + ": fetching roster"); } - query.setAttribute("ver", account.getRosterVersion()); - iqPacket.addChild(query); + iqPacket.query("jabber:iq:roster").setAttribute("ver", account.getRosterVersion()); account.getXmppConnection().sendIqPacket(iqPacket, new OnIqPacketReceived() { @@ -958,13 +955,8 @@ public class XmppConnectionService extends Service { public void deleteContact(Contact contact) { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - Element query = new Element("query"); - query.setAttribute("xmlns", "jabber:iq:roster"); - Element item = new Element("item"); - item.setAttribute("jid", contact.getJid()); - item.setAttribute("subscription", "remove"); - query.addChild(item); - iq.addChild(query); + Element query = iq.query("jabber:iq:roster"); + query.addChild("item").setAttribute("jid", contact.getJid()).setAttribute("subscription", "remove"); contact.getAccount().getXmppConnection().sendIqPacket(iq, null); replaceContactInConversation(contact.getJid(), null); databaseBackend.deleteContact(contact); @@ -1032,11 +1024,9 @@ public class XmppConnectionService extends Service { Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); if (conversation.getMessages().size() != 0) { - Element history = new Element("history"); long lastMsgTime = conversation.getLatestMessage().getTimeSent(); long diff = (System.currentTimeMillis() - lastMsgTime) / 1000 - 1; - history.setAttribute("seconds", diff + ""); - x.addChild(history); + x.addChild("history").setAttribute("seconds", diff + ""); } packet.addChild(x); conversation.getAccount().getXmppConnection() diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index 4ae85dc2..6e5105f5 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -388,52 +388,54 @@ public class ConversationFragment extends Fragment { public void updateMessages() { ConversationActivity activity = (ConversationActivity) getActivity(); - List<Message> encryptedMessages = new LinkedList<Message>(); - for (Message message : this.conversation.getMessages()) { - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - encryptedMessages.add(message); - } - } - if (encryptedMessages.size() > 0) { - DecryptMessage task = new DecryptMessage(); - Message[] msgs = new Message[encryptedMessages.size()]; - task.execute(encryptedMessages.toArray(msgs)); - } - this.messageList.clear(); - this.messageList.addAll(this.conversation.getMessages()); - this.messageListAdapter.notifyDataSetChanged(); - if (conversation.getMode() == Conversation.MODE_SINGLE) { - if (messageList.size() >= 1) { - int latestEncryption = this.conversation.getLatestMessage() - .getEncryption(); - if (latestEncryption == Message.ENCRYPTION_DECRYPTED) { - conversation.nextMessageEncryption = Message.ENCRYPTION_PGP; - } else { - conversation.nextMessageEncryption = latestEncryption; + if (this.conversation != null) { + List<Message> encryptedMessages = new LinkedList<Message>(); + for (Message message : this.conversation.getMessages()) { + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + encryptedMessages.add(message); } - makeFingerprintWarning(latestEncryption); } - } else { - if (conversation.getMucOptions().getError() != 0) { - mucError.setVisibility(View.VISIBLE); - if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) { - mucErrorText.setText(getString(R.string.nick_in_use)); + if (encryptedMessages.size() > 0) { + DecryptMessage task = new DecryptMessage(); + Message[] msgs = new Message[encryptedMessages.size()]; + task.execute(encryptedMessages.toArray(msgs)); + } + this.messageList.clear(); + this.messageList.addAll(this.conversation.getMessages()); + this.messageListAdapter.notifyDataSetChanged(); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + if (messageList.size() >= 1) { + int latestEncryption = this.conversation.getLatestMessage() + .getEncryption(); + if (latestEncryption == Message.ENCRYPTION_DECRYPTED) { + conversation.nextMessageEncryption = Message.ENCRYPTION_PGP; + } else { + conversation.nextMessageEncryption = latestEncryption; + } + makeFingerprintWarning(latestEncryption); } } else { - mucError.setVisibility(View.GONE); + if (conversation.getMucOptions().getError() != 0) { + mucError.setVisibility(View.VISIBLE); + if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) { + mucErrorText.setText(getString(R.string.nick_in_use)); + } + } else { + mucError.setVisibility(View.GONE); + } + } + getActivity().invalidateOptionsMenu(); + updateChatMsgHint(); + int size = this.messageList.size(); + if (size >= 1) + messagesView.setSelection(size - 1); + if (!activity.shouldPaneBeOpen()) { + conversation.markRead(); + // TODO update notifications + UIHelper.updateNotification(getActivity(), + activity.getConversationList(), null, false); + activity.updateConversationList(); } - } - getActivity().invalidateOptionsMenu(); - updateChatMsgHint(); - int size = this.messageList.size(); - if (size >= 1) - messagesView.setSelection(size - 1); - if (!activity.shouldPaneBeOpen()) { - conversation.markRead(); - // TODO update notifications - UIHelper.updateNotification(getActivity(), - activity.getConversationList(), null, false); - activity.updateConversationList(); } } diff --git a/src/eu/siacs/conversations/utils/MessageParser.java b/src/eu/siacs/conversations/utils/MessageParser.java index de8f4de8..58235678 100644 --- a/src/eu/siacs/conversations/utils/MessageParser.java +++ b/src/eu/siacs/conversations/utils/MessageParser.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.utils; import java.util.List; -import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; import android.util.Log; @@ -31,13 +30,16 @@ public class MessageParser { } public static Message parseOtrChat(MessagePacket packet, Account account, XmppConnectionService service) { + boolean justStarted = false; boolean properlyAddressed = (packet.getTo().split("/").length == 2) || (account.countPresences() == 1); String[] fromParts = packet.getFrom().split("/"); Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false); String body = packet.getBody(); if (!conversation.hasValidOtrSession()) { if (properlyAddressed) { + Log.d("xmppService","starting new otr session with "+packet.getFrom()+" because no valid otr session has been found"); conversation.startOtrSession(service.getApplicationContext(), fromParts[1]); + justStarted = true; } else { Log.d("xmppService",account.getJid()+": ignoring otr session with "+fromParts[0]); return null; @@ -47,7 +49,9 @@ public class MessageParser { if (!foreignPresence.equals(fromParts[1])) { conversation.resetOtrSession(); if (properlyAddressed) { + Log.d("xmppService","replacing otr session with "+packet.getFrom()); conversation.startOtrSession(service.getApplicationContext(), fromParts[1]); + justStarted = true; } else { return null; } @@ -84,7 +88,9 @@ public class MessageParser { Log.d(LOGTAG,"otr session stoped"); } } catch (Exception e) { - conversation.resetOtrSession(); + if (!justStarted) { + conversation.resetOtrSession(); + } return null; } diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java index 5d879714..96d628fa 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -305,15 +305,19 @@ public class UIHelper { } public static Bitmap getSelfContactPicture(Account account, int size, boolean showPhoneSelfContactPicture, Activity activity) { - Uri selfiUri = PhoneHelper.getSefliUri(activity); - if (selfiUri != null) { - try { - return BitmapFactory.decodeStream(activity - .getContentResolver().openInputStream(selfiUri)); - } catch (FileNotFoundException e) { - return getUnknownContactPicture(account.getJid(), size); + if (showPhoneSelfContactPicture) { + Uri selfiUri = PhoneHelper.getSefliUri(activity); + if (selfiUri != null) { + try { + return BitmapFactory.decodeStream(activity + .getContentResolver().openInputStream(selfiUri)); + } catch (FileNotFoundException e) { + return getUnknownContactPicture(account.getJid(), size); + } } + return getUnknownContactPicture(account.getJid(), size); + } else { + return getUnknownContactPicture(account.getJid(), size); } - return getUnknownContactPicture(account.getJid(), size); } } diff --git a/src/eu/siacs/conversations/xml/Element.java b/src/eu/siacs/conversations/xml/Element.java index 6cf6d3a5..91d9ed6b 100644 --- a/src/eu/siacs/conversations/xml/Element.java +++ b/src/eu/siacs/conversations/xml/Element.java @@ -17,7 +17,22 @@ public class Element { public Element addChild(Element child) { this.content = null; children.add(child); - return this; + return child; + } + + public Element addChild(String name) { + this.content = null; + Element child = new Element(name); + children.add(child); + return child; + } + + public Element addChild(String name, String xmlns) { + this.content = null; + Element child = new Element(name); + child.setAttribute("xmlns", xmlns); + children.add(child); + return child; } public Element setContent(String content) { diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 49aca167..1d386f91 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -461,11 +461,7 @@ public class XmppConnection implements Runnable { if (this.streamFeatures.hasChild("session")) { Log.d(LOGTAG,"sending session"); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); - Element session = new Element("session"); - session.setAttribute("xmlns", - "urn:ietf:params:xml:ns:xmpp-session"); - session.setContent(""); - startSession.addChild(session); + startSession.addChild("session","urn:ietf:params:xml:ns:xmpp-session"); //setContent("") this.sendIqPacket(startSession, null); } } @@ -484,7 +480,8 @@ public class XmppConnection implements Runnable { IqPacket register = new IqPacket(IqPacket.TYPE_SET); Element username = new Element("username").setContent(account.getUsername()); Element password = new Element("password").setContent(account.getPassword()); - register.query("jabber:iq:register").addChild(username).addChild(password); + register.query("jabber:iq:register").addChild(username); + register.query().addChild(password); sendIqPacket(register, new OnIqPacketReceived() { @Override @@ -515,14 +512,9 @@ public class XmppConnection implements Runnable { packet.setAttribute("from", account.getFullJid()); if (account.getKeys().has("pgp_signature")) { try { - String signature = account.getKeys().getString("pgp_signature"); - Element status = new Element("status"); - status.setContent("online"); - packet.addChild(status); - Element x = new Element("x"); - x.setAttribute("xmlns", "jabber:x:signed"); - x.setContent(signature); - packet.addChild(x); + String signature = account.getKeys().getString("pgp_signature"); + packet.addChild("status").setContent("online"); + packet.addChild("x","jabber:x:signed").setContent(signature); } catch (JSONException e) { // } @@ -532,12 +524,7 @@ public class XmppConnection implements Runnable { private void sendBindRequest() throws IOException { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - Element bind = new Element("bind"); - bind.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind"); - Element resource = new Element("resource"); - resource.setContent("Conversations"); - bind.addChild(resource); - iq.addChild(bind); + iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind").addChild("resource").setContent(account.getResource()); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { @@ -601,9 +588,7 @@ public class XmppConnection implements Runnable { private void sendEnableCarbons() { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - Element enable = new Element("enable"); - enable.setAttribute("xmlns", "urn:xmpp:carbons:2"); - iq.addChild(enable); + iq.addChild("enable","urn:xmpp:carbons:2"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override @@ -679,10 +664,8 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(new RequestPacket()); } else { IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - Element ping = new Element("ping"); - iq.setAttribute("from",account.getFullJid()); - ping.setAttribute("xmlns", "urn:xmpp:ping"); - iq.addChild(ping); + iq.setFrom(account.getFullJid()); + iq.addChild("ping","urn:xmpp:ping"); this.sendIqPacket(iq, null); } } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java index f70a74dd..3ab3b6c3 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.xmpp.stanzas; -import android.graphics.YuvImage; import eu.siacs.conversations.xml.Element; @@ -39,8 +38,7 @@ public class IqPacket extends AbstractStanza { public Element query() { Element query = findChild("query"); if (query==null) { - query = new Element("query"); - addChild(query); + query = addChild("query"); } return query; } |