added limited private muc chat feature (messages are now properly marked) - long press on user icon will sent private messages. fixed #259

This commit is contained in:
iNPUTmice 2014-08-10 15:27:44 +02:00
parent 6de53791b5
commit b35aa3ed07
7 changed files with 155 additions and 30 deletions

View File

@ -277,4 +277,7 @@
<string name="error_saving_avatar">Could not save avatar to disk</string> <string name="error_saving_avatar">Could not save avatar to disk</string>
<string name="or_long_press_for_default">(Or long press to bring back default)</string> <string name="or_long_press_for_default">(Or long press to bring back default)</string>
<string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string> <string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string>
<string name="private_message">in private</string>
<string name="private_message_to">in private to %s</string>
<string name="send_private_message_to">Send private message to %s</string>
</resources> </resources>

View File

@ -33,6 +33,7 @@ public class Message extends AbstractEntity {
public static final int TYPE_IMAGE = 1; public static final int TYPE_IMAGE = 1;
public static final int TYPE_AUDIO = 2; public static final int TYPE_AUDIO = 2;
public static final int TYPE_STATUS = 3; public static final int TYPE_STATUS = 3;
public static final int TYPE_PRIVATE = 4;
public static String CONVERSATION = "conversationUuid"; public static String CONVERSATION = "conversationUuid";
public static String COUNTERPART = "counterpart"; public static String COUNTERPART = "counterpart";
@ -257,4 +258,8 @@ public class Message extends AbstractEntity {
message.setConversation(conversation); message.setConversation(conversation);
return message; return message;
} }
public void setCounterpart(String counterpart) {
this.counterpart = counterpart;
}
} }

View File

@ -22,6 +22,9 @@ public class MessageGenerator {
packet.setTo(message.getCounterpart()); packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT); packet.setType(MessagePacket.TYPE_CHAT);
packet.addChild("markable", "urn:xmpp:chat-markers:0"); packet.addChild("markable", "urn:xmpp:chat-markers:0");
} else if (message.getType() == Message.TYPE_PRIVATE) {
packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT);
} else { } else {
packet.setTo(message.getCounterpart().split("/")[0]); packet.setTo(message.getCounterpart().split("/")[0]);
packet.setType(MessagePacket.TYPE_GROUPCHAT); packet.setType(MessagePacket.TYPE_GROUPCHAT);

View File

@ -40,6 +40,14 @@ public class MessageParser extends AbstractParser implements
packet.getBody(), Message.ENCRYPTION_NONE, packet.getBody(), Message.ENCRYPTION_NONE,
Message.STATUS_RECIEVED); Message.STATUS_RECIEVED);
} }
if (conversation.getMode() == Conversation.MODE_MULTI
&& fromParts.length >= 2) {
finishedMessage.setType(Message.TYPE_PRIVATE);
finishedMessage.setPresence(fromParts[1]);
finishedMessage.setTrueCounterpart(conversation.getMucOptions()
.getTrueCounterpart(fromParts[1]));
}
finishedMessage.setTime(getTimestamp(packet)); finishedMessage.setTime(getTimestamp(packet));
return finishedMessage; return finishedMessage;
} }
@ -113,7 +121,8 @@ public class MessageParser extends AbstractParser implements
private Message parseGroupchat(MessagePacket packet, Account account) { private Message parseGroupchat(MessagePacket packet, Account account) {
int status; int status;
String[] fromParts = packet.getFrom().split("/"); String[] fromParts = packet.getFrom().split("/");
if (mXmppConnectionService.find(account.pendingConferenceLeaves,account,fromParts[0]) != null) { if (mXmppConnectionService.find(account.pendingConferenceLeaves,
account, fromParts[0]) != null) {
return null; return null;
} }
Conversation conversation = mXmppConnectionService Conversation conversation = mXmppConnectionService
@ -150,7 +159,8 @@ public class MessageParser extends AbstractParser implements
} }
finishedMessage.setTime(getTimestamp(packet)); finishedMessage.setTime(getTimestamp(packet));
if (status == Message.STATUS_RECIEVED) { if (status == Message.STATUS_RECIEVED) {
finishedMessage.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterPart)); finishedMessage.setTrueCounterpart(conversation.getMucOptions()
.getTrueCounterpart(counterPart));
} }
return finishedMessage; return finishedMessage;
} }
@ -180,7 +190,7 @@ public class MessageParser extends AbstractParser implements
} }
if (status == Message.STATUS_RECIEVED) { if (status == Message.STATUS_RECIEVED) {
fullJid = message.getAttribute("from"); fullJid = message.getAttribute("from");
if (fullJid == null ) { if (fullJid == null) {
return null; return null;
} else { } else {
updateLastseen(message, account, true); updateLastseen(message, account, true);
@ -195,6 +205,7 @@ public class MessageParser extends AbstractParser implements
Conversation conversation = mXmppConnectionService Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, parts[0], false); .findOrCreateConversation(account, parts[0], false);
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
String pgpBody = getPgpBody(message); String pgpBody = getPgpBody(message);
Message finishedMessage; Message finishedMessage;
if (pgpBody != null) { if (pgpBody != null) {
@ -206,6 +217,16 @@ public class MessageParser extends AbstractParser implements
Message.ENCRYPTION_NONE, status); Message.ENCRYPTION_NONE, status);
} }
finishedMessage.setTime(getTimestamp(message)); finishedMessage.setTime(getTimestamp(message));
if (conversation.getMode() == Conversation.MODE_MULTI
&& parts.length >= 2) {
finishedMessage.setType(Message.TYPE_PRIVATE);
finishedMessage.setPresence(parts[1]);
finishedMessage.setTrueCounterpart(conversation.getMucOptions()
.getTrueCounterpart(parts[1]));
}
return finishedMessage; return finishedMessage;
} }
@ -216,9 +237,10 @@ public class MessageParser extends AbstractParser implements
} }
private void parseNormal(Element packet, Account account) { private void parseNormal(Element packet, Account account) {
if (packet.hasChild("event","http://jabber.org/protocol/pubsub#event")) { if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
Element event = packet.findChild("event","http://jabber.org/protocol/pubsub#event"); Element event = packet.findChild("event",
parseEvent(event,packet.getAttribute("from"),account); "http://jabber.org/protocol/pubsub#event");
parseEvent(event, packet.getAttribute("from"), account);
} }
if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) { if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
String id = packet String id = packet
@ -235,8 +257,9 @@ public class MessageParser extends AbstractParser implements
updateLastseen(packet, account, false); updateLastseen(packet, account, false);
mXmppConnectionService.markMessage(account, fromParts[0], id, mXmppConnectionService.markMessage(account, fromParts[0], id,
Message.STATUS_SEND_RECEIVED); Message.STATUS_SEND_RECEIVED);
} else if (packet.hasChild("x","http://jabber.org/protocol/muc#user")) { } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
Element x = packet.findChild("x","http://jabber.org/protocol/muc#user"); Element x = packet.findChild("x",
"http://jabber.org/protocol/muc#user");
if (x.hasChild("invite")) { if (x.hasChild("invite")) {
Conversation conversation = mXmppConnectionService Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, .findOrCreateConversation(account,
@ -244,15 +267,15 @@ public class MessageParser extends AbstractParser implements
if (!conversation.getMucOptions().online()) { if (!conversation.getMucOptions().online()) {
mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
} }
} }
} else if (packet.hasChild("x", "jabber:x:conference")) { } else if (packet.hasChild("x", "jabber:x:conference")) {
Element x = packet.findChild("x", "jabber:x:conference"); Element x = packet.findChild("x", "jabber:x:conference");
String jid = x.getAttribute("jid"); String jid = x.getAttribute("jid");
if (jid!=null) { if (jid != null) {
Conversation conversation = mXmppConnectionService Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account,jid, true); .findOrCreateConversation(account, jid, true);
if (!conversation.getMucOptions().online()) { if (!conversation.getMucOptions().online()) {
mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();
@ -264,11 +287,12 @@ public class MessageParser extends AbstractParser implements
private void parseEvent(Element event, String from, Account account) { private void parseEvent(Element event, String from, Account account) {
Element items = event.findChild("items"); Element items = event.findChild("items");
String node = items.getAttribute("node"); String node = items.getAttribute("node");
if (node!=null) { if (node != null) {
if (node.equals("urn:xmpp:avatar:metadata")) { if (node.equals("urn:xmpp:avatar:metadata")) {
Avatar avatar = Avatar.parseMetadata(items); Avatar avatar = Avatar.parseMetadata(items);
avatar.owner = from; avatar.owner = from;
if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { if (mXmppConnectionService.getFileBackend().isAvatarCached(
avatar)) {
if (account.getJid().equals(from)) { if (account.getJid().equals(from)) {
account.setAvatar(avatar.getFilename()); account.setAvatar(avatar.getFilename());
} else { } else {
@ -279,10 +303,11 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.fetchAvatar(account, avatar); mXmppConnectionService.fetchAvatar(account, avatar);
} }
} else { } else {
Log.d("xmppService",account.getJid()+": "+node+" from "+from); Log.d("xmppService", account.getJid() + ": " + node + " from "
+ from);
} }
} else { } else {
Log.d("xmppService",event.toString()); Log.d("xmppService", event.toString());
} }
} }
@ -345,8 +370,7 @@ public class MessageParser extends AbstractParser implements
message.markUnread(); message.markUnread();
} else { } else {
message.getConversation().markRead(); message.getConversation().markRead();
lastCarbonMessageReceived = SystemClock lastCarbonMessageReceived = SystemClock.elapsedRealtime();
.elapsedRealtime();
notify = false; notify = false;
} }
} }
@ -366,11 +390,15 @@ public class MessageParser extends AbstractParser implements
if ((mXmppConnectionService.confirmMessages()) if ((mXmppConnectionService.confirmMessages())
&& ((packet.getId() != null))) { && ((packet.getId() != null))) {
if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0"); MessagePacket receipt = mXmppConnectionService
.getMessageGenerator().received(account, packet,
"urn:xmpp:chat-markers:0");
mXmppConnectionService.sendMessagePacket(account, receipt); mXmppConnectionService.sendMessagePacket(account, receipt);
} }
if (packet.hasChild("request", "urn:xmpp:receipts")) { if (packet.hasChild("request", "urn:xmpp:receipts")) {
MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, packet, "urn:xmpp:receipts"); MessagePacket receipt = mXmppConnectionService
.getMessageGenerator().received(account, packet,
"urn:xmpp:receipts");
mXmppConnectionService.sendMessagePacket(account, receipt); mXmppConnectionService.sendMessagePacket(account, receipt);
} }
} }
@ -383,9 +411,10 @@ public class MessageParser extends AbstractParser implements
} }
private void parseHeadline(MessagePacket packet, Account account) { private void parseHeadline(MessagePacket packet, Account account) {
if (packet.hasChild("event","http://jabber.org/protocol/pubsub#event")) { if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
Element event = packet.findChild("event","http://jabber.org/protocol/pubsub#event"); Element event = packet.findChild("event",
parseEvent(event,packet.getFrom(),account); "http://jabber.org/protocol/pubsub#event");
parseEvent(event, packet.getFrom(), account);
} }
} }
} }

View File

@ -528,7 +528,7 @@ public class XmppConnectionService extends Service {
} else { } else {
message.getConversation().endOtrIfNeeded(); message.getConversation().endOtrIfNeeded();
failWaitingOtrMessages(message.getConversation()); failWaitingOtrMessages(message.getConversation());
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { if (message.getConversation().getMode() == Conversation.MODE_SINGLE || message.getType() == Message.TYPE_PRIVATE) {
message.setStatus(Message.STATUS_SEND); message.setStatus(Message.STATUS_SEND);
} }
packet = mMessageGenerator.generateChat(message); packet = mMessageGenerator.generateChat(message);

View File

@ -17,6 +17,7 @@ import eu.siacs.conversations.ui.EditMessage.OnEnterPressed;
import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected; import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected;
import eu.siacs.conversations.ui.adapter.MessageAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Fragment; import android.app.Fragment;
@ -160,10 +161,22 @@ public class ConversationFragment extends Fragment {
private void sendMessage() { private void sendMessage() {
if (mEditMessage.getText().length() < 1) if (mEditMessage.getText().length() < 1) {
if (this.conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setNextPresence(null);
updateChatMsgHint();
}
return; return;
}
Message message = new Message(conversation, mEditMessage.getText() Message message = new Message(conversation, mEditMessage.getText()
.toString(), conversation.getNextEncryption()); .toString(), conversation.getNextEncryption());
if (conversation.getMode() == Conversation.MODE_MULTI) {
if (conversation.getNextPresence() != null) {
message.setPresence(conversation.getNextPresence());
message.setType(Message.TYPE_PRIVATE);
conversation.setNextPresence(null);
}
}
if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) { if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
sendOtrMessage(message); sendOtrMessage(message);
} else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
@ -230,7 +243,24 @@ public class ConversationFragment extends Fragment {
@Override @Override
public void onContactPictureClicked(Message message) { public void onContactPictureClicked(Message message) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) { if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
highlightInConference(message.getCounterpart()); if (message.getPresence() != null) {
highlightInConference(message.getPresence());
} else {
highlightInConference(message.getCounterpart());
}
}
}
});
messageListAdapter.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
@Override
public void onContactPictureLongClicked(Message message) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
if (message.getPresence() != null) {
privateMessageWith(message.getPresence());
} else {
privateMessageWith(message.getCounterpart());
}
} }
} }
}); });
@ -238,6 +268,12 @@ public class ConversationFragment extends Fragment {
return view; return view;
} }
protected void privateMessageWith(String counterpart) {
this.mEditMessage.setHint(getString(R.string.send_private_message_to,counterpart));
this.mEditMessage.setText("");
this.conversation.setNextPresence(counterpart);
}
protected void highlightInConference(String nick) { protected void highlightInConference(String nick) {
String oldString = mEditMessage.getText().toString().trim(); String oldString = mEditMessage.getText().toString().trim();
@ -303,6 +339,9 @@ public class ConversationFragment extends Fragment {
activity.invalidateOptionsMenu(); activity.invalidateOptionsMenu();
} }
} }
if (this.conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setNextPresence(null);
}
} }
private void decryptMessage(Message message) { private void decryptMessage(Message message) {
@ -406,6 +445,7 @@ public class ConversationFragment extends Fragment {
messagesView.setSelection(size - 1); messagesView.setSelection(size - 1);
} }
mEditMessage.setText(""); mEditMessage.setText("");
updateChatMsgHint();
} }
protected void updateStatusMessages() { protected void updateStatusMessages() {

View File

@ -14,6 +14,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.text.Html;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -40,6 +41,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private DisplayMetrics metrics; private DisplayMetrics metrics;
private OnContactPictureClicked mOnContactPictureClickedListener; private OnContactPictureClicked mOnContactPictureClickedListener;
private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
public MessageAdapter(ConversationActivity activity, List<Message> messages) { public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages); super(activity, 0, messages);
@ -61,6 +63,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public void setOnContactPictureClicked(OnContactPictureClicked listener) { public void setOnContactPictureClicked(OnContactPictureClicked listener) {
this.mOnContactPictureClickedListener = listener; this.mOnContactPictureClickedListener = listener;
} }
public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) {
this.mOnContactPictureLongClickedListener = listener;
}
@Override @Override
public int getViewTypeCount() { public int getViewTypeCount() {
@ -120,7 +126,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (contact != null) { if (contact != null) {
info = contact.getDisplayName(); info = contact.getDisplayName();
} else { } else {
info = message.getCounterpart(); if (message.getPresence() != null) {
info = message.getPresence();
} else {
info = message.getCounterpart();
}
} }
} }
break; break;
@ -190,14 +200,33 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.messageBody.setTextIsSelectable(false); viewHolder.messageBody.setTextIsSelectable(false);
} }
private void displayTextMessage(ViewHolder viewHolder, String text) { private void displayTextMessage(ViewHolder viewHolder, Message message) {
if (viewHolder.download_button != null) { if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
} }
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setVisibility(View.VISIBLE);
if (text != null) { if (message.getBody() != null) {
viewHolder.messageBody.setText(text.trim()); if (message.getType() != Message.TYPE_PRIVATE) {
viewHolder.messageBody.setText(message.getBody().trim());
} else {
StringBuilder builder = new StringBuilder();
builder.append(message.getBody().trim());
builder.append("&nbsp;<b><i>(");
if (message.getStatus() <= Message.STATUS_RECIEVED) {
builder.append(activity.getString(R.string.private_message));
} else {
String to;
if (message.getPresence() != null) {
to = message.getPresence();
} else {
to = message.getCounterpart();
}
builder.append(activity.getString(R.string.private_message_to, to));
}
builder.append(")</i></b>");
viewHolder.messageBody.setText(Html.fromHtml(builder.toString()));
}
} else { } else {
viewHolder.messageBody.setText(""); viewHolder.messageBody.setText("");
} }
@ -376,6 +405,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
}); });
viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item);
return true;
} else {
return false;
}
}
});
} }
} }
@ -426,7 +467,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder); displayDecryptionFailed(viewHolder);
} else { } else {
displayTextMessage(viewHolder, item.getBody()); displayTextMessage(viewHolder, item);
} }
} }
@ -474,4 +515,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public interface OnContactPictureClicked { public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message); public void onContactPictureClicked(Message message);
} }
public interface OnContactPictureLongClicked {
public void onContactPictureLongClicked(Message message);
}
} }