aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/drawable-hdpi/ic_action_chat.pngbin0 -> 295 bytes
-rw-r--r--res/drawable-mdpi/ic_action_chat.pngbin0 -> 261 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_chat.pngbin0 -> 310 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_chat.pngbin0 -> 383 bytes
-rw-r--r--res/layout/activity_new_conversation.xml3
-rw-r--r--res/layout/contact.xml3
-rw-r--r--res/menu/newconversation_context.xml14
-rw-r--r--res/values/strings.xml1
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java39
-rw-r--r--src/eu/siacs/conversations/ui/ConversationFragment.java7
-rw-r--r--src/eu/siacs/conversations/ui/MucDetailsActivity.java2
-rw-r--r--src/eu/siacs/conversations/ui/NewConversationActivity.java197
-rw-r--r--src/eu/siacs/conversations/xmpp/XmppConnection.java49
-rw-r--r--src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java9
14 files changed, 273 insertions, 51 deletions
diff --git a/res/drawable-hdpi/ic_action_chat.png b/res/drawable-hdpi/ic_action_chat.png
new file mode 100644
index 00000000..0847ac46
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_chat.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_chat.png b/res/drawable-mdpi/ic_action_chat.png
new file mode 100644
index 00000000..8fdb5d75
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_chat.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_chat.png b/res/drawable-xhdpi/ic_action_chat.png
new file mode 100644
index 00000000..8a9a4314
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_chat.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_chat.png b/res/drawable-xxhdpi/ic_action_chat.png
new file mode 100644
index 00000000..04000fd0
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_action_chat.png
Binary files differ
diff --git a/res/layout/activity_new_conversation.xml b/res/layout/activity_new_conversation.xml
index 88e1b802..a2b00af1 100644
--- a/res/layout/activity_new_conversation.xml
+++ b/res/layout/activity_new_conversation.xml
@@ -49,7 +49,8 @@
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/contacts_header"
- tools:listitem="@layout/contact" >
+ tools:listitem="@layout/contact"
+ android:choiceMode="multipleChoice">
</ListView>
</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/contact.xml b/res/layout/contact.xml
index 82404d3f..fcf8e8b9 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">
<ImageView
android:id="@+id/contact_photo"
diff --git a/res/menu/newconversation_context.xml b/res/menu/newconversation_context.xml
new file mode 100644
index 00000000..64c630f1
--- /dev/null
+++ b/res/menu/newconversation_context.xml
@@ -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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 062b2b0d..b3a3dcd9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -29,4 +29,5 @@
<string name="ask_again"><u>Click to ask again</u></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="start_conversation">Start Conversation</string>
</resources>
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index 99fc961a..54fbbbbd 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<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);
+ }
+
+ }
} \ 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 7385d8a9..d72dae8e 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 adc44d51..75906604 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 537e6c51..45e00cc0 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<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() {
@@ -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 1f0f23a2..dadd310a 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<String> discoFeatures = new HashSet<String>();
+ private List<String> discoItems = new ArrayList<String>();
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<Element> elements = packet.findChild("query")
- .getChildren();
+ List<Element> 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<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() {
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 f31a78e6..941bda4f 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;