From 0ad6d0616f9663814f6f9f24590833425f6ad372 Mon Sep 17 00:00:00 2001 From: iNPUTmice Date: Thu, 20 Nov 2014 18:20:42 +0100 Subject: [PATCH] brought ad hoc conferences back. fixed #688 fixed #367 --- .../conversations/entities/Conversation.java | 18 +- .../conversations/entities/MucOptions.java | 160 ++++++++++------ .../generator/MessageGenerator.java | 4 +- .../conversations/services/AvatarService.java | 2 +- .../services/XmppConnectionService.java | 171 ++++++++++++++---- .../ui/ConferenceDetailsActivity.java | 51 +++--- .../ui/ConversationActivity.java | 14 +- .../ui/ConversationFragment.java | 2 +- .../siacs/conversations/ui/XmppActivity.java | 59 ++++-- .../conversations/xmpp/XmppConnection.java | 9 +- .../siacs/conversations/xmpp/forms/Data.java | 75 ++++++++ .../siacs/conversations/xmpp/forms/Field.java | 50 +++++ src/main/res/values/strings.xml | 4 + 13 files changed, 481 insertions(+), 138 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/xmpp/forms/Data.java create mode 100644 src/main/java/eu/siacs/conversations/xmpp/forms/Field.java diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 8003759d..4801fe12 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -149,11 +149,19 @@ public class Conversation extends AbstractEntity { } public String getName() { - if (getMode() == MODE_MULTI && getMucOptions().getSubject() != null) { - return getMucOptions().getSubject(); - } else if (getMode() == MODE_MULTI && bookmark != null - && bookmark.getName() != null) { - return bookmark.getName(); + if (getMode() == MODE_MULTI) { + if (getMucOptions().getSubject() != null) { + return getMucOptions().getSubject(); + } else if (bookmark != null && bookmark.getName() != null) { + return bookmark.getName(); + } else { + String generatedName = getMucOptions().createNameFromParticipants(); + if (generatedName != null) { + return generatedName; + } else { + return getContactJid().getLocalpart(); + } + } } else { return this.getContact().getDisplayName(); } diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 4725bffa..2655fc44 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -4,18 +4,20 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import android.annotation.SuppressLint; +import android.util.Log; @SuppressLint("DefaultLocale") public class MucOptions { public static final int ERROR_NO_ERROR = 0; public static final int ERROR_NICK_IN_USE = 1; - public static final int ERROR_ROOM_NOT_FOUND = 2; + public static final int ERROR_UNKNOWN = 2; public static final int ERROR_PASSWORD_REQUIRED = 3; public static final int ERROR_BANNED = 4; public static final int ERROR_MEMBERS_ONLY = 5; @@ -25,8 +27,17 @@ public class MucOptions { public static final String STATUS_CODE_BANNED = "301"; public static final String STATUS_CODE_KICKED = "307"; - public interface OnRenameListener { - public void onRename(boolean success); + private interface OnEventListener { + public void onSuccess(); + public void onFailure(); + } + + public interface OnRenameListener extends OnEventListener { + + } + + public interface OnJoinListener extends OnEventListener { + } public class User { @@ -119,13 +130,13 @@ public class MucOptions { private List users = new CopyOnWriteArrayList<>(); private Conversation conversation; private boolean isOnline = false; - private int error = ERROR_ROOM_NOT_FOUND; - private OnRenameListener renameListener = null; - private boolean aboutToRename = false; + private int error = ERROR_UNKNOWN; + private OnRenameListener onRenameListener = null; + private OnJoinListener onJoinListener = null; private User self = new User(); private String subject = null; - private String joinnick; private String password = null; + private boolean mNickChangingInProgress = false; public MucOptions(Conversation conversation) { this.account = conversation.getAccount(); @@ -155,10 +166,11 @@ public class MucOptions { final Jid from = packet.getFrom(); if (!from.isBareJid()) { final String name = from.getResourcepart(); - String type = packet.getAttribute("type"); + final String type = packet.getAttribute("type"); + final Element x = packet.findChild("x","http://jabber.org/protocol/muc#user"); + final List codes = getStatusCodes(x); if (type == null) { User user = new User(); - Element x = packet.findChild("x","http://jabber.org/protocol/muc#user"); if (x != null) { Element item = x.findChild("item"); if (item != null) { @@ -166,16 +178,16 @@ public class MucOptions { user.setAffiliation(item.getAttribute("affiliation")); user.setRole(item.getAttribute("role")); user.setJid(item.getAttributeAsJid("jid")); - user.setName(name); - if (name.equals(this.joinnick)) { + if (codes.contains("110")) { this.isOnline = true; this.error = ERROR_NO_ERROR; self = user; - if (aboutToRename) { - if (renameListener != null) { - renameListener.onRename(true); - } - aboutToRename = false; + if (mNickChangingInProgress) { + onRenameListener.onSuccess(); + mNickChangingInProgress = false; + } else if (this.onJoinListener != null) { + this.onJoinListener.onSuccess(); + this.onJoinListener = null; } } else { addUser(user); @@ -196,48 +208,65 @@ public class MucOptions { } } } - } else if (type.equals("unavailable") && name.equals(this.joinnick)) { - Element x = packet.findChild("x", - "http://jabber.org/protocol/muc#user"); - if (x != null) { - Element status = x.findChild("status"); - if (status != null) { - String code = status.getAttribute("code"); - if (STATUS_CODE_KICKED.equals(code)) { - this.isOnline = false; - this.error = KICKED_FROM_ROOM; - } else if (STATUS_CODE_BANNED.equals(code)) { - this.isOnline = false; - this.error = ERROR_BANNED; - } - } + } else if (type.equals("unavailable") && codes.contains("110")) { + if (codes.contains("303")) { + this.mNickChangingInProgress = true; + } else if (codes.contains(STATUS_CODE_KICKED)) { + setError(KICKED_FROM_ROOM); + } else if (codes.contains(STATUS_CODE_BANNED)) { + setError(ERROR_BANNED); + } else { + setError(ERROR_UNKNOWN); } } else if (type.equals("unavailable")) { - deleteUser(packet.getAttribute("from").split("/", 2)[1]); + deleteUser(name); } else if (type.equals("error")) { Element error = packet.findChild("error"); if (error != null && error.hasChild("conflict")) { - if (aboutToRename) { - if (renameListener != null) { - renameListener.onRename(false); + if (isOnline) { + if (onRenameListener != null) { + onRenameListener.onFailure(); } - aboutToRename = false; - this.setJoinNick(getActualNick()); } else { - this.error = ERROR_NICK_IN_USE; + setError(ERROR_NICK_IN_USE); } } else if (error != null && error.hasChild("not-authorized")) { - this.error = ERROR_PASSWORD_REQUIRED; + setError(ERROR_PASSWORD_REQUIRED); } else if (error != null && error.hasChild("forbidden")) { - this.error = ERROR_BANNED; - } else if (error != null - && error.hasChild("registration-required")) { - this.error = ERROR_MEMBERS_ONLY; + setError(ERROR_BANNED); + } else if (error != null && error.hasChild("registration-required")) { + setError(ERROR_MEMBERS_ONLY); + } else { + setError(ERROR_UNKNOWN); } } } } + private void setError(int error) { + this.isOnline = false; + this.error = error; + if (onJoinListener != null) { + onJoinListener.onFailure(); + onJoinListener = null; + } + } + + private List getStatusCodes(Element x) { + List codes = new ArrayList(); + if (x != null) { + for(Element child : x.getChildren()) { + if (child.getName().equals("status")) { + String code = child.getAttribute("code"); + if (code!=null) { + codes.add(code); + } + } + } + } + return codes; + } + public List getUsers() { return this.users; } @@ -263,10 +292,6 @@ public class MucOptions { } } - public void setJoinNick(String nick) { - this.joinnick = nick; - } - public boolean online() { return this.isOnline; } @@ -276,11 +301,11 @@ public class MucOptions { } public void setOnRenameListener(OnRenameListener listener) { - this.renameListener = listener; + this.onRenameListener = listener; } - public OnRenameListener getOnRenameListener() { - return this.renameListener; + public void setOnJoinListener(OnJoinListener listener) { + this.onJoinListener = listener; } public void setOffline() { @@ -301,8 +326,28 @@ public class MucOptions { return this.subject; } - public void flagAboutToRename() { - this.aboutToRename = true; + public String createNameFromParticipants() { + if (users.size() >= 2) { + List names = new ArrayList(); + for (User user : users) { + Contact contact = user.getContact(); + if (contact != null && !contact.getDisplayName().isEmpty()) { + names.add(contact.getDisplayName().split("\\s+")[0]); + } else { + names.add(user.getName()); + } + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < names.size(); ++i) { + builder.append(names.get(i)); + if (i != names.size() - 1) { + builder.append(", "); + } + } + return builder.toString(); + } else { + return null; + } } public long[] getPgpKeyIds() { @@ -337,10 +382,9 @@ public class MucOptions { return true; } - public Jid getJoinJid() { + public Jid createJoinJid(String nick) { try { - return Jid.fromString(this.conversation.getContactJid().toBareJid().toString() + "/" -+ this.joinnick); + return Jid.fromString(this.conversation.getContactJid().toBareJid().toString() + "/"+nick); } catch (final InvalidJidException e) { return null; } @@ -356,8 +400,7 @@ public class MucOptions { } public String getPassword() { - this.password = conversation - .getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD); + this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD); if (this.password == null && conversation.getBookmark() != null && conversation.getBookmark().getPassword() != null) { return conversation.getBookmark().getPassword(); @@ -372,8 +415,7 @@ public class MucOptions { } else { this.password = password; } - conversation - .setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password); + conversation.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password); } public Conversation getConversation() { diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 65f776ff..ca5417e0 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -153,14 +153,14 @@ public class MessageGenerator extends AbstractGenerator { return packet; } - public MessagePacket invite(Conversation conversation, String contact) { + public MessagePacket invite(Conversation conversation, Jid contact) { MessagePacket packet = new MessagePacket(); packet.setTo(conversation.getContactJid().toBareJid()); packet.setFrom(conversation.getAccount().getJid()); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); Element invite = new Element("invite"); - invite.setAttribute("to", contact); + invite.setAttribute("to", contact.toBareJid().toString()); x.addChild(invite); packet.addChild(x); return packet; diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index b8154d65..1125859c 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -274,7 +274,7 @@ public class AvatarService { } } } - String name = user.getName(); + String name = contact != null ? contact.getDisplayName() : user.getName(); String letter; int color; if (name.length() > 0) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index c82d3374..f8aa2c57 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -35,6 +35,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; +import java.math.BigInteger; import java.security.SecureRandom; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -273,7 +274,6 @@ public class XmppConnectionService extends Service { } }; private LruCache mBitmapCache; - private OnRenameListener renameListener = null; private IqGenerator mIqGenerator = new IqGenerator(this); public PgpEngine getPgpEngine() { @@ -1055,10 +1055,6 @@ public class XmppConnectionService extends Service { updateConversationUi(); } - public int getConversationCount() { - return this.databaseBackend.getConversationCount(); - } - public void createAccount(Account account) { account.initOtrEngine(this); databaseBackend.createAccount(account); @@ -1276,10 +1272,9 @@ public class XmppConnectionService extends Service { Log.d(Config.LOGTAG, "joining conversation " + conversation.getContactJid()); String nick = conversation.getMucOptions().getProposedNick(); - conversation.getMucOptions().setJoinNick(nick); + Jid joinJid = conversation.getMucOptions().createJoinJid(nick); PresencePacket packet = new PresencePacket(); - final Jid joinJid = conversation.getMucOptions().getJoinJid(); - packet.setTo(conversation.getMucOptions().getJoinJid()); + packet.setTo(joinJid); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); if (conversation.getMucOptions().getPassword() != null) { @@ -1311,10 +1306,6 @@ public class XmppConnectionService extends Service { } } - public void setOnRenameListener(OnRenameListener listener) { - this.renameListener = listener; - } - public void providePasswordForMuc(Conversation conversation, String password) { if (conversation.getMode() == Conversation.MODE_MULTI) { conversation.getMucOptions().setPassword(password); @@ -1327,33 +1318,33 @@ public class XmppConnectionService extends Service { } } - public void renameInMuc(final Conversation conversation, final String nick) { + public void renameInMuc(final Conversation conversation, final String nick, final UiCallback callback) { final MucOptions options = conversation.getMucOptions(); - options.setJoinNick(nick); + final Jid joinJid = options.createJoinJid(nick); if (options.online()) { Account account = conversation.getAccount(); options.setOnRenameListener(new OnRenameListener() { @Override - public void onRename(boolean success) { - if (renameListener != null) { - renameListener.onRename(success); - } - if (success) { - conversation.setContactJid(conversation.getMucOptions() - .getJoinJid()); - databaseBackend.updateConversation(conversation); - Bookmark bookmark = conversation.getBookmark(); - if (bookmark != null) { - bookmark.setNick(nick); - pushBookmarks(bookmark.getAccount()); - } + public void onSuccess() { + conversation.setContactJid(joinJid); + databaseBackend.updateConversation(conversation); + Bookmark bookmark = conversation.getBookmark(); + if (bookmark != null) { + bookmark.setNick(nick); + pushBookmarks(bookmark.getAccount()); } + callback.success(conversation); + } + + @Override + public void onFailure() { + callback.error(R.string.nick_in_use,conversation); } }); - options.flagAboutToRename(); + PresencePacket packet = new PresencePacket(); - packet.setTo(options.getJoinJid()); + packet.setTo(joinJid); packet.setFrom(conversation.getAccount().getJid()); String sig = account.getPgpSignature(); @@ -1363,7 +1354,7 @@ public class XmppConnectionService extends Service { } sendPresencePacket(account, packet); } else { - conversation.setContactJid(options.getJoinJid()); + conversation.setContactJid(joinJid); databaseBackend.updateConversation(conversation); if (conversation.getAccount().getStatus() == Account.State.ONLINE) { Bookmark bookmark = conversation.getBookmark(); @@ -1382,7 +1373,7 @@ public class XmppConnectionService extends Service { account.pendingConferenceLeaves.remove(conversation); if (account.getStatus() == Account.State.ONLINE) { PresencePacket packet = new PresencePacket(); - packet.setTo(conversation.getMucOptions().getJoinJid()); + packet.setTo(conversation.getContactJid()); packet.setFrom(conversation.getAccount().getJid()); packet.setAttribute("type", "unavailable"); sendPresencePacket(conversation.getAccount(), packet); @@ -1395,6 +1386,117 @@ public class XmppConnectionService extends Service { } } + private String findConferenceServer(final Account account) { + String server; + if (account.getXmppConnection() != null) { + server = account.getXmppConnection().getMucServer(); + if (server != null) { + return server; + } + } + for(Account other : getAccounts()) { + if (other != account && other.getXmppConnection() != null) { + server = other.getXmppConnection().getMucServer(); + if (server != null) { + return server; + } + } + } + return null; + } + + public void createAdhocConference(final Account account, final List jids, final UiCallback callback) { + Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": creating adhoc conference with "+ jids.toString()); + if (account.getStatus() == Account.State.ONLINE) { + try { + String server = findConferenceServer(account); + if (server == null) { + if (callback != null) { + callback.error(R.string.no_conference_server_found,null); + } + return; + } + String name = new BigInteger(75,getRNG()).toString(32); + Jid jid = Jid.fromParts(name,server,null); + final Conversation conversation = findOrCreateConversation(account, jid, true); + joinMuc(conversation); + Bundle options = new Bundle(); + options.putString("muc#roomconfig_persistentroom", "1"); + options.putString("muc#roomconfig_membersonly", "1"); + options.putString("muc#roomconfig_publicroom", "0"); + options.putString("muc#roomconfig_whois", "anyone"); + pushConferenceConfiguration(conversation, options, new OnConferenceOptionsPushed() { + @Override + public void onPushSucceeded() { + for(Jid invite : jids) { + invite(conversation,invite); + } + if (callback != null) { + callback.success(conversation); + } + } + + @Override + public void onPushFailed() { + if (callback != null) { + callback.error(R.string.conference_creation_failed, conversation); + } + } + }); + + } catch (InvalidJidException e) { + if (callback != null) { + callback.error(R.string.conference_creation_failed, null); + } + } + } else { + if (callback != null) { + callback.error(R.string.not_connected_try_again,null); + } + } + } + + public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) { + IqPacket request = new IqPacket(IqPacket.TYPE_GET); + request.setTo(conversation.getContactJid().toBareJid()); + request.query("http://jabber.org/protocol/muc#owner"); + sendIqPacket(conversation.getAccount(),request,new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() != IqPacket.TYPE_ERROR) { + Data data = Data.parse(packet.query().findChild("x", "jabber:x:data")); + for (Field field : data.getFields()) { + if (options.containsKey(field.getName())) { + field.setValue(options.getString(field.getName())); + } + } + data.submit(); + IqPacket set = new IqPacket(IqPacket.TYPE_SET); + set.setTo(conversation.getContactJid().toBareJid()); + set.query("http://jabber.org/protocol/muc#owner").addChild(data); + sendIqPacket(account, set, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE_RESULT) { + if (callback != null) { + callback.onPushSucceeded(); + } + } else { + if (callback != null) { + callback.onPushFailed(); + } + } + } + }); + } else { + if (callback != null) { + callback.onPushFailed(); + } + } + } + }); + } + public void disconnect(Account account, boolean force) { if ((account.getStatus() == Account.State.ONLINE) || (account.getStatus() == Account.State.DISABLED)) { @@ -1726,7 +1828,7 @@ public class XmppConnectionService extends Service { }).start(); } - public void invite(Conversation conversation, String contact) { + public void invite(Conversation conversation, Jid contact) { MessagePacket packet = mMessageGenerator.invite(conversation, contact); sendMessagePacket(conversation.getAccount(), packet); } @@ -2023,6 +2125,11 @@ public class XmppConnectionService extends Service { public void onRosterUpdate(); } + private interface OnConferenceOptionsPushed { + public void onPushSucceeded(); + public void onPushFailed(); + } + public class XmppConnectionBinder extends Binder { public XmppConnectionService getService() { return XmppConnectionService.this; diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index c97f9e56..a966f77b 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -34,7 +34,7 @@ import eu.siacs.conversations.entities.MucOptions.User; import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; -public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnRenameListener { +public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate { public static final String ACTION_VIEW_MUC = "view_muc"; private Conversation mConversation; private OnClickListener inviteListener = new OnClickListener() { @@ -57,26 +57,34 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers private List users = new ArrayList<>(); private User mSelectedUser = null; - @Override - public void onRename(final boolean success) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - populateView(); - if (success) { - Toast.makeText( - ConferenceDetailsActivity.this, - getString(R.string.your_nick_has_been_changed), - Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(ConferenceDetailsActivity.this, - getString(R.string.nick_in_use), - Toast.LENGTH_SHORT).show(); + private UiCallback renameCallback = new UiCallback() { + @Override + public void success(Conversation object) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(ConferenceDetailsActivity.this,getString(R.string.your_nick_has_been_changed),Toast.LENGTH_SHORT).show(); + populateView(); } - } - }); - } + }); + + } + + @Override + public void error(final int errorCode, Conversation object) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(ConferenceDetailsActivity.this,getString(errorCode),Toast.LENGTH_SHORT).show(); + } + }); + } + + @Override + public void userInputRequried(PendingIntent pi, Conversation object) { + + } + }; @Override public void onConversationUpdate() { @@ -114,8 +122,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers @Override public void onValueEdited(String value) { - xmppConnectionService.renameInMuc(mConversation, - value); + xmppConnectionService.renameInMuc(mConversation,value,renameCallback); } }); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 721ad9c3..0be3a957 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -225,6 +225,18 @@ public class ConversationActivity extends XmppActivity implements } } + @Override + public void switchToConversation(Conversation conversation) { + setSelectedConversation(conversation); + runOnUiThread(new Runnable() { + @Override + public void run() { + ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); + openConversation(); + } + }); + } + public void openConversation() { ActionBar ab = getActionBar(); if (ab != null) { @@ -286,7 +298,7 @@ public class ConversationActivity extends XmppActivity implements menuAttach.setVisible(false); } else { menuMucDetails.setVisible(false); - menuInviteContact.setVisible(false); + menuInviteContact.setTitle(R.string.conference_with); } if (this.getSelectedConversation().isMuted()) { menuMute.setVisible(false); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index abfd0727..287d1636 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -553,7 +553,7 @@ public class ConversationFragment extends Fragment { showSnackbar(R.string.nick_in_use, R.string.edit, clickToMuc); break; - case MucOptions.ERROR_ROOM_NOT_FOUND: + case MucOptions.ERROR_UNKNOWN: showSnackbar(R.string.conference_not_found, R.string.leave, leaveMuc); break; diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 2ff317a4..a9147fba 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -42,6 +42,7 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageView; +import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; @@ -54,6 +55,7 @@ import net.java.otr4j.session.SessionID; import java.io.FileNotFoundException; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.concurrent.RejectedExecutionException; @@ -240,9 +242,6 @@ public abstract class XmppActivity extends Activity { if (this instanceof XmppConnectionService.OnRosterUpdate) { this.xmppConnectionService.setOnRosterUpdateListener((XmppConnectionService.OnRosterUpdate) this); } - if (this instanceof MucOptions.OnRenameListener) { - this.xmppConnectionService.setOnRenameListener((MucOptions.OnRenameListener) this); - } } protected void unregisterListeners() { @@ -255,9 +254,6 @@ public abstract class XmppActivity extends Activity { if (this instanceof XmppConnectionService.OnRosterUpdate) { this.xmppConnectionService.removeOnRosterUpdateListener(); } - if (this instanceof MucOptions.OnRenameListener) { - this.xmppConnectionService.setOnRenameListener(null); - } } public boolean onOptionsItemSelected(MenuItem item) { @@ -603,18 +599,53 @@ public abstract class XmppActivity extends Activity { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) { - String contactJid = data.getStringExtra("contact"); - String conversationUuid = data.getStringExtra("conversation"); - Conversation conversation = xmppConnectionService - .findConversationByUuid(conversationUuid); - if (conversation.getMode() == Conversation.MODE_MULTI) { - xmppConnectionService.invite(conversation, contactJid); + try { + Jid jid = Jid.fromString(data.getStringExtra("contact")); + String conversationUuid = data.getStringExtra("conversation"); + Conversation conversation = xmppConnectionService + .findConversationByUuid(conversationUuid); + if (conversation.getMode() == Conversation.MODE_MULTI) { + xmppConnectionService.invite(conversation, jid); + } else { + List jids = new ArrayList(); + jids.add(conversation.getContactJid().toBareJid()); + jids.add(jid); + xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback); + } + } catch (final InvalidJidException ignored) { + } - Log.d(Config.LOGTAG, "inviting " + contactJid + " to " - + conversation.getName()); } } + private UiCallback adhocCallback = new UiCallback() { + @Override + public void success(final Conversation conversation) { + switchToConversation(conversation); + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(XmppActivity.this,R.string.conference_created,Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void error(final int errorCode, Conversation object) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(XmppActivity.this,errorCode,Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public void userInputRequried(PendingIntent pi, Conversation object) { + + } + }; + public int getSecondaryTextColor() { return this.mSecondaryTextColor; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index f7a86802..dc895ead 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1049,7 +1049,14 @@ public class XmppConnection implements Runnable { } public String getMucServer() { - return findDiscoItemByFeature("http://jabber.org/protocol/muc"); + final List items = new ArrayList<>(); + for (Entry> cursor : disco.entrySet()) { + final List value = cursor.getValue(); + if (value.contains("http://jabber.org/protocol/muc") && !value.contains("jabber:iq:gateway")) { + return cursor.getKey(); + } + } + return null; } public int getTimeToNextAttempt() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java new file mode 100644 index 00000000..ff9acb3f --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java @@ -0,0 +1,75 @@ +package eu.siacs.conversations.xmpp.forms; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import eu.siacs.conversations.xml.Element; + +public class Data extends Element { + + public Data() { + super("x"); + this.setAttribute("xmlns","jabber:x:data"); + } + + public List getFields() { + ArrayList fields = new ArrayList(); + for(Element child : getChildren()) { + if (child.getName().equals("field")) { + fields.add(Field.parse(child)); + } + } + return fields; + } + + public Field getFieldByName(String needle) { + for(Element child : getChildren()) { + if (child.getName().equals("field") && needle.equals(child.getAttribute("var"))) { + return Field.parse(child); + } + } + return null; + } + + public void put(String name, String value) { + Field field = getFieldByName(name); + if (field == null) { + field = new Field(name); + } + field.setValue(value); + } + + public void put(String name, Collection values) { + Field field = getFieldByName(name); + if (field == null) { + field = new Field(name); + } + field.setValues(values); + } + + public void submit() { + this.setAttribute("type","submit"); + removeNonFieldChildren(); + for(Field field : getFields()) { + field.removeNonValueChildren(); + } + } + + private void removeNonFieldChildren() { + for(Iterator iterator = this.children.iterator(); iterator.hasNext();) { + Element element = iterator.next(); + if (!element.getName().equals("field")) { + iterator.remove(); + } + } + } + + public static Data parse(Element element) { + Data data = new Data(); + data.setAttributes(element.getAttributes()); + data.setChildren(element.getChildren()); + return data; + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Field.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Field.java new file mode 100644 index 00000000..ee2c51a9 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Field.java @@ -0,0 +1,50 @@ +package eu.siacs.conversations.xmpp.forms; + +import java.util.Collection; +import java.util.Iterator; + +import eu.siacs.conversations.xml.Element; + +public class Field extends Element { + + public Field(String name) { + super("field"); + this.setAttribute("var",name); + } + + private Field() { + super("field"); + } + + public String getName() { + return this.getAttribute("var"); + } + + public void setValue(String value) { + this.children.clear(); + this.addChild("value").setContent(value); + } + + public void setValues(Collection values) { + this.children.clear(); + for(String value : values) { + this.addChild("value").setContent(value); + } + } + + public void removeNonValueChildren() { + for(Iterator iterator = this.children.iterator(); iterator.hasNext();) { + Element element = iterator.next(); + if (!element.getName().equals("value")) { + iterator.remove(); + } + } + } + + public static Field parse(Element element) { + Field field = new Field(); + field.setAttributes(element.getAttributes()); + field.setChildren(element.getChildren()); + return field; + } +} diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 6881c7f9..1bb0ef75 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -352,4 +352,8 @@ Show dynamic tags Display read-only tags underneath contacts Enable notifications + Create conference with… + No conference server found + Conference creation failed! + Conference created!