Display "*Encrypted*" in preview of encrypted messages

This commit is contained in:
cketti 2015-12-16 15:10:47 +01:00
parent a0a362a19d
commit d6b4452ade
15 changed files with 191 additions and 224 deletions

View file

@ -142,7 +142,6 @@ public abstract class Message implements Part, CompositeBody {
public abstract long getId();
public abstract String getPreview();
public abstract boolean hasAttachments();
public abstract int getSize();

View file

@ -656,11 +656,6 @@ public class MimeMessage extends Message {
return Long.parseLong(mUid); //or maybe .mMessageId?
}
@Override
public String getPreview() {
return "";
}
@Override
public boolean hasAttachments() {
return false;

View file

@ -88,6 +88,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mailstore.DatabasePreviewType;
import com.fsck.k9.mailstore.LocalFolder;
import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.mailstore.LocalStore;
@ -125,6 +126,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
MessageColumns.FORWARDED,
MessageColumns.ATTACHMENT_COUNT,
MessageColumns.FOLDER_ID,
MessageColumns.PREVIEW_TYPE,
MessageColumns.PREVIEW,
ThreadColumns.ROOT,
SpecialColumns.ACCOUNT_UUID,
@ -147,11 +149,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
private static final int FORWARDED_COLUMN = 11;
private static final int ATTACHMENT_COUNT_COLUMN = 12;
private static final int FOLDER_ID_COLUMN = 13;
private static final int PREVIEW_COLUMN = 14;
private static final int THREAD_ROOT_COLUMN = 15;
private static final int ACCOUNT_UUID_COLUMN = 16;
private static final int FOLDER_NAME_COLUMN = 17;
private static final int THREAD_COUNT_COLUMN = 18;
private static final int PREVIEW_TYPE_COLUMN = 14;
private static final int PREVIEW_COLUMN = 15;
private static final int THREAD_ROOT_COLUMN = 16;
private static final int ACCOUNT_UUID_COLUMN = 17;
private static final int FOLDER_NAME_COLUMN = 18;
private static final int THREAD_COUNT_COLUMN = 19;
private static final String[] PROJECTION = Arrays.copyOf(THREADED_PROJECTION,
THREAD_COUNT_COLUMN);
@ -2029,10 +2032,8 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
.append(beforePreviewText);
if (mPreviewLines > 0) {
String preview = cursor.getString(PREVIEW_COLUMN);
if (preview != null) {
messageStringBuilder.append(" ").append(preview);
}
String preview = getPreview(cursor);
messageStringBuilder.append(" ").append(preview);
}
holder.preview.setText(messageStringBuilder, TextView.BufferType.SPANNABLE);
@ -2096,6 +2097,25 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
holder.date.setText(displayDate);
}
private String getPreview(Cursor cursor) {
String previewTypeString = cursor.getString(PREVIEW_TYPE_COLUMN);
DatabasePreviewType previewType = DatabasePreviewType.fromDatabaseValue(previewTypeString);
switch (previewType) {
case NONE: {
return "";
}
case ENCRYPTED: {
return getString(R.string.preview_encrypted);
}
case TEXT: {
return cursor.getString(PREVIEW_COLUMN);
}
}
throw new AssertionError("Unknown preview type: " + previewType);
}
}
class MessageViewHolder implements View.OnClickListener {

View file

@ -0,0 +1,20 @@
package com.fsck.k9.mailstore;
import java.util.ArrayList;
import java.util.List;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MessageExtractor;
class AttachmentCounter {
public int getAttachmentCount(Message message) throws MessagingException {
List<Part> attachments = new ArrayList<Part>();
MessageExtractor.getViewables(message, attachments);
return attachments.size();
}
}

View file

@ -0,0 +1,49 @@
package com.fsck.k9.mailstore;
import com.fsck.k9.message.preview.PreviewResult.PreviewType;
public enum DatabasePreviewType {
NONE("none", PreviewType.NONE),
TEXT("text", PreviewType.TEXT),
ENCRYPTED("encrypted", PreviewType.ENCRYPTED);
private final String databaseValue;
private final PreviewType previewType;
DatabasePreviewType(String databaseValue, PreviewType previewType) {
this.databaseValue = databaseValue;
this.previewType = previewType;
}
public static DatabasePreviewType fromDatabaseValue(String databaseValue) {
for (DatabasePreviewType databasePreviewType : values()) {
if (databasePreviewType.getDatabaseValue().equals(databaseValue)) {
return databasePreviewType;
}
}
throw new AssertionError("Unknown database value: " + databaseValue);
}
public static DatabasePreviewType fromPreviewType(PreviewType previewType) {
for (DatabasePreviewType databasePreviewType : values()) {
if (databasePreviewType.previewType == previewType) {
return databasePreviewType;
}
}
throw new AssertionError("Unknown preview type: " + previewType);
}
public String getDatabaseValue() {
return databaseValue;
}
public PreviewType getPreviewType() {
return previewType;
}
}

View file

@ -54,6 +54,9 @@ import com.fsck.k9.mail.internet.SizeAware;
import com.fsck.k9.mail.message.MessageHeaderParser;
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
import com.fsck.k9.message.preview.MessagePreviewCreator;
import com.fsck.k9.message.preview.PreviewResult;
import com.fsck.k9.message.preview.PreviewResult.PreviewType;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.util.MimeUtil;
@ -1233,9 +1236,13 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
}
try {
MessageInfoExtractor messageExtractor = new MessageInfoExtractor(localStore.context, message);
String preview = messageExtractor.getMessageTextPreview();
int attachmentCount = messageExtractor.getAttachmentCount();
MessagePreviewCreator previewCreator = localStore.getMessagePreviewCreator();
PreviewResult previewResult = previewCreator.createPreview(message);
PreviewType previewType = previewResult.getPreviewType();
DatabasePreviewType databasePreviewType = DatabasePreviewType.fromPreviewType(previewType);
AttachmentCounter attachmentCounter = localStore.getAttachmentCounter();
int attachmentCount = attachmentCounter.getAttachmentCount(message);
long rootMessagePartId = saveMessageParts(db, message);
@ -1256,7 +1263,6 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
cv.put("to_list", Address.pack(message.getRecipients(RecipientType.TO)));
cv.put("cc_list", Address.pack(message.getRecipients(RecipientType.CC)));
cv.put("bcc_list", Address.pack(message.getRecipients(RecipientType.BCC)));
cv.put("preview", preview);
cv.put("reply_to_list", Address.pack(message.getReplyTo()));
cv.put("attachment_count", attachmentCount);
cv.put("internal_date", message.getInternalDate() == null
@ -1264,6 +1270,13 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
cv.put("mime_type", message.getMimeType());
cv.put("empty", 0);
cv.put("preview_type", databasePreviewType.getDatabaseValue());
if (previewResult.isPreviewTextAvailable()) {
cv.put("preview", previewResult.getPreviewText());
} else {
cv.putNull("preview");
}
String messageId = message.getMessageId();
if (messageId != null) {
cv.put("message_id", messageId);

View file

@ -20,6 +20,8 @@ import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
import com.fsck.k9.message.preview.PreviewResult.PreviewType;
public class LocalMessage extends MimeMessage {
protected MessageReference mReference;
@ -37,6 +39,7 @@ public class LocalMessage extends MimeMessage {
private long mRootId;
private long messagePartId;
private String mimeType;
private PreviewType previewType;
private LocalMessage(LocalStore localStore) {
this.localStore = localStore;
@ -84,8 +87,14 @@ public class LocalMessage extends MimeMessage {
this.setInternalDate(new Date(cursor.getLong(11)));
this.setMessageId(cursor.getString(12));
final String preview = cursor.getString(14);
mPreview = (preview == null ? "" : preview);
String previewTypeString = cursor.getString(24);
DatabasePreviewType databasePreviewType = DatabasePreviewType.fromDatabaseValue(previewTypeString);
previewType = databasePreviewType.getPreviewType();
if (previewType == PreviewType.TEXT) {
mPreview = cursor.getString(14);
} else {
mPreview = "";
}
if (this.mFolder == null) {
LocalFolder f = new LocalFolder(this.localStore, cursor.getInt(13));
@ -134,7 +143,10 @@ public class LocalMessage extends MimeMessage {
super.writeTo(out);
}
@Override
public PreviewType getPreviewType() {
return previewType;
}
public String getPreview() {
return mPreview;
}

View file

@ -26,6 +26,7 @@ import com.fsck.k9.mailstore.LocalFolder.MoreMessages;
import com.fsck.k9.mailstore.StorageManager.StorageProvider;
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
import com.fsck.k9.message.preview.MessagePreviewCreator;
import com.fsck.k9.provider.EmailProvider;
import com.fsck.k9.provider.EmailProvider.MessageColumns;
import com.fsck.k9.search.LocalSearch;
@ -85,7 +86,7 @@ public class LocalStore extends Store implements Serializable {
"subject, sender_list, date, uid, flags, messages.id, to_list, cc_list, " +
"bcc_list, reply_to_list, attachment_count, internal_date, messages.message_id, " +
"folder_id, preview, threads.id, threads.root, deleted, read, flagged, answered, " +
"forwarded, message_part_id, mime_type ";
"forwarded, message_part_id, mime_type, preview_type ";
static final String GET_FOLDER_COLS =
"folders.id, name, visible_limit, last_updated, status, push_state, last_pushed, " +
@ -129,7 +130,7 @@ public class LocalStore extends Store implements Serializable {
*/
private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 500;
public static final int DB_VERSION = 53;
public static final int DB_VERSION = 54;
public static String getColumnNameForFlag(Flag flag) {
@ -161,6 +162,8 @@ public class LocalStore extends Store implements Serializable {
private ContentResolver mContentResolver;
private final Account mAccount;
private final MessagePreviewCreator messagePreviewCreator;
private final AttachmentCounter attachmentCounter;
/**
* local://localhost/path/to/database/uuid.db
@ -178,6 +181,9 @@ public class LocalStore extends Store implements Serializable {
database.setStorageProviderId(account.getLocalStorageProviderId());
uUid = account.getUuid();
messagePreviewCreator = MessagePreviewCreator.newInstance();
attachmentCounter = new AttachmentCounter();
database.open();
}
@ -831,6 +837,14 @@ public class LocalStore extends Store implements Serializable {
return database;
}
public MessagePreviewCreator getMessagePreviewCreator() {
return messagePreviewCreator;
}
public AttachmentCounter getAttachmentCounter() {
return attachmentCounter;
}
void notifyChange() {
Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + uUid + "/messages");
mContentResolver.notifyChange(uri, null);

View file

@ -1,43 +0,0 @@
package com.fsck.k9.mailstore;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MessageExtractor;
import com.fsck.k9.mail.internet.Viewable;
class MessageInfoExtractor {
private final Context context;
private final Message message;
private List<Viewable> viewables;
private List<Part> attachments;
public MessageInfoExtractor(Context context, Message message) {
this.context = context;
this.message = message;
}
public String getMessageTextPreview() throws MessagingException {
getViewablesIfNecessary();
return MessagePreviewExtractor.extractPreview(context, viewables);
}
public int getAttachmentCount() throws MessagingException {
getViewablesIfNecessary();
return attachments.size();
}
private void getViewablesIfNecessary() throws MessagingException {
if (viewables == null) {
attachments = new ArrayList<Part>();
viewables = MessageExtractor.getViewables(message, attachments);
}
}
}

View file

@ -1,154 +0,0 @@
package com.fsck.k9.mailstore;
import java.util.List;
import android.content.Context;
import android.text.TextUtils;
import com.fsck.k9.R;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MessageExtractor;
import com.fsck.k9.mail.internet.Viewable;
import com.fsck.k9.mail.internet.Viewable.Alternative;
import com.fsck.k9.mail.internet.Viewable.Html;
import com.fsck.k9.mail.internet.Viewable.MessageHeader;
import com.fsck.k9.mail.internet.Viewable.Textual;
class MessagePreviewExtractor {
private static final int MAX_PREVIEW_LENGTH = 512;
private static final int MAX_CHARACTERS_CHECKED_FOR_PREVIEW = 8192;
public static String extractPreview(Context context, List<Viewable> viewables) {
StringBuilder text = new StringBuilder();
boolean divider = false;
for (Viewable viewable : viewables) {
if (viewable instanceof Textual) {
appendText(text, viewable, divider);
divider = true;
} else if (viewable instanceof MessageHeader) {
appendMessagePreview(context, text, (MessageHeader) viewable, divider);
divider = false;
} else if (viewable instanceof Alternative) {
appendAlternative(text, (Alternative) viewable, divider);
divider = true;
}
if (hasMaxPreviewLengthBeenReached(text)) {
break;
}
}
if (hasMaxPreviewLengthBeenReached(text)) {
text.setLength(MAX_PREVIEW_LENGTH - 1);
text.append('…');
}
return text.toString();
}
private static void appendText(StringBuilder text, Viewable viewable, boolean prependDivider) {
if (viewable instanceof Textual) {
appendTextual(text, (Textual) viewable, prependDivider);
} else if (viewable instanceof Alternative) {
appendAlternative(text, (Alternative) viewable, prependDivider);
} else {
throw new IllegalArgumentException("Unknown Viewable");
}
}
private static void appendTextual(StringBuilder text, Textual textual, boolean prependDivider) {
Part part = textual.getPart();
if (prependDivider) {
appendDivider(text);
}
String textFromPart = MessageExtractor.getTextFromPart(part);
if (textFromPart == null) {
textFromPart = "";
} else if (textual instanceof Html) {
textFromPart = HtmlConverter.htmlToText(textFromPart);
}
text.append(stripTextForPreview(textFromPart));
}
private static void appendAlternative(StringBuilder text, Alternative alternative, boolean prependDivider) {
List<Viewable> textAlternative = alternative.getText().isEmpty() ?
alternative.getHtml() : alternative.getText();
boolean divider = prependDivider;
for (Viewable textViewable : textAlternative) {
appendText(text, textViewable, divider);
divider = true;
if (hasMaxPreviewLengthBeenReached(text)) {
break;
}
}
}
private static void appendMessagePreview(Context context, StringBuilder text, MessageHeader messageHeader,
boolean divider) {
if (divider) {
appendDivider(text);
}
String subject = messageHeader.getMessage().getSubject();
if (TextUtils.isEmpty(subject)) {
text.append(context.getString(R.string.preview_untitled_inner_message));
} else {
text.append(context.getString(R.string.preview_inner_message, subject));
}
}
private static void appendDivider(StringBuilder text) {
text.append(" / ");
}
private static String stripTextForPreview(String text) {
if (text == null) {
return "";
}
// Only look at the first 8k of a message when calculating
// the preview. This should avoid unnecessary
// memory usage on large messages
if (text.length() > MAX_CHARACTERS_CHECKED_FOR_PREVIEW) {
text = text.substring(0, MAX_CHARACTERS_CHECKED_FOR_PREVIEW);
}
// Remove (correctly delimited by '-- \n') signatures
text = text.replaceAll("(?ms)^-- [\\r\\n]+.*", "");
// try to remove lines of dashes in the preview
text = text.replaceAll("(?m)^----.*?$", "");
// remove quoted text from the preview
text = text.replaceAll("(?m)^[#>].*$", "");
// Remove a common quote header from the preview
text = text.replaceAll("(?m)^On .*wrote.?$", "");
// Remove a more generic quote header from the preview
text = text.replaceAll("(?m)^.*\\w+:$", "");
// Remove horizontal rules.
text = text.replaceAll("\\s*([-=_]{30,}+)\\s*", " ");
// URLs in the preview should just be shown as "..." - They're not
// clickable and they usually overwhelm the preview
text = text.replaceAll("https?://\\S+", "...");
// Don't show newlines in the preview
text = text.replaceAll("(\\r|\\n)+", " ");
// Collapse whitespace in the preview
text = text.replaceAll("\\s+", " ");
// Remove any whitespace at the beginning and end of the string.
text = text.trim();
return (text.length() <= MAX_PREVIEW_LENGTH) ? text : text.substring(0, MAX_PREVIEW_LENGTH);
}
private static boolean hasMaxPreviewLengthBeenReached(StringBuilder text) {
return text.length() >= MAX_PREVIEW_LENGTH;
}
}

View file

@ -90,6 +90,7 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
"attachment_count INTEGER, " +
"internal_date INTEGER, " +
"message_id TEXT, " +
"preview_type TEXT default \"none\", " +
"preview TEXT, " +
"mime_type TEXT, "+
"normalized_subject_hash INTEGER, " +
@ -575,6 +576,9 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
if (db.getVersion() < 53) {
removeNullValuesFromEmptyColumnInMessagesTable(db);
}
if (db.getVersion() < 54) {
addPreviewTypeColumn(db);
}
}
db.setVersion(LocalStore.DB_VERSION);
@ -637,4 +641,9 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
private void removeNullValuesFromEmptyColumnInMessagesTable(SQLiteDatabase db) {
db.execSQL("UPDATE messages SET empty = 0 WHERE empty IS NULL");
}
private void addPreviewTypeColumn(SQLiteDatabase db) {
db.execSQL("ALTER TABLE messages ADD preview_type TEXT default \"none\"");
db.execSQL("UPDATE messages SET preview_type = 'text' WHERE preview IS NOT NULL");
}
}

View file

@ -18,6 +18,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.message.preview.PreviewResult.PreviewType;
class NotificationContentCreator {
@ -41,12 +42,12 @@ class NotificationContentCreator {
return new NotificationContent(messageReference, displaySender, subject, preview, summary, starred);
}
private CharSequence getMessagePreview(Message message) {
private CharSequence getMessagePreview(LocalMessage message) {
String subject = message.getSubject();
String snippet = message.getPreview();
String snippet = getPreview(message);
boolean isSubjectEmpty = TextUtils.isEmpty(subject);
boolean isSnippetPresent = !TextUtils.isEmpty(snippet);
boolean isSnippetPresent = message.getPreviewType() != PreviewType.NONE;
if (isSubjectEmpty && isSnippetPresent) {
return snippet;
}
@ -65,6 +66,20 @@ class NotificationContentCreator {
return preview;
}
private String getPreview(LocalMessage message) {
PreviewType previewType = message.getPreviewType();
switch (previewType) {
case NONE:
return null;
case TEXT:
return message.getPreview();
case ENCRYPTED:
return context.getString(R.string.preview_encrypted);
}
throw new AssertionError("Unknown preview type: " + previewType);
}
private CharSequence buildMessageSummary(String sender, String subject) {
if (sender == null) {
return subject;

View file

@ -143,6 +143,7 @@ public class EmailProvider extends ContentProvider {
public static final String FLAGS = "flags";
public static final String ATTACHMENT_COUNT = "attachment_count";
public static final String FOLDER_ID = "folder_id";
public static final String PREVIEW_TYPE = "preview_type";
public static final String PREVIEW = "preview";
public static final String READ = "read";
public static final String FLAGGED = "flagged";

View file

@ -1158,5 +1158,6 @@ Please submit bug reports, contribute new features and ask questions at
<string name="crypto_incomplete_message">Incomplete message</string>
<!-- Note: This references message_view_download_remainder -->
<string name="crypto_download_complete_message_to_decrypt">Click \'Download complete message\' to allow decryption.</string>
<string name="preview_encrypted">*Encrypted*</string>
</resources>

View file

@ -9,6 +9,7 @@ import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.message.preview.PreviewResult.PreviewType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -76,6 +77,7 @@ public class NotificationContentCreatorTest {
@Test
public void createFromMessage_withoutPreview() throws Exception {
when(message.getPreviewType()).thenReturn(PreviewType.NONE);
when(message.getPreview()).thenReturn(null);
NotificationContent content = contentCreator.createFromMessage(account, message);
@ -84,6 +86,18 @@ public class NotificationContentCreatorTest {
assertEquals(SUBJECT, content.preview.toString());
}
@Test
public void createFromMessage_withEncryptedMessage() throws Exception {
when(message.getPreviewType()).thenReturn(PreviewType.ENCRYPTED);
when(message.getPreview()).thenReturn(null);
NotificationContent content = contentCreator.createFromMessage(account, message);
String encrypted = "*Encrypted*";
assertEquals(SUBJECT, content.subject);
assertEquals(SUBJECT + "\n" + encrypted, content.preview.toString());
}
@Test
public void createFromMessage_withoutSender() throws Exception {
when(message.getFrom()).thenReturn(null);
@ -118,6 +132,7 @@ public class NotificationContentCreatorTest {
public void createFromMessage_withoutEmptyMessage() throws Exception {
when(message.getFrom()).thenReturn(null);
when(message.getSubject()).thenReturn(null);
when(message.getPreviewType()).thenReturn(PreviewType.NONE);
when(message.getPreview()).thenReturn(null);
NotificationContent content = contentCreator.createFromMessage(account, message);
@ -145,6 +160,7 @@ public class NotificationContentCreatorTest {
LocalMessage message = mock(LocalMessage.class);
when(message.makeMessageReference()).thenReturn(messageReference);
when(message.getPreviewType()).thenReturn(PreviewType.TEXT);
when(message.getPreview()).thenReturn(PREVIEW);
when(message.getSubject()).thenReturn(SUBJECT);
when(message.getFrom()).thenReturn(new Address[] { new Address(SENDER_ADDRESS, SENDER_NAME) });