From afe12447098424254366f5714645420973b37f36 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 15 Mar 2014 04:59:18 +0100 Subject: [PATCH] muc creation --- res/drawable-hdpi/ic_action_chat.png | Bin 0 -> 295 bytes res/drawable-mdpi/ic_action_chat.png | Bin 0 -> 261 bytes res/drawable-xhdpi/ic_action_chat.png | Bin 0 -> 310 bytes res/drawable-xxhdpi/ic_action_chat.png | Bin 0 -> 383 bytes res/layout/activity_new_conversation.xml | 3 +- res/layout/contact.xml | 3 +- res/menu/newconversation_context.xml | 14 ++ res/values/strings.xml | 1 + .../services/XmppConnectionService.java | 39 +++- .../ui/ConversationFragment.java | 7 +- .../conversations/ui/MucDetailsActivity.java | 2 - .../ui/NewConversationActivity.java | 197 +++++++++++++++--- .../conversations/xmpp/XmppConnection.java | 49 ++++- .../xmpp/stanzas/MessagePacket.java | 9 +- 14 files changed, 273 insertions(+), 51 deletions(-) create mode 100644 res/drawable-hdpi/ic_action_chat.png create mode 100644 res/drawable-mdpi/ic_action_chat.png create mode 100644 res/drawable-xhdpi/ic_action_chat.png create mode 100644 res/drawable-xxhdpi/ic_action_chat.png create mode 100644 res/menu/newconversation_context.xml diff --git a/res/drawable-hdpi/ic_action_chat.png b/res/drawable-hdpi/ic_action_chat.png new file mode 100644 index 0000000000000000000000000000000000000000..0847ac4662f30520cd7efee2c29cea0cc33c3ea2 GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GCp}#pLn>~)nSPLy$xy&`@eRi+4`$0u-i@ZSFG}rM%CNWU z-s*Jro6RhXH|rcc`Ad$cF){L=f1CUx8KC9^|2Jt#8@lOto3AQAVadO~_w4NcvY?X- ze$2kIFlU|h%qhz&ZvL(AXIu5C+TcUro>J!cqtRvygz|4Tc-82-dHa3Z{8+}cX#1Px zE}O$@{0(=-FwYY7GGgi!{_&7A=6IH;#OmJ0otjI0RvzF8y>W`K^_W$SH}f=ki}1B}lL?P7vuSInZ&&(9G=H(&oAM&&{*lEq*M~ zaq^diTc0}IY1cX6a{K~YG~buMj~fdZ!cM<@$}neIvNyBA2eFR+$qAhMwtQxp{XwnsAeYfn?40oMn{1_ow^&HFU(vx!X50Xmt%)78&q Iol`;+0ER7O;Q#;t literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_action_chat.png b/res/drawable-xhdpi/ic_action_chat.png new file mode 100644 index 0000000000000000000000000000000000000000..8a9a431411f0b633baabdfc9d7375e98a0dd6628 GIT binary patch literal 310 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XZg{#lhE&{ob8RE$u<( zQ>J9oG;jX{;v4+#{GIctNt5rE0`&sH!8OnHp3FJlS9H%yb9%V=HAcHnIX^g9 z_wc^o+tB~eRy;uTUN+;^hqhu1xZba2D6Q~k)#0*#IraPfR9mUm8+9Mwl)h}f#l)6b zl{4|)t0$5VRXiB?%QQCsS+)BF>l?|&2g(A7Gke1PeeeCZ z`^^fsyICzGJ^Na}{PBg)j?7#uXY{Xp&#!t`iJw21RQmNDKH;Vg7@R#Wp3valQ*WC8 zLFW07$NP>M1Zk$)Z{2W@k%^;$foZ~$qp=s#y2|D6F>)v{_^xBTzW&8@@teuA$CI)c z^^$*TK8h + tools:listitem="@layout/contact" + android:choiceMode="multipleChoice"> \ No newline at end of file diff --git a/res/layout/contact.xml b/res/layout/contact.xml index 82404d3f0..fcf8e8b97 100644 --- a/res/layout/contact.xml +++ b/res/layout/contact.xml @@ -3,7 +3,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="8dp" - android:paddingBottom="8dp"> + android:paddingBottom="8dp" + android:background="?android:attr/activatedBackgroundIndicator"> + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 062b2b0d7..b3a3dcd96 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -29,4 +29,5 @@ Click to ask again OTR fingerprint No OTR Fingerprint generated. Just go ahead an start an encrypted conversation + Start Conversation diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 99fc961a8..54fbbbbd3 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -148,8 +148,20 @@ public class XmppConnectionService extends Service { } } else if (packet.getType() == MessagePacket.TYPE_ERROR) { message = MessageParser.parseError(packet, account, service); - } else { - // Log.d(LOGTAG, "unparsed message " + packet.toString()); + } else if (packet.getType() == MessagePacket.TYPE_NORMAL) { + 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)) { return; @@ -223,7 +235,7 @@ public class XmppConnectionService extends Service { && (packet.findChild("x").getAttribute("xmlns") .startsWith("http://jabber.org/protocol/muc"))) { Conversation muc = findMuc(packet.getAttribute("from").split( - "/")[0]); + "/")[0],account); if (muc != null) { int error = muc.getMucOptions().getError(); 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) { - if (conversation.getContactJid().split("/")[0].equals(name)) { + if (conversation.getContactJid().split("/")[0].equals(name)&&(conversation.getAccount() == account)) { return conversation; } } @@ -1246,4 +1258,21 @@ public class XmppConnectionService extends Service { account.getXmppConnection().sendMessagePacket(packet); } } + + public void inviteToConference(Conversation conversation, + List 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); + } + + } } \ No newline at end of file diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index 7385d8a96..d72dae8e6 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -207,12 +207,14 @@ public class ConversationFragment extends Fragment { viewHolder.imageView = (ImageView) view .findViewById(R.id.message_photo); viewHolder.imageView.setImageBitmap(selfBitmap); + viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator); break; case RECIEVED: view = (View) inflater.inflate( R.layout.message_recieved, null); viewHolder.imageView = (ImageView) view .findViewById(R.id.message_photo); + viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator); if (item.getConversation().getMode() == Conversation.MODE_SINGLE) { viewHolder.imageView.setImageBitmap(mBitmapCache @@ -239,7 +241,6 @@ public class ConversationFragment extends Fragment { .findViewById(R.id.message_body); viewHolder.time = (TextView) view .findViewById(R.id.message_time); - viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); @@ -279,7 +280,9 @@ public class ConversationFragment extends Fragment { viewHolder.messageBody.setTextColor(0xff000000); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); - viewHolder.indicator.setVisibility(View.GONE); + if (item.getStatus() != Message.STATUS_ERROR) { + viewHolder.indicator.setVisibility(View.GONE); + } } } else { viewHolder.indicator.setVisibility(View.GONE); diff --git a/src/eu/siacs/conversations/ui/MucDetailsActivity.java b/src/eu/siacs/conversations/ui/MucDetailsActivity.java index adc44d51a..75906604a 100644 --- a/src/eu/siacs/conversations/ui/MucDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/MucDetailsActivity.java @@ -40,11 +40,9 @@ public class MucDetailsActivity extends XmppActivity { @Override public void onClick(View arg0) { - Log.d("gultsch","on click change muc"); MucOptions options = conversation.getMucOptions(); String nick = mYourNick.getText().toString(); if (!options.getNick().equals(nick)) { - Log.d("gultsch","call to change muc"); xmppConnectionService.renameInMuc(conversation, nick); finish(); } diff --git a/src/eu/siacs/conversations/ui/NewConversationActivity.java b/src/eu/siacs/conversations/ui/NewConversationActivity.java index 537e6c51f..45e00cc06 100644 --- a/src/eu/siacs/conversations/ui/NewConversationActivity.java +++ b/src/eu/siacs/conversations/ui/NewConversationActivity.java @@ -1,7 +1,9 @@ package eu.siacs.conversations.ui; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.net.URLDecoder; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -16,11 +18,16 @@ import eu.siacs.conversations.utils.Validator; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; @@ -49,6 +56,110 @@ public class NewConversationActivity extends XmppActivity { protected String searchString = ""; private TextView contactsHeader; private List accounts; + private List selectedContacts = new ArrayList(); + + 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 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() { @@ -86,6 +197,20 @@ public class NewConversationActivity extends XmppActivity { contactsAdapter.notifyDataSetChanged(); 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 protected void onCreate(Bundle savedInstanceState) { @@ -143,43 +268,40 @@ public class NewConversationActivity extends XmppActivity { } }; contactsView.setAdapter(contactsAdapter); - final Activity activity = this; + contactsView.setMultiChoiceModeListener(actionModeCallback); contactsView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView arg0, final View view, int pos, long arg3) { - final Contact clickedContact = aggregatedContacts.get(pos); - - 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(); + if (!isActionMode) { + Contact clickedContact = aggregatedContacts.get(pos); + startConversation(clickedContact); + } else { - if (clickedContact.getAccount()==null) { - clickedContact.setAccount(accounts.get(0)); - } - showIsMucDialogIfNeeded(clickedContact); + actionMode.invalidate(); } } }); - contactsView.setOnItemLongClickListener(new OnItemLongClickListener() { + contactsView.setOnItemLongClickListener(this.onLongClickListener); + } + + public void startConversation(final Contact contact) { + if ((contact.getAccount()==null)&&(accounts.size()>1)) { + getAccountChooser(new OnClickListener() { - @Override - public boolean onItemLongClick(AdapterView arg0, View arg1, - int pos, long arg3) { - Intent intent = new Intent(activity,ContactDetailsActivity.class); - intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); - intent.putExtra("uuid", aggregatedContacts.get(pos).getUuid()); - startActivity(intent); - return true; + @Override + public void onClick(DialogInterface dialog, int which) { + contact.setAccount(accounts.get(which)); + showIsMucDialogIfNeeded(contact); + } + }).show(); + } else { + if (contact.getAccount()==null) { + contact.setAccount(accounts.get(0)); } - }); + showIsMucDialogIfNeeded(contact); + } } 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); + } + } diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 1f0f23a23..dadd310aa 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -15,6 +15,7 @@ import java.security.SecureRandom; import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.HashSet; import java.util.Hashtable; import java.util.List; @@ -66,6 +67,7 @@ public class XmppConnection implements Runnable { private boolean shouldAuthenticate = true; private Element streamFeatures; private HashSet discoFeatures = new HashSet(); + private List discoItems = new ArrayList(); private String streamId = null; @@ -550,7 +552,8 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(enable); } sendInitialPresence(); - sendServiceDiscovery(); + sendServiceDiscoveryInfo(); + sendServiceDiscoveryItems(); if (statusListener != null) { 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); - iq.setAttribute("to", account.getServer()); - Element query = new Element("query"); - query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info"); - iq.addChild(query); + iq.setTo(account.getServer()); + iq.query("http://jabber.org/protocol/disco#info"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.hasChild("query")) { - List elements = packet.findChild("query") - .getChildren(); + List elements = packet.query().getChildren(); for (int i = 0; i < elements.size(); ++i) { if (elements.get(i).getName().equals("feature")) { discoFeatures.add(elements.get(i).getAttribute( "var")); } } - } if (discoFeatures.contains("urn:xmpp:carbons:2")) { 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 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() { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); @@ -754,4 +770,17 @@ public class XmppConnection implements Runnable { public int getSentStanzas() { 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; + } } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index f31a78e64..941bda4f8 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -5,7 +5,7 @@ import eu.siacs.conversations.xml.Element; public class MessagePacket extends AbstractStanza { public static final int TYPE_CHAT = 0; 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_ERROR = 4; @@ -46,9 +46,10 @@ public class MessagePacket extends AbstractStanza { public int getType() { String type = getAttribute("type"); if (type==null) { - return TYPE_NO; - } - if (type.equals("chat")) { + return TYPE_NORMAL; + } else if (type.equals("normal")) { + return TYPE_NORMAL; + } else if (type.equals("chat")) { return TYPE_CHAT; } else if (type.equals("groupchat")) { return TYPE_GROUPCHAT;