diff --git a/res/values/strings.xml b/res/values/strings.xml index 7faeeed99..89d1cc11c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -277,4 +277,7 @@ Could not save avatar to disk (Or long press to bring back default) Your server does not support the publication of avatars + in private + in private to %s + Send private message to %s diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java index 1fa5ce421..3fe20707d 100644 --- a/src/eu/siacs/conversations/entities/Message.java +++ b/src/eu/siacs/conversations/entities/Message.java @@ -33,6 +33,7 @@ public class Message extends AbstractEntity { public static final int TYPE_IMAGE = 1; public static final int TYPE_AUDIO = 2; public static final int TYPE_STATUS = 3; + public static final int TYPE_PRIVATE = 4; public static String CONVERSATION = "conversationUuid"; public static String COUNTERPART = "counterpart"; @@ -257,4 +258,8 @@ public class Message extends AbstractEntity { message.setConversation(conversation); return message; } + + public void setCounterpart(String counterpart) { + this.counterpart = counterpart; + } } diff --git a/src/eu/siacs/conversations/generator/MessageGenerator.java b/src/eu/siacs/conversations/generator/MessageGenerator.java index 4449a7ecd..26182aad0 100644 --- a/src/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/eu/siacs/conversations/generator/MessageGenerator.java @@ -22,6 +22,9 @@ public class MessageGenerator { packet.setTo(message.getCounterpart()); packet.setType(MessagePacket.TYPE_CHAT); 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 { packet.setTo(message.getCounterpart().split("/")[0]); packet.setType(MessagePacket.TYPE_GROUPCHAT); diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java index b54f6b989..44624cbe6 100644 --- a/src/eu/siacs/conversations/parser/MessageParser.java +++ b/src/eu/siacs/conversations/parser/MessageParser.java @@ -40,6 +40,14 @@ public class MessageParser extends AbstractParser implements packet.getBody(), Message.ENCRYPTION_NONE, 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)); return finishedMessage; } @@ -113,7 +121,8 @@ public class MessageParser extends AbstractParser implements private Message parseGroupchat(MessagePacket packet, Account account) { int status; 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; } Conversation conversation = mXmppConnectionService @@ -150,7 +159,8 @@ public class MessageParser extends AbstractParser implements } finishedMessage.setTime(getTimestamp(packet)); if (status == Message.STATUS_RECIEVED) { - finishedMessage.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterPart)); + finishedMessage.setTrueCounterpart(conversation.getMucOptions() + .getTrueCounterpart(counterPart)); } return finishedMessage; } @@ -180,7 +190,7 @@ public class MessageParser extends AbstractParser implements } if (status == Message.STATUS_RECIEVED) { fullJid = message.getAttribute("from"); - if (fullJid == null ) { + if (fullJid == null) { return null; } else { updateLastseen(message, account, true); @@ -195,6 +205,7 @@ public class MessageParser extends AbstractParser implements Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, parts[0], false); conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); + String pgpBody = getPgpBody(message); Message finishedMessage; if (pgpBody != null) { @@ -206,6 +217,16 @@ public class MessageParser extends AbstractParser implements Message.ENCRYPTION_NONE, status); } 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; } @@ -216,9 +237,10 @@ public class MessageParser extends AbstractParser implements } private void parseNormal(Element packet, Account account) { - if (packet.hasChild("event","http://jabber.org/protocol/pubsub#event")) { - Element event = packet.findChild("event","http://jabber.org/protocol/pubsub#event"); - parseEvent(event,packet.getAttribute("from"),account); + if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { + Element event = packet.findChild("event", + "http://jabber.org/protocol/pubsub#event"); + parseEvent(event, packet.getAttribute("from"), account); } if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) { String id = packet @@ -235,8 +257,9 @@ public class MessageParser extends AbstractParser implements updateLastseen(packet, account, false); mXmppConnectionService.markMessage(account, fromParts[0], id, Message.STATUS_SEND_RECEIVED); - } else if (packet.hasChild("x","http://jabber.org/protocol/muc#user")) { - Element x = packet.findChild("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"); if (x.hasChild("invite")) { Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, @@ -244,15 +267,15 @@ public class MessageParser extends AbstractParser implements if (!conversation.getMucOptions().online()) { mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.updateConversationUi(); - } + } } } else if (packet.hasChild("x", "jabber:x:conference")) { Element x = packet.findChild("x", "jabber:x:conference"); String jid = x.getAttribute("jid"); - if (jid!=null) { + if (jid != null) { Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account,jid, true); + .findOrCreateConversation(account, jid, true); if (!conversation.getMucOptions().online()) { mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.updateConversationUi(); @@ -264,11 +287,12 @@ public class MessageParser extends AbstractParser implements private void parseEvent(Element event, String from, Account account) { Element items = event.findChild("items"); String node = items.getAttribute("node"); - if (node!=null) { + if (node != null) { if (node.equals("urn:xmpp:avatar:metadata")) { Avatar avatar = Avatar.parseMetadata(items); avatar.owner = from; - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (mXmppConnectionService.getFileBackend().isAvatarCached( + avatar)) { if (account.getJid().equals(from)) { account.setAvatar(avatar.getFilename()); } else { @@ -279,10 +303,11 @@ public class MessageParser extends AbstractParser implements mXmppConnectionService.fetchAvatar(account, avatar); } } else { - Log.d("xmppService",account.getJid()+": "+node+" from "+from); + Log.d("xmppService", account.getJid() + ": " + node + " from " + + from); } } else { - Log.d("xmppService",event.toString()); + Log.d("xmppService", event.toString()); } } @@ -345,8 +370,7 @@ public class MessageParser extends AbstractParser implements message.markUnread(); } else { message.getConversation().markRead(); - lastCarbonMessageReceived = SystemClock - .elapsedRealtime(); + lastCarbonMessageReceived = SystemClock.elapsedRealtime(); notify = false; } } @@ -366,11 +390,15 @@ public class MessageParser extends AbstractParser implements if ((mXmppConnectionService.confirmMessages()) && ((packet.getId() != null))) { 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); } 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); } } @@ -383,9 +411,10 @@ public class MessageParser extends AbstractParser implements } private void parseHeadline(MessagePacket packet, Account account) { - if (packet.hasChild("event","http://jabber.org/protocol/pubsub#event")) { - Element event = packet.findChild("event","http://jabber.org/protocol/pubsub#event"); - parseEvent(event,packet.getFrom(),account); + if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { + Element event = packet.findChild("event", + "http://jabber.org/protocol/pubsub#event"); + parseEvent(event, packet.getFrom(), account); } } } diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index d94f20da9..1c114f603 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -528,7 +528,7 @@ public class XmppConnectionService extends Service { } else { message.getConversation().endOtrIfNeeded(); 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); } packet = mMessageGenerator.generateChat(message); diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index e0733291c..fced1e387 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -17,6 +17,7 @@ import eu.siacs.conversations.ui.EditMessage.OnEnterPressed; import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected; import eu.siacs.conversations.ui.adapter.MessageAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked; +import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked; import eu.siacs.conversations.utils.UIHelper; import android.app.AlertDialog; import android.app.Fragment; @@ -160,10 +161,22 @@ public class ConversationFragment extends Fragment { 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; + } Message message = new Message(conversation, mEditMessage.getText() .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) { sendOtrMessage(message); } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { @@ -230,7 +243,24 @@ public class ConversationFragment extends Fragment { @Override public void onContactPictureClicked(Message message) { 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; } + + 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) { String oldString = mEditMessage.getText().toString().trim(); @@ -303,6 +339,9 @@ public class ConversationFragment extends Fragment { activity.invalidateOptionsMenu(); } } + if (this.conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setNextPresence(null); + } } private void decryptMessage(Message message) { @@ -406,6 +445,7 @@ public class ConversationFragment extends Fragment { messagesView.setSelection(size - 1); } mEditMessage.setText(""); + updateChatMsgHint(); } protected void updateStatusMessages() { diff --git a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java index a84fc01cf..ca475cc90 100644 --- a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -14,6 +14,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Typeface; +import android.text.Html; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; @@ -40,6 +41,7 @@ public class MessageAdapter extends ArrayAdapter { private DisplayMetrics metrics; private OnContactPictureClicked mOnContactPictureClickedListener; + private OnContactPictureLongClicked mOnContactPictureLongClickedListener; public MessageAdapter(ConversationActivity activity, List messages) { super(activity, 0, messages); @@ -61,6 +63,10 @@ public class MessageAdapter extends ArrayAdapter { public void setOnContactPictureClicked(OnContactPictureClicked listener) { this.mOnContactPictureClickedListener = listener; } + + public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) { + this.mOnContactPictureLongClickedListener = listener; + } @Override public int getViewTypeCount() { @@ -120,7 +126,11 @@ public class MessageAdapter extends ArrayAdapter { if (contact != null) { info = contact.getDisplayName(); } else { - info = message.getCounterpart(); + if (message.getPresence() != null) { + info = message.getPresence(); + } else { + info = message.getCounterpart(); + } } } break; @@ -190,14 +200,33 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.messageBody.setTextIsSelectable(false); } - private void displayTextMessage(ViewHolder viewHolder, String text) { + private void displayTextMessage(ViewHolder viewHolder, Message message) { if (viewHolder.download_button != null) { viewHolder.download_button.setVisibility(View.GONE); } viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.VISIBLE); - if (text != null) { - viewHolder.messageBody.setText(text.trim()); + if (message.getBody() != null) { + if (message.getType() != Message.TYPE_PRIVATE) { + viewHolder.messageBody.setText(message.getBody().trim()); + } else { + StringBuilder builder = new StringBuilder(); + builder.append(message.getBody().trim()); + builder.append(" ("); + 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(")"); + viewHolder.messageBody.setText(Html.fromHtml(builder.toString())); + } } else { viewHolder.messageBody.setText(""); } @@ -376,6 +405,18 @@ public class MessageAdapter extends ArrayAdapter { } }); + 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 { } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { displayDecryptionFailed(viewHolder); } else { - displayTextMessage(viewHolder, item.getBody()); + displayTextMessage(viewHolder, item); } } @@ -474,4 +515,8 @@ public class MessageAdapter extends ArrayAdapter { public interface OnContactPictureClicked { public void onContactPictureClicked(Message message); } + + public interface OnContactPictureLongClicked { + public void onContactPictureLongClicked(Message message); + } }