paint single unicode hearts as red and slightly larger

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Message.java          | 72 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java | 26 
src/main/java/eu/siacs/conversations/utils/UIHelper.java            |  4 
3 files changed, 66 insertions(+), 36 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Message.java 🔗

@@ -9,6 +9,7 @@ import java.util.Arrays;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.utils.GeoHelper;
+import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
 
@@ -95,9 +96,9 @@ public class Message extends AbstractEntity {
 	}
 
 	private Message(final String uuid, final String conversationUUid, final Jid counterpart,
-			final Jid trueCounterpart, final String body, final long timeSent,
-			final int encryption, final int status, final int type, final String remoteMsgId,
-			final String relativeFilePath, final String serverMsgId) {
+					final Jid trueCounterpart, final String body, final long timeSent,
+					final int encryption, final int status, final int type, final String remoteMsgId,
+					final String relativeFilePath, final String serverMsgId) {
 		this.uuid = uuid;
 		this.conversationUuid = conversationUUid;
 		this.counterpart = counterpart;
@@ -179,7 +180,7 @@ public class Message extends AbstractEntity {
 		values.put(TYPE, type);
 		values.put(REMOTE_MSG_ID, remoteMsgId);
 		values.put(RELATIVE_FILE_PATH, relativeFilePath);
-		values.put(SERVER_MSG_ID,serverMsgId);
+		values.put(SERVER_MSG_ID, serverMsgId);
 		return values;
 	}
 
@@ -211,7 +212,7 @@ public class Message extends AbstractEntity {
 				return null;
 			} else {
 				return this.conversation.getAccount().getRoster()
-					.getContactFromRoster(this.trueCounterpart);
+						.getContactFromRoster(this.trueCounterpart);
 			}
 		}
 	}
@@ -359,34 +360,36 @@ public class Message extends AbstractEntity {
 
 	public boolean mergeable(final Message message) {
 		return message != null &&
-			(message.getType() == Message.TYPE_TEXT &&
-			 this.getDownloadable() == null &&
-			 message.getDownloadable() == null &&
-			 message.getEncryption() != Message.ENCRYPTION_PGP &&
-			 this.getType() == message.getType() &&
-			 //this.getStatus() == message.getStatus() &&
-			 isStatusMergeable(this.getStatus(),message.getStatus()) &&
-			 this.getEncryption() == message.getEncryption() &&
-			 this.getCounterpart() != null &&
-			 this.getCounterpart().equals(message.getCounterpart()) &&
-			 (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
-			 !GeoHelper.isGeoUri(message.getBody()) &&
-			 !GeoHelper.isGeoUri(this.body) &&
-			 !message.bodyContainsDownloadable() &&
-			 !this.bodyContainsDownloadable() &&
-			 !message.getBody().startsWith(ME_COMMAND) &&
-			 !this.getBody().startsWith(ME_COMMAND)
-			);
+				(message.getType() == Message.TYPE_TEXT &&
+						this.getDownloadable() == null &&
+						message.getDownloadable() == null &&
+						message.getEncryption() != Message.ENCRYPTION_PGP &&
+						this.getType() == message.getType() &&
+						//this.getStatus() == message.getStatus() &&
+						isStatusMergeable(this.getStatus(), message.getStatus()) &&
+						this.getEncryption() == message.getEncryption() &&
+						this.getCounterpart() != null &&
+						this.getCounterpart().equals(message.getCounterpart()) &&
+						(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
+						!GeoHelper.isGeoUri(message.getBody()) &&
+						!GeoHelper.isGeoUri(this.body) &&
+						!message.bodyContainsDownloadable() &&
+						!this.bodyContainsDownloadable() &&
+						!message.getBody().startsWith(ME_COMMAND) &&
+						!this.getBody().startsWith(ME_COMMAND) &&
+						!this.bodyIsHeart() &&
+						!message.bodyIsHeart()
+				);
 	}
 
 	private static boolean isStatusMergeable(int a, int b) {
 		return a == b || (
-				( a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
-				|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
-				|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
-				|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
-				|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
-				|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
+				(a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
+						|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
+						|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
+						|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
+						|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
+						|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
 		);
 	}
 
@@ -443,7 +446,7 @@ public class Message extends AbstractEntity {
 			if (!url.getProtocol().equalsIgnoreCase("http")
 					&& !url.getProtocol().equalsIgnoreCase("https")) {
 				return false;
-					}
+			}
 
 			String sUrlPath = url.getPath();
 			if (sUrlPath == null || sUrlPath.isEmpty()) {
@@ -457,14 +460,14 @@ public class Message extends AbstractEntity {
 			String[] extensionParts = sLastUrlPath.split("\\.");
 			if (extensionParts.length == 2
 					&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
-						extensionParts[extensionParts.length - 1])) {
+					extensionParts[extensionParts.length - 1])) {
 				return true;
 			} else if (extensionParts.length == 3
 					&& Arrays
 					.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
 					.contains(extensionParts[extensionParts.length - 1])
 					&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
-						extensionParts[extensionParts.length - 2])) {
+					extensionParts[extensionParts.length - 2])) {
 				return true;
 			} else {
 				return false;
@@ -474,6 +477,11 @@ public class Message extends AbstractEntity {
 		}
 	}
 
+	public boolean bodyIsHeart() {
+		return body != null &&
+				(body.trim().equals(UIHelper.BLACK_HEART_SUIT) || body.trim().equals(UIHelper.HEAVY_BLACK_HEART_SUIT));
+	}
+
 	public ImageParams getImageParams() {
 		ImageParams params = getLegacyImageParams();
 		if (params != null) {

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -7,7 +7,9 @@ import android.graphics.Typeface;
 import android.net.Uri;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
 import android.text.style.StyleSpan;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -207,12 +209,24 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		viewHolder.image.setVisibility(View.GONE);
 		viewHolder.messageBody.setVisibility(View.VISIBLE);
 		viewHolder.messageBody.setText(getContext().getString(
-					R.string.decryption_failed));
+				R.string.decryption_failed));
 		viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
 		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
 		viewHolder.messageBody.setTextIsSelectable(false);
 	}
 
+	private void displayHeartMesage(final ViewHolder viewHolder, final String body) {
+		if (viewHolder.download_button != null) {
+			viewHolder.download_button.setVisibility(View.GONE);
+		}
+		viewHolder.image.setVisibility(View.GONE);
+		viewHolder.messageBody.setVisibility(View.VISIBLE);
+		Spannable span = new SpannableString(body);
+		span.setSpan(new RelativeSizeSpan(4.0f),0,body.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+		span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()),0,body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+		viewHolder.messageBody.setText(span);
+	}
+
 	private void displayTextMessage(final ViewHolder viewHolder, final Message message) {
 		if (viewHolder.download_button != null) {
 			viewHolder.download_button.setVisibility(View.GONE);
@@ -289,7 +303,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		viewHolder.image.setVisibility(View.GONE);
 		viewHolder.messageBody.setVisibility(View.GONE);
 		viewHolder.download_button.setVisibility(View.VISIBLE);
-		viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity,message)));
+		viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
 		viewHolder.download_button.setOnClickListener(new OnClickListener() {
 
 			@Override
@@ -334,7 +348,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 			scalledH = (int) (params.height / ((double) params.width / target));
 		}
 		viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
-					scalledW, scalledH));
+				scalledW, scalledH));
 		activity.loadBitmap(message, viewHolder.image);
 		viewHolder.image.setOnClickListener(new OnClickListener() {
 
@@ -528,7 +542,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 			if (GeoHelper.isGeoUri(message.getBody())) {
 				displayLocationMessage(viewHolder,message);
 			} else {
-				displayTextMessage(viewHolder, message);
+				if (message.bodyIsHeart()) {
+					displayHeartMesage(viewHolder," "+message.getBody().trim()+" ");
+				} else {
+					displayTextMessage(viewHolder, message);
+				}
 			}
 		}
 

src/main/java/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -17,6 +17,10 @@ import android.text.format.DateUtils;
 import android.util.Pair;
 
 public class UIHelper {
+
+	public static String BLACK_HEART_SUIT = "\u2665";
+	public static String HEAVY_BLACK_HEART_SUIT = "\u2764";
+
 	private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
 		| DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
 	private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME