muc creation

This commit is contained in:
Daniel Gultsch 2014-03-15 04:59:18 +01:00
parent f3b6c99693
commit afe1244709
14 changed files with 273 additions and 51 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View file

@ -49,7 +49,8 @@
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_below="@+id/contacts_header" android:layout_below="@+id/contacts_header"
tools:listitem="@layout/contact" > tools:listitem="@layout/contact"
android:choiceMode="multipleChoice">
</ListView> </ListView>
</RelativeLayout> </RelativeLayout>

View file

@ -3,7 +3,8 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp" android:padding="8dp"
android:paddingBottom="8dp"> android:paddingBottom="8dp"
android:background="?android:attr/activatedBackgroundIndicator">
<ImageView <ImageView
android:id="@+id/contact_photo" android:id="@+id/contact_photo"

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_contact_details"
android:showAsAction="ifRoom"
android:icon="@drawable/ic_action_person"
android:title="@string/action_contact_details" />
<item
android:id="@+id/action_start_conversation"
android:showAsAction="ifRoom"
android:icon="@drawable/ic_action_chat"
android:title="@string/start_conversation" />
</menu>

View file

@ -29,4 +29,5 @@
<string name="ask_again"><u>Click to ask again</u></string> <string name="ask_again"><u>Click to ask again</u></string>
<string name="show_otr_key">OTR fingerprint</string> <string name="show_otr_key">OTR fingerprint</string>
<string name="no_otr_fingerprint">No OTR Fingerprint generated. Just go ahead an start an encrypted conversation</string> <string name="no_otr_fingerprint">No OTR Fingerprint generated. Just go ahead an start an encrypted conversation</string>
<string name="start_conversation">Start Conversation</string>
</resources> </resources>

View file

@ -148,8 +148,20 @@ public class XmppConnectionService extends Service {
} }
} else if (packet.getType() == MessagePacket.TYPE_ERROR) { } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
message = MessageParser.parseError(packet, account, service); message = MessageParser.parseError(packet, account, service);
} else { } else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
// Log.d(LOGTAG, "unparsed message " + packet.toString()); if (packet.hasChild("x")) {
Element x = packet.findChild("x");
if (x.hasChild("invite")) {
findOrCreateConversation(account, packet.getFrom(), true);
if (convChangedListener != null) {
convChangedListener.onConversationListChanged();
}
Log.d(LOGTAG,"invitation received to "+packet.getFrom());
}
} else {
Log.d(LOGTAG, "unparsed message " + packet.toString());
}
} }
if ((message == null)||(message.getBody() == null)) { if ((message == null)||(message.getBody() == null)) {
return; return;
@ -223,7 +235,7 @@ public class XmppConnectionService extends Service {
&& (packet.findChild("x").getAttribute("xmlns") && (packet.findChild("x").getAttribute("xmlns")
.startsWith("http://jabber.org/protocol/muc"))) { .startsWith("http://jabber.org/protocol/muc"))) {
Conversation muc = findMuc(packet.getAttribute("from").split( Conversation muc = findMuc(packet.getAttribute("from").split(
"/")[0]); "/")[0],account);
if (muc != null) { if (muc != null) {
int error = muc.getMucOptions().getError(); int error = muc.getMucOptions().getError();
muc.getMucOptions().processPacket(packet); muc.getMucOptions().processPacket(packet);
@ -336,9 +348,9 @@ public class XmppConnectionService extends Service {
} }
protected Conversation findMuc(String name) { protected Conversation findMuc(String name, Account account) {
for (Conversation conversation : this.conversations) { for (Conversation conversation : this.conversations) {
if (conversation.getContactJid().split("/")[0].equals(name)) { if (conversation.getContactJid().split("/")[0].equals(name)&&(conversation.getAccount() == account)) {
return conversation; return conversation;
} }
} }
@ -1246,4 +1258,21 @@ public class XmppConnectionService extends Service {
account.getXmppConnection().sendMessagePacket(packet); account.getXmppConnection().sendMessagePacket(packet);
} }
} }
public void inviteToConference(Conversation conversation,
List<Contact> contacts) {
for(Contact contact : contacts) {
MessagePacket packet = new MessagePacket();
packet.setTo(conversation.getContactJid().split("/")[0]);
packet.setFrom(conversation.getAccount().getFullJid());
Element x = new Element("x");
x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
Element invite = new Element("invite");
invite.setAttribute("to", contact.getJid());
x.addChild(invite);
packet.addChild(x);
conversation.getAccount().getXmppConnection().sendMessagePacket(packet);
}
}
} }

View file

@ -207,12 +207,14 @@ public class ConversationFragment extends Fragment {
viewHolder.imageView = (ImageView) view viewHolder.imageView = (ImageView) view
.findViewById(R.id.message_photo); .findViewById(R.id.message_photo);
viewHolder.imageView.setImageBitmap(selfBitmap); viewHolder.imageView.setImageBitmap(selfBitmap);
viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
break; break;
case RECIEVED: case RECIEVED:
view = (View) inflater.inflate( view = (View) inflater.inflate(
R.layout.message_recieved, null); R.layout.message_recieved, null);
viewHolder.imageView = (ImageView) view viewHolder.imageView = (ImageView) view
.findViewById(R.id.message_photo); .findViewById(R.id.message_photo);
viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) { if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
viewHolder.imageView.setImageBitmap(mBitmapCache viewHolder.imageView.setImageBitmap(mBitmapCache
@ -239,7 +241,6 @@ public class ConversationFragment extends Fragment {
.findViewById(R.id.message_body); .findViewById(R.id.message_body);
viewHolder.time = (TextView) view viewHolder.time = (TextView) view
.findViewById(R.id.message_time); .findViewById(R.id.message_time);
viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
view.setTag(viewHolder); view.setTag(viewHolder);
} else { } else {
viewHolder = (ViewHolder) view.getTag(); viewHolder = (ViewHolder) view.getTag();
@ -279,7 +280,9 @@ public class ConversationFragment extends Fragment {
viewHolder.messageBody.setTextColor(0xff000000); viewHolder.messageBody.setTextColor(0xff000000);
viewHolder.messageBody.setTypeface(null, viewHolder.messageBody.setTypeface(null,
Typeface.NORMAL); Typeface.NORMAL);
viewHolder.indicator.setVisibility(View.GONE); if (item.getStatus() != Message.STATUS_ERROR) {
viewHolder.indicator.setVisibility(View.GONE);
}
} }
} else { } else {
viewHolder.indicator.setVisibility(View.GONE); viewHolder.indicator.setVisibility(View.GONE);

View file

@ -40,11 +40,9 @@ public class MucDetailsActivity extends XmppActivity {
@Override @Override
public void onClick(View arg0) { public void onClick(View arg0) {
Log.d("gultsch","on click change muc");
MucOptions options = conversation.getMucOptions(); MucOptions options = conversation.getMucOptions();
String nick = mYourNick.getText().toString(); String nick = mYourNick.getText().toString();
if (!options.getNick().equals(nick)) { if (!options.getNick().equals(nick)) {
Log.d("gultsch","call to change muc");
xmppConnectionService.renameInMuc(conversation, nick); xmppConnectionService.renameInMuc(conversation, nick);
finish(); finish();
} }

View file

@ -1,7 +1,9 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -16,11 +18,16 @@ import eu.siacs.conversations.utils.Validator;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener; import android.widget.AdapterView.OnItemLongClickListener;
@ -49,6 +56,110 @@ public class NewConversationActivity extends XmppActivity {
protected String searchString = ""; protected String searchString = "";
private TextView contactsHeader; private TextView contactsHeader;
private List<Account> accounts; private List<Account> accounts;
private List<Contact> selectedContacts = new ArrayList<Contact>();
private boolean isActionMode = false;
private ActionMode actionMode = null;
private AbsListView.MultiChoiceModeListener actionModeCallback = new AbsListView.MultiChoiceModeListener() {
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
menu.clear();
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.newconversation_context, menu);
SparseBooleanArray checkedItems = contactsView.getCheckedItemPositions();
selectedContacts.clear();
for(int i = 0; i < aggregatedContacts.size(); ++i) {
if (checkedItems.get(i, false)) {
selectedContacts.add(aggregatedContacts.get(i));
}
}
if (selectedContacts.size() == 0) {
menu.findItem(R.id.action_start_conversation).setVisible(false);
menu.findItem(R.id.action_contact_details).setVisible(false);
} else if (selectedContacts.size() == 1) {
menu.findItem(R.id.action_start_conversation).setVisible(true);
menu.findItem(R.id.action_contact_details).setVisible(true);
} else {
menu.findItem(R.id.action_start_conversation).setVisible(true);
menu.findItem(R.id.action_contact_details).setVisible(false);
}
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.action_start_conversation:
if (selectedContacts.size() == 1) {
startConversation(selectedContacts.get(0));
} else {
startConference();
}
break;
case R.id.action_contact_details:
Intent intent = new Intent(getApplicationContext(),ContactDetailsActivity.class);
intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
intent.putExtra("uuid", selectedContacts.get(0).getUuid());
startActivity(intent);
break;
default:
break;
}
// TODO Auto-generated method stub
return false;
}
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
}
};
private void startConference() {
if (accounts.size()>1) {
getAccountChooser(new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startConference(accounts.get(which), selectedContacts);
}
}).show();
} else {
startConference(accounts.get(0), selectedContacts);
}
}
private void startConference(Account account, List<Contact> contacts) {
SecureRandom random = new SecureRandom();
String mucName = new BigInteger(100,random).toString(32);
String serverName = account.getXmppConnection().getMucServer();
String jid = mucName+"@"+serverName;
Conversation conversation = xmppConnectionService.findOrCreateConversation(account, jid , true);
StringBuilder subject = new StringBuilder();
for(int i = 0; i < selectedContacts.size(); ++i) {
if (i+1!=selectedContacts.size()) {
subject.append(selectedContacts.get(i).getDisplayName()+", ");
} else {
subject.append(selectedContacts.get(i).getDisplayName());
}
}
xmppConnectionService.sendConversationSubject(conversation, subject.toString());
xmppConnectionService.inviteToConference(conversation, contacts);
switchToConversation(conversation, null);
}
protected void updateAggregatedContacts() { protected void updateAggregatedContacts() {
@ -87,6 +198,20 @@ public class NewConversationActivity extends XmppActivity {
contactsView.setScrollX(0); contactsView.setScrollX(0);
} }
private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View view,
int position, long arg3) {
if (!isActionMode) {
contactsView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
contactsView.setItemChecked(position,true);
actionMode = contactsView.startActionMode(actionModeCallback);
}
return true;
}
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -143,43 +268,40 @@ public class NewConversationActivity extends XmppActivity {
} }
}; };
contactsView.setAdapter(contactsAdapter); contactsView.setAdapter(contactsAdapter);
final Activity activity = this; contactsView.setMultiChoiceModeListener(actionModeCallback);
contactsView.setOnItemClickListener(new OnItemClickListener() { contactsView.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> arg0, final View view, public void onItemClick(AdapterView<?> arg0, final View view,
int pos, long arg3) { int pos, long arg3) {
final Contact clickedContact = aggregatedContacts.get(pos); if (!isActionMode) {
Contact clickedContact = aggregatedContacts.get(pos);
startConversation(clickedContact);
if ((clickedContact.getAccount()==null)&&(accounts.size()>1)) {
getAccountChooser(new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
clickedContact.setAccount(accounts.get(which));
showIsMucDialogIfNeeded(clickedContact);
}
}).show();
} else { } else {
if (clickedContact.getAccount()==null) { actionMode.invalidate();
clickedContact.setAccount(accounts.get(0));
}
showIsMucDialogIfNeeded(clickedContact);
} }
} }
}); });
contactsView.setOnItemLongClickListener(new OnItemLongClickListener() { contactsView.setOnItemLongClickListener(this.onLongClickListener);
}
@Override public void startConversation(final Contact contact) {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, if ((contact.getAccount()==null)&&(accounts.size()>1)) {
int pos, long arg3) { getAccountChooser(new OnClickListener() {
Intent intent = new Intent(activity,ContactDetailsActivity.class);
intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); @Override
intent.putExtra("uuid", aggregatedContacts.get(pos).getUuid()); public void onClick(DialogInterface dialog, int which) {
startActivity(intent); contact.setAccount(accounts.get(which));
return true; showIsMucDialogIfNeeded(contact);
}
}).show();
} else {
if (contact.getAccount()==null) {
contact.setAccount(accounts.get(0));
} }
}); showIsMucDialogIfNeeded(contact);
}
} }
protected AlertDialog getAccountChooser(OnClickListener listener) { protected AlertDialog getAccountChooser(OnClickListener listener) {
@ -329,4 +451,27 @@ public class NewConversationActivity extends XmppActivity {
} }
} }
} }
@Override
public void onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
this.isActionMode = true;
search.setEnabled(false);
}
@Override
public void onActionModeFinished(ActionMode mode) {
super.onActionModeFinished(mode);
this.isActionMode = false;
contactsView.clearChoices();
contactsView.requestLayout();
contactsView.post(new Runnable() {
@Override
public void run() {
contactsView.setChoiceMode(ListView.CHOICE_MODE_NONE);
}
});
search.setEnabled(true);
}
} }

View file

@ -15,6 +15,7 @@ import java.security.SecureRandom;
import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
@ -66,6 +67,7 @@ public class XmppConnection implements Runnable {
private boolean shouldAuthenticate = true; private boolean shouldAuthenticate = true;
private Element streamFeatures; private Element streamFeatures;
private HashSet<String> discoFeatures = new HashSet<String>(); private HashSet<String> discoFeatures = new HashSet<String>();
private List<String> discoItems = new ArrayList<String>();
private String streamId = null; private String streamId = null;
@ -550,7 +552,8 @@ public class XmppConnection implements Runnable {
tagWriter.writeStanzaAsync(enable); tagWriter.writeStanzaAsync(enable);
} }
sendInitialPresence(); sendInitialPresence();
sendServiceDiscovery(); sendServiceDiscoveryInfo();
sendServiceDiscoveryItems();
if (statusListener != null) { if (statusListener != null) {
statusListener.onStatusChanged(account); statusListener.onStatusChanged(account);
} }
@ -558,32 +561,45 @@ public class XmppConnection implements Runnable {
}); });
} }
private void sendServiceDiscovery() { private void sendServiceDiscoveryInfo() {
IqPacket iq = new IqPacket(IqPacket.TYPE_GET); IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.setAttribute("to", account.getServer()); iq.setTo(account.getServer());
Element query = new Element("query"); iq.query("http://jabber.org/protocol/disco#info");
query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
iq.addChild(query);
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.hasChild("query")) { List<Element> elements = packet.query().getChildren();
List<Element> elements = packet.findChild("query")
.getChildren();
for (int i = 0; i < elements.size(); ++i) { for (int i = 0; i < elements.size(); ++i) {
if (elements.get(i).getName().equals("feature")) { if (elements.get(i).getName().equals("feature")) {
discoFeatures.add(elements.get(i).getAttribute( discoFeatures.add(elements.get(i).getAttribute(
"var")); "var"));
} }
} }
}
if (discoFeatures.contains("urn:xmpp:carbons:2")) { if (discoFeatures.contains("urn:xmpp:carbons:2")) {
sendEnableCarbons(); sendEnableCarbons();
} }
} }
}); });
} }
private void sendServiceDiscoveryItems() {
IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.setTo(account.getServer());
iq.query("http://jabber.org/protocol/disco#items");
this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
List<Element> elements = packet.query().getChildren();
for (int i = 0; i < elements.size(); ++i) {
if (elements.get(i).getName().equals("item")) {
discoItems.add(elements.get(i).getAttribute(
"jid"));
}
}
}
});
}
private void sendEnableCarbons() { private void sendEnableCarbons() {
IqPacket iq = new IqPacket(IqPacket.TYPE_SET); IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
@ -754,4 +770,17 @@ public class XmppConnection implements Runnable {
public int getSentStanzas() { public int getSentStanzas() {
return this.stanzasSent; return this.stanzasSent;
} }
public String getMucServer() {
for(int i = 0; i < discoItems.size(); ++i) {
if (discoItems.get(i).contains("conference.")) {
return discoItems.get(i);
} else if (discoItems.get(i).contains("conf.")) {
return discoItems.get(i);
} else if (discoItems.get(i).contains("muc.")) {
return discoItems.get(i);
}
}
return null;
}
} }

View file

@ -5,7 +5,7 @@ import eu.siacs.conversations.xml.Element;
public class MessagePacket extends AbstractStanza { public class MessagePacket extends AbstractStanza {
public static final int TYPE_CHAT = 0; public static final int TYPE_CHAT = 0;
public static final int TYPE_UNKNOWN = 1; public static final int TYPE_UNKNOWN = 1;
public static final int TYPE_NO = 2; public static final int TYPE_NORMAL = 2;
public static final int TYPE_GROUPCHAT = 3; public static final int TYPE_GROUPCHAT = 3;
public static final int TYPE_ERROR = 4; public static final int TYPE_ERROR = 4;
@ -46,9 +46,10 @@ public class MessagePacket extends AbstractStanza {
public int getType() { public int getType() {
String type = getAttribute("type"); String type = getAttribute("type");
if (type==null) { if (type==null) {
return TYPE_NO; return TYPE_NORMAL;
} } else if (type.equals("normal")) {
if (type.equals("chat")) { return TYPE_NORMAL;
} else if (type.equals("chat")) {
return TYPE_CHAT; return TYPE_CHAT;
} else if (type.equals("groupchat")) { } else if (type.equals("groupchat")) {
return TYPE_GROUPCHAT; return TYPE_GROUPCHAT;