more muc options
* show invite button only with admin privileges or on public conferences * Offer to ban user in public conferences. Thanks to @betheg for the awesome ground work for this
This commit is contained in:
parent
d70b5f93f3
commit
8d655f445a
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.PgpEngine;
|
import eu.siacs.conversations.crypto.PgpEngine;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
|
@ -12,6 +13,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
public class MucOptions {
|
public class MucOptions {
|
||||||
|
@ -51,8 +53,6 @@ public class MucOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
public enum Role {
|
public enum Role {
|
||||||
MODERATOR("moderator", R.string.moderator),
|
MODERATOR("moderator", R.string.moderator),
|
||||||
VISITOR("visitor", R.string.visitor),
|
VISITOR("visitor", R.string.visitor),
|
||||||
|
@ -86,6 +86,7 @@ public class MucOptions {
|
||||||
|
|
||||||
public static final int KICKED_FROM_ROOM = 9;
|
public static final int KICKED_FROM_ROOM = 9;
|
||||||
|
|
||||||
|
public static final String STATUS_CODE_ROOM_CONFIG_CHANGED = "104";
|
||||||
public static final String STATUS_CODE_SELF_PRESENCE = "110";
|
public static final String STATUS_CODE_SELF_PRESENCE = "110";
|
||||||
public static final String STATUS_CODE_BANNED = "301";
|
public static final String STATUS_CODE_BANNED = "301";
|
||||||
public static final String STATUS_CODE_CHANGED_NICK = "303";
|
public static final String STATUS_CODE_CHANGED_NICK = "303";
|
||||||
|
@ -107,8 +108,8 @@ public class MucOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class User {
|
public class User {
|
||||||
private Role role;
|
private Role role = Role.NONE;
|
||||||
private Affiliation affiliation;
|
private Affiliation affiliation = Affiliation.NONE;
|
||||||
private String name;
|
private String name;
|
||||||
private Jid jid;
|
private Jid jid;
|
||||||
private long pgpKeyId = 0;
|
private long pgpKeyId = 0;
|
||||||
|
@ -190,6 +191,7 @@ public class MucOptions {
|
||||||
|
|
||||||
private Account account;
|
private Account account;
|
||||||
private List<User> users = new CopyOnWriteArrayList<>();
|
private List<User> users = new CopyOnWriteArrayList<>();
|
||||||
|
private List<String> features = new ArrayList<>();
|
||||||
private Conversation conversation;
|
private Conversation conversation;
|
||||||
private boolean isOnline = false;
|
private boolean isOnline = false;
|
||||||
private int error = ERROR_UNKNOWN;
|
private int error = ERROR_UNKNOWN;
|
||||||
|
@ -205,6 +207,23 @@ public class MucOptions {
|
||||||
this.conversation = conversation;
|
this.conversation = conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateFeatures(ArrayList<String> features) {
|
||||||
|
this.features.clear();
|
||||||
|
this.features.addAll(features);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasFeature(String feature) {
|
||||||
|
return this.features.contains(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canInvite() {
|
||||||
|
return !membersOnly() || self.getAffiliation().ranks(Affiliation.ADMIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean membersOnly() {
|
||||||
|
return hasFeature("muc_membersonly");
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteUser(String name) {
|
public void deleteUser(String name) {
|
||||||
for (int i = 0; i < users.size(); ++i) {
|
for (int i = 0; i < users.size(); ++i) {
|
||||||
if (users.get(i).getName().equals(name)) {
|
if (users.get(i).getName().equals(name)) {
|
||||||
|
@ -225,6 +244,7 @@ public class MucOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processPacket(PresencePacket packet, PgpEngine pgp) {
|
public void processPacket(PresencePacket packet, PgpEngine pgp) {
|
||||||
|
Log.d(Config.LOGTAG, packet.toString());
|
||||||
final Jid from = packet.getFrom();
|
final Jid from = packet.getFrom();
|
||||||
if (!from.isBareJid()) {
|
if (!from.isBareJid()) {
|
||||||
final String name = from.getResourcepart();
|
final String name = from.getResourcepart();
|
||||||
|
@ -320,7 +340,7 @@ public class MucOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getStatusCodes(Element x) {
|
private List<String> getStatusCodes(Element x) {
|
||||||
List<String> codes = new ArrayList<String>();
|
List<String> codes = new ArrayList<>();
|
||||||
if (x != null) {
|
if (x != null) {
|
||||||
for (Element child : x.getChildren()) {
|
for (Element child : x.getChildren()) {
|
||||||
if (child.getName().equals("status")) {
|
if (child.getName().equals("status")) {
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package eu.siacs.conversations.parser;
|
package eu.siacs.conversations.parser;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import net.java.otr4j.session.Session;
|
import net.java.otr4j.session.Session;
|
||||||
import net.java.otr4j.session.SessionStatus;
|
import net.java.otr4j.session.SessionStatus;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.entities.MucOptions;
|
||||||
import eu.siacs.conversations.services.MessageArchiveService;
|
import eu.siacs.conversations.services.MessageArchiveService;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
@ -142,14 +140,25 @@ public class MessageParser extends AbstractParser implements
|
||||||
Conversation conversation = mXmppConnectionService
|
Conversation conversation = mXmppConnectionService
|
||||||
.findOrCreateConversation(account, from.toBareJid(), true);
|
.findOrCreateConversation(account, from.toBareJid(), true);
|
||||||
if (packet.hasChild("subject")) {
|
if (packet.hasChild("subject")) {
|
||||||
conversation.getMucOptions().setSubject(
|
conversation.getMucOptions().setSubject(packet.findChild("subject").getContent());
|
||||||
packet.findChild("subject").getContent());
|
|
||||||
mXmppConnectionService.updateConversationUi();
|
mXmppConnectionService.updateConversationUi();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (from.isBareJid()) {
|
|
||||||
|
final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
|
||||||
|
if (from.isBareJid() && (x == null || !x.hasChild("status"))) {
|
||||||
return null;
|
return null;
|
||||||
|
} else if (from.isBareJid() && x.hasChild("status")) {
|
||||||
|
for(Element child : x.getChildren()) {
|
||||||
|
if (child.getName().equals("status")) {
|
||||||
|
String code = child.getAttribute("code");
|
||||||
|
if (code.contains(MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED)) {
|
||||||
|
mXmppConnectionService.fetchConferenceConfiguration(conversation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
|
if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
|
||||||
if (mXmppConnectionService.markMessage(conversation,
|
if (mXmppConnectionService.markMessage(conversation,
|
||||||
packet.getId(), Message.STATUS_SEND)) {
|
packet.getId(), Message.STATUS_SEND)) {
|
||||||
|
@ -350,7 +359,7 @@ public class MessageParser extends AbstractParser implements
|
||||||
final Jid from = packet.getAttributeAsJid("from");
|
final Jid from = packet.getAttributeAsJid("from");
|
||||||
Element invite = extractInvite(packet);
|
Element invite = extractInvite(packet);
|
||||||
if (invite != null) {
|
if (invite != null) {
|
||||||
Conversation conversation = mXmppConnectionService.findOrCreateConversation(account,from, true);
|
Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, from, true);
|
||||||
if (!conversation.getMucOptions().online()) {
|
if (!conversation.getMucOptions().online()) {
|
||||||
Element password = invite.findChild("password");
|
Element password = invite.findChild("password");
|
||||||
conversation.getMucOptions().setPassword(password == null ? null : password.getContent());
|
conversation.getMucOptions().setPassword(password == null ? null : password.getContent());
|
||||||
|
|
|
@ -1316,6 +1316,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
packet.addChild("x", "jabber:x:signed").setContent(sig);
|
packet.addChild("x", "jabber:x:signed").setContent(sig);
|
||||||
}
|
}
|
||||||
sendPresencePacket(account, packet);
|
sendPresencePacket(account, packet);
|
||||||
|
fetchConferenceConfiguration(conversation);
|
||||||
if (!joinJid.equals(conversation.getJid())) {
|
if (!joinJid.equals(conversation.getJid())) {
|
||||||
conversation.setContactJid(joinJid);
|
conversation.setContactJid(joinJid);
|
||||||
databaseBackend.updateConversation(conversation);
|
databaseBackend.updateConversation(conversation);
|
||||||
|
@ -1475,6 +1476,29 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fetchConferenceConfiguration(final Conversation conversation) {
|
||||||
|
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
||||||
|
request.setTo(conversation.getJid().toBareJid());
|
||||||
|
request.query("http://jabber.org/protocol/disco#info");
|
||||||
|
sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
|
||||||
|
@Override
|
||||||
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
|
if (packet.getType() != IqPacket.TYPE.ERROR) {
|
||||||
|
ArrayList<String> features = new ArrayList<String>();
|
||||||
|
for (Element child : packet.query().getChildren()) {
|
||||||
|
if (child != null && child.getName().equals("feature")) {
|
||||||
|
String var = child.getAttribute("var");
|
||||||
|
if (var != null) {
|
||||||
|
features.add(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conversation.getMucOptions().updateFeatures(features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) {
|
public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) {
|
||||||
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
||||||
request.setTo(conversation.getJid().toBareJid());
|
request.setTo(conversation.getJid().toBareJid());
|
||||||
|
@ -1520,14 +1544,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
final Jid jid = user.toBareJid();
|
final Jid jid = user.toBareJid();
|
||||||
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
|
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
|
||||||
Log.d(Config.LOGTAG,request.toString());
|
Log.d(Config.LOGTAG,request.toString());
|
||||||
sendIqPacket(conference.getAccount(),request,new OnIqPacketReceived() {
|
sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
|
||||||
@Override
|
@Override
|
||||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
Log.d(Config.LOGTAG,packet.toString());
|
Log.d(Config.LOGTAG, packet.toString());
|
||||||
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
||||||
callback.onAffiliationChangedSuccessful(jid);
|
callback.onAffiliationChangedSuccessful(jid);
|
||||||
} else {
|
} else {
|
||||||
callback.onAffiliationChangeFailed(jid,R.string.could_not_change_affiliation);
|
callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package eu.siacs.conversations.ui;
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.IntentSender.SendIntentException;
|
import android.content.IntentSender.SendIntentException;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -285,13 +287,31 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
|
xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.MEMBER,this);
|
||||||
return true;
|
return true;
|
||||||
case R.id.remove_from_room:
|
case R.id.remove_from_room:
|
||||||
xmppConnectionService.changeAffiliationInConference(mConversation,mSelectedUser.getJid(), MucOptions.Affiliation.OUTCAST,this);
|
removeFromRoom(mSelectedUser);
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onContextItemSelected(item);
|
return super.onContextItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeFromRoom(final User user) {
|
||||||
|
if (mConversation.getMucOptions().membersOnly()) {
|
||||||
|
xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.NONE,this);
|
||||||
|
} else {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setTitle(R.string.ban_user_from_conference);
|
||||||
|
builder.setMessage(getString(R.string.removing_from_public_conference,user.getName()));
|
||||||
|
builder.setNegativeButton(R.string.cancel,null);
|
||||||
|
builder.setPositiveButton(R.string.ban_now,new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
xmppConnectionService.changeAffiliationInConference(mConversation,user.getJid(), MucOptions.Affiliation.OUTCAST,ConferenceDetailsActivity.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.create().show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void startConversation(User user) {
|
protected void startConversation(User user) {
|
||||||
if (user.getJid() != null) {
|
if (user.getJid() != null) {
|
||||||
Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false);
|
Conversation conversation = xmppConnectionService.findOrCreateConversation(this.mConversation.getAccount(),user.getJid().toBareJid(),false);
|
||||||
|
@ -397,6 +417,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
|
ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
|
||||||
iv.setImageBitmap(bm);
|
iv.setImageBitmap(bm);
|
||||||
membersView.addView(view);
|
membersView.addView(view);
|
||||||
|
if (mConversation.getMucOptions().canInvite()) {
|
||||||
|
mInviteButton.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
mInviteButton.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import android.content.DialogInterface;
|
||||||
import android.content.DialogInterface.OnClickListener;
|
import android.content.DialogInterface.OnClickListener;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentSender.SendIntentException;
|
import android.content.IntentSender.SendIntentException;
|
||||||
import android.media.MediaActionSound;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
@ -83,11 +82,6 @@ public class ConversationActivity extends XmppActivity
|
||||||
|
|
||||||
private boolean mActivityPaused = false;
|
private boolean mActivityPaused = false;
|
||||||
|
|
||||||
|
|
||||||
public List<Conversation> getConversationList() {
|
|
||||||
return this.conversationList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Conversation getSelectedConversation() {
|
public Conversation getSelectedConversation() {
|
||||||
return this.mSelectedConversation;
|
return this.mSelectedConversation;
|
||||||
}
|
}
|
||||||
|
@ -284,8 +278,7 @@ public class ConversationActivity extends XmppActivity
|
||||||
final MenuItem menuBlock = menu.findItem(R.id.action_block);
|
final MenuItem menuBlock = menu.findItem(R.id.action_block);
|
||||||
final MenuItem menuUnblock = menu.findItem(R.id.action_unblock);
|
final MenuItem menuUnblock = menu.findItem(R.id.action_unblock);
|
||||||
|
|
||||||
if (isConversationsOverviewVisable()
|
if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) {
|
||||||
&& isConversationsOverviewHideable()) {
|
|
||||||
menuArchive.setVisible(false);
|
menuArchive.setVisible(false);
|
||||||
menuMucDetails.setVisible(false);
|
menuMucDetails.setVisible(false);
|
||||||
menuContactDetails.setVisible(false);
|
menuContactDetails.setVisible(false);
|
||||||
|
@ -309,9 +302,9 @@ public class ConversationActivity extends XmppActivity
|
||||||
menuAttach.setVisible(false);
|
menuAttach.setVisible(false);
|
||||||
menuBlock.setVisible(false);
|
menuBlock.setVisible(false);
|
||||||
menuUnblock.setVisible(false);
|
menuUnblock.setVisible(false);
|
||||||
|
menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
|
||||||
} else {
|
} else {
|
||||||
menuMucDetails.setVisible(false);
|
menuMucDetails.setVisible(false);
|
||||||
menuInviteContact.setTitle(R.string.conference_with);
|
|
||||||
if (this.getSelectedConversation().isBlocked()) {
|
if (this.getSelectedConversation().isBlocked()) {
|
||||||
menuBlock.setVisible(false);
|
menuBlock.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -417,4 +417,7 @@
|
||||||
<string name="remove_admin_privileges">Remove admin privileges</string>
|
<string name="remove_admin_privileges">Remove admin privileges</string>
|
||||||
<string name="remove_from_room">Remove from room</string>
|
<string name="remove_from_room">Remove from room</string>
|
||||||
<string name="could_not_change_affiliation">Could not change affiliation</string>
|
<string name="could_not_change_affiliation">Could not change affiliation</string>
|
||||||
|
<string name="ban_user_from_conference">Ban user from conference</string>
|
||||||
|
<string name="removing_from_public_conference">You are trying to remove %s from a public conference. The only way to do that is to ban that user for ever.</string>
|
||||||
|
<string name="ban_now">Ban now</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue