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: * * 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 } }