package de.thedevstack.conversationsplus.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.DownloadableFile;
import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.Transferable;
import de.thedevstack.conversationsplus.entities.TransferablePlaceholder;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.enums.MessageConfirmation;
import de.thedevstack.conversationsplus.enums.MessageDirection;
import de.thedevstack.conversationsplus.enums.MessageStatus;
import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.utils.messaging.MessageReceiptUtil;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.tzur.conversations.Settings;
/**
* Utility class to work with messages.
*/
public final class MessageUtil {
/**
* Checks if an attached file is an image or not.
* Prerequisite for calling this method: The check if a file is attached is done before.
* @param message
* @return
*/
public static boolean isAttachedFileAnImage(Message message) {
final FileParams fileParams = message.getFileParams();
String mimeType = (null != fileParams) ? fileParams.getMimeType() : null;
return message.getType() == Message.TYPE_IMAGE
|| (fileParams != null && fileParams.getWidth() > 0)
|| (null != mimeType && mimeType.startsWith("image/"));
}
public static boolean isTypeFileAndDecrypted(Message message) {
return message.getType() == Message.TYPE_FILE
&& message.getEncryption() != Message.ENCRYPTION_PGP
&& message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED;
}
public static boolean isDecrypted(Message message) {
return message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED;
}
/**
* Checks if the file status of an message is NOT one of:
*
* - FileStatus#NOT_FOUND
* - FileStatus#DELETED
*
* The file status does not guarantee that a file is really available or not.
* @param message
* @return
*/
public static boolean mayFileRemoteAvailable(Message message) {
FileStatus fileStatus = (null != message.getFileParams()) ? message.getFileParams().getFileStatus() : null;
return null != fileStatus
&& FileStatus.NOT_FOUND != fileStatus
&& FileStatus.DELETED != fileStatus;
}
public static boolean needsDownload(Message message) {
FileStatus fileStatus = (null != message.getFileParams()) ? message.getFileParams().getFileStatus() : null;
return (message.hasFileAttached() || MessageUtil.hasDownloadableLink(message))
&& (null == fileStatus
|| (fileStatus == FileStatus.NEEDS_DOWNLOAD || fileStatus == FileStatus.UNDEFINED))
&& message.treatAsDownloadable() != Message.Decision.NEVER;
}
public static boolean hasDownloadableLink(Message message) {
String url = (null != message.getFileParams()) ? message.getFileParams().getUrl() : null;
return ConversationsPlusPreferences.autoDownloadFileLink()
&& null != url;
}
public static boolean isOutgoingMessage(Message message) {
return MessageDirection.OUT == message.getDirection();
}
public static boolean isIncomingMessage(Message message) {
return MessageDirection.IN == message.getDirection();
}
public static boolean isMessageSent(Message message) {
return MessageUtil.isOutgoingMessage(message)
&& MessageStatus.TRANSMITTED == message.getMessageStatus();
}
public static boolean isMessageReceived(Message message) {
return MessageUtil.isIncomingMessage(message)
&& MessageStatus.TRANSMITTED == message.getMessageStatus();
}
public static boolean isMessageTransmittedOrDisplayedOrReceived(Message message) {
MessageStatus status = message.getMessageStatus();
return MessageStatus.TRANSMITTED == status
|| MessageStatus.RECEIVED == status
|| MessageStatus.DISPLAYED == status;
}
public static void setAndSaveStatusForFileDeleted(Message message) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
//FIXME: File Status needs to be changed
MessageStatus msgStatus = message.getMessageStatus();
if (MessageUtil.isOutgoingMessage(message)
&& (MessageStatus.TRANSMITTING == msgStatus || MessageStatus.WAITING == msgStatus)) {
MessageUtil.setAndSaveMessageStatus(message, MessageStatus.FAILED); // FIXME: add some information why this is failed
}
}
public static void setAndSaveFileStatus(Message message, FileStatus fileStatus) {
if (setFileStatus(message, fileStatus)) {
DatabaseBackend.getInstance().updateMessage(message);
UiUpdateHelper.updateConversationUi();
}
}
public static boolean setFileStatus(Message message, FileStatus fileStatus) {
message.getFileParams().setFileStatus(fileStatus);
if (FileStatus.DOWNLOAD_FAILED == fileStatus
|| FileStatus.UPLOAD_FAILED == fileStatus) {
setMessageStatus(message, MessageStatus.FAILED);
} else if (FileStatus.DOWNLOADED == fileStatus) {
setMessageStatus(message, MessageStatus.TRANSMITTED);
}
return true;
}
public static boolean markMessageAsReceived(Account account, Jid from, String id) {
Message message = XmppConnectionServiceAccessor.xmppConnectionService.getMessage(account, from, id);
if (null != message) {
MessageUtil.setAndSaveMessageStatus(message, MessageStatus.RECEIVED);
return true;
}
return false;
}
public static boolean setAndSaveMessageStatus(Conversation conversation, String uuid, MessageStatus newStatus) {
if (uuid == null) {
return false;
} else {
Message message = conversation.findSentMessageWithUuid(uuid);
if (message != null) {
setAndSaveMessageStatus(message, newStatus);
return true;
} else {
return false;
}
}
}
public static void setAndSaveMessageStatus(Message message, MessageStatus newStatus) {
if (setMessageStatus(message, newStatus)) {
DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateMessage(message);
UiUpdateHelper.updateConversationUi();
}
}
private static boolean sendReceipt(Message message) {
return Settings.CONFIRM_MESSAGE_RECEIVED // Only if user allows to send message confirmations
&& isIncomingMessage(message) // Only if a message is incoming
&& MessageConfirmation.NONE != message.getConfirmation() // Only if message contained an confirmation request
&& message.getRemoteMsgId() != null // Only if there is an remote id
&& !message.isMamReceived() // Only if it is not received using MAM
&& !message.isCarbon() // Only if it is not received with carbons
&& !message.isRead(); // Only if the message is not read yet
}
public static boolean setMessageStatus(Message message, MessageStatus newStatus) {
MessageStatus currentStatus = message.getMessageStatus();
if ((MessageStatus.FAILED == newStatus && (MessageStatus.RECEIVED == currentStatus || MessageStatus.DISPLAYED == currentStatus))) {
return false;
}
message.setMessageStatus(newStatus);
if (MessageStatus.TRANSMITTED == newStatus) {
if (sendReceipt(message)) {
Logging.d("message-util", "Send message receipt from setMessageStatus");
MessageReceiptUtil.sendMessageReceipts(message);
}
Logging.d("message-util", "Push notification for message");
XmppConnectionServiceAccessor.xmppConnectionService.getNotificationService().push(message);
}
return true;
}
public static boolean wasHighlightedOrPrivate(final Message message) {
final String nick = message.getConversation().getMucOptions().getActualNick();
final Pattern highlight = generateNickHighlightPattern(nick);
if (message.getBody() == null || nick == null) {
return false;
}
final Matcher m = highlight.matcher(message.getBody());
return (m.find() || message.getType() == Message.TYPE_PRIVATE);
}
private static Pattern generateNickHighlightPattern(final String nick) {
// We expect a word boundary, i.e. space or start of string, followed by
// the
// nick (matched in case-insensitive manner), followed by optional
// punctuation (for example "bob: i disagree" or "how are you alice?"),
// followed by another word boundary.
return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b",
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
}
public static void updateMessageWithImageDetails(Message message, String filePath, long size, int imageWidth, int imageHeight) {
message.setRelativeFilePath(filePath);
MessageUtil.updateMessageWithFileParams(message, null, size, imageWidth, imageHeight);
}
public static void updateFileParams(Message message) {
updateFileParams(message, null);
}
public static void updateFileParams(Message message, URL url) {
DownloadableFile file = FileBackend.getFile(message);
int imageWidth = -1;
int imageHeight = -1;
if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
imageHeight = options.outHeight;
imageWidth = options.outWidth;
if (null != bmp) {
bmp.recycle();
}
}
MessageUtil.updateMessageWithFileParams(message, url, file.getSize(), imageWidth, imageHeight);
}
private static void updateMessageWithFileParams(Message message, URL url, long size, int imageWidth, int imageHeight) {
FileParams fileParams = message.getFileParams();
if (null == fileParams) {
fileParams = new FileParams();
message.setFileParams(fileParams);
}
fileParams.setSize(size);
if (null != url) {
fileParams.setUrl(url.toString());
}
if (-1 < imageWidth) {
fileParams.setWidth(imageWidth);
}
if (-1 < imageHeight) {
fileParams.setHeight(imageHeight);
}
String relativeFilePathFromMessage = message.getRelativeFilePath();
if (null != relativeFilePathFromMessage && relativeFilePathFromMessage.startsWith("/") && (null == fileParams.getPath() || !relativeFilePathFromMessage.equals(fileParams.getPath()))) {
fileParams.setPath(relativeFilePathFromMessage);
}
}
public static Message createStatusMessage(Conversation conversation, String body) {
final Message message = new Message();
message.setType(Message.TYPE_STATUS);
message.setConversation(conversation);
message.setBody(body);
return message;
}
public static Message createOutgoingMessage(Conversation conversation, String body) {
int encryption = conversation.getNextEncryption();
if (encryption == Message.ENCRYPTION_PGP) {
encryption = Message.ENCRYPTION_DECRYPTED;
}
Message message = new Message(conversation, body, encryption);
message.setMessageStatus(MessageStatus.WAITING);
message.setDirection(MessageDirection.OUT);
if (conversation.getNextCounterpart() != null) {
message.setCounterpart(conversation.getNextCounterpart());
}
return message;
}
private MessageUtil() {
// Static helper class
}
}