/** * Copyright (C) 2012 Moxie Marlinpsike * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.thoughtcrime.SMP.database.model; import android.content.Context; import android.text.Spannable; import android.text.SpannableString; import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; import org.thoughtcrime.SMP.R; import org.thoughtcrime.SMP.database.MmsSmsColumns; import org.thoughtcrime.SMP.database.SmsDatabase; import org.thoughtcrime.SMP.database.documents.NetworkFailure; import org.thoughtcrime.SMP.database.documents.IdentityKeyMismatch; import org.thoughtcrime.SMP.recipients.Recipient; import org.thoughtcrime.SMP.recipients.Recipients; import org.thoughtcrime.SMP.util.GroupUtil; import java.util.List; /** * The base class for message record models that are displayed in * conversations, as opposed to models that are displayed in a thread list. * Encapsulates the shared data between both SMS and MMS messages. * * @author Moxie Marlinspike * */ public abstract class MessageRecord extends DisplayRecord { public static final int DELIVERY_STATUS_NONE = 0; public static final int DELIVERY_STATUS_RECEIVED = 1; public static final int DELIVERY_STATUS_PENDING = 2; public static final int DELIVERY_STATUS_FAILED = 3; // TODO: default is 2000 private static final int MAX_DISPLAY_LENGTH = 4000; private final Recipient individualRecipient; private final int recipientDeviceId; private final long id; private final int deliveryStatus; private final int receiptCount; private final List<IdentityKeyMismatch> mismatches; private final List<NetworkFailure> networkFailures; MessageRecord(Context context, long id, Body body, Recipients recipients, Recipient individualRecipient, int recipientDeviceId, long dateSent, long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type, List<IdentityKeyMismatch> mismatches, List<NetworkFailure> networkFailures) { super(context, body, recipients, dateSent, dateReceived, threadId, type); this.id = id; this.individualRecipient = individualRecipient; this.recipientDeviceId = recipientDeviceId; this.deliveryStatus = deliveryStatus; this.receiptCount = receiptCount; this.mismatches = mismatches; this.networkFailures = networkFailures; } public abstract boolean isMms(); public abstract boolean isMmsNotification(); public boolean isFailed() { return MmsSmsColumns.Types.isFailedMessageType(type) || MmsSmsColumns.Types.isPendingSecureSmsFallbackType(type) || getDeliveryStatus() == DELIVERY_STATUS_FAILED; } public boolean isOutgoing() { return MmsSmsColumns.Types.isOutgoingMessageType(type); } public boolean isPending() { return MmsSmsColumns.Types.isPendingMessageType(type); } public boolean isSecure() { return MmsSmsColumns.Types.isSecureType(type); } public boolean isLegacyMessage() { return MmsSmsColumns.Types.isLegacyType(type); } public boolean isAsymmetricEncryption() { return MmsSmsColumns.Types.isAsymmetricEncryption(type); } @Override public SpannableString getDisplayBody() { if (isGroupUpdate() && isOutgoing()) { return emphasisAdded(context.getString(R.string.MessageRecord_updated_group)); } else if (isGroupUpdate()) { return emphasisAdded(GroupUtil.getDescription(context, getBody().getBody())); } else if (isGroupQuit() && isOutgoing()) { return emphasisAdded(context.getString(R.string.MessageRecord_left_group)); } else if (isGroupQuit()) { return emphasisAdded(context.getString(R.string.ConversationItem_group_action_left, getIndividualRecipient().toShortString())); } else if (getBody().getBody().length() > MAX_DISPLAY_LENGTH) { return new SpannableString(getBody().getBody().substring(0, MAX_DISPLAY_LENGTH)); } return new SpannableString(getBody().getBody()); } public long getId() { return id; } public int getDeliveryStatus() { return deliveryStatus; } public boolean isDelivered() { return getDeliveryStatus() == DELIVERY_STATUS_RECEIVED || receiptCount > 0; } public boolean isPush() { return SmsDatabase.Types.isPushType(type) && !SmsDatabase.Types.isForcedSms(type); } public boolean isForcedSms() { return SmsDatabase.Types.isForcedSms(type); } public boolean isStaleKeyExchange() { return SmsDatabase.Types.isStaleKeyExchange(type); } public boolean isProcessedKeyExchange() { return SmsDatabase.Types.isProcessedKeyExchange(type); } public boolean isPendingInsecureSmsFallback() { return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type); } public boolean isIdentityMismatchFailure() { return mismatches != null && !mismatches.isEmpty(); } public boolean isBundleKeyExchange() { return SmsDatabase.Types.isBundleKeyExchange(type); } public boolean isIdentityUpdate() { return SmsDatabase.Types.isIdentityUpdate(type); } public boolean isCorruptedKeyExchange() { return SmsDatabase.Types.isCorruptedKeyExchange(type); } public boolean isInvalidVersionKeyExchange() { return SmsDatabase.Types.isInvalidVersionKeyExchange(type); } public Recipient getIndividualRecipient() { return individualRecipient; } public int getRecipientDeviceId() { return recipientDeviceId; } public long getType() { return type; } public List<IdentityKeyMismatch> getIdentityKeyMismatches() { return mismatches; } public List<NetworkFailure> getNetworkFailures() { return networkFailures; } public boolean hasNetworkFailures() { return networkFailures != null && !networkFailures.isEmpty(); } protected SpannableString emphasisAdded(String sequence) { SpannableString spannable = new SpannableString(sequence); spannable.setSpan(new RelativeSizeSpan(0.9f), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return spannable; } public boolean equals(Object other) { return other != null && other instanceof MessageRecord && ((MessageRecord) other).getId() == getId() && ((MessageRecord) other).isMms() == isMms(); } public int hashCode() { return (int)getId(); } }