diff --git a/FAQ.md b/FAQ.md index 777da878c1..396897f5f8 100644 --- a/FAQ.md +++ b/FAQ.md @@ -30,15 +30,15 @@ Most, if not all, other email apps don't show a notification with the "side effe The low priority status bar notification shows the number of pending operations, which can be: -* seen: mark message as seen/unseen in remote folder * add: add message to remote folder * move: move message to another remote folder * delete: delete message from remote folder * send: send message -* attachment: download attachment -* headers: download message headers -* body: download message body +* seen: mark message as seen/unseen in remote folder * flag: star/unstar remote message +* headers: download message headers +* body: download message text +* attachment: download attachment Operations are processed only when there is a connection to the email server or when manually synchronizing. See also [this FAQ](#FAQ16). diff --git a/app/schemas/eu.faircode.email.DB/16.json b/app/schemas/eu.faircode.email.DB/16.json index fef1a5427d..d7aaf75ff2 100644 --- a/app/schemas/eu.faircode.email.DB/16.json +++ b/app/schemas/eu.faircode.email.DB/16.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 16, - "identityHash": "bac90fa06592121fae18c1305a2e5b25", + "identityHash": "95bd1e083056fa7625f21430f7d53e2d", "entities": [ { "tableName": "identity", @@ -369,7 +369,7 @@ }, { "tableName": "message", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `folder` INTEGER NOT NULL, `identity` INTEGER, `replying` INTEGER, `uid` INTEGER, `msgid` TEXT, `references` TEXT, `inreplyto` TEXT, `thread` TEXT, `avatar` TEXT, `from` TEXT, `to` TEXT, `cc` TEXT, `bcc` TEXT, `reply` TEXT, `headers` TEXT, `subject` TEXT, `downloaded` INTEGER NOT NULL, `sent` INTEGER, `received` INTEGER NOT NULL, `stored` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `flagged` INTEGER NOT NULL, `ui_seen` INTEGER NOT NULL, `ui_flagged` INTEGER NOT NULL, `ui_hide` INTEGER NOT NULL, `ui_found` INTEGER NOT NULL, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`identity`) REFERENCES `identity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`replying`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `folder` INTEGER NOT NULL, `identity` INTEGER, `replying` INTEGER, `uid` INTEGER, `msgid` TEXT, `references` TEXT, `inreplyto` TEXT, `thread` TEXT, `avatar` TEXT, `from` TEXT, `to` TEXT, `cc` TEXT, `bcc` TEXT, `reply` TEXT, `headers` TEXT, `subject` TEXT, `size` INTEGER, `content` INTEGER NOT NULL, `sent` INTEGER, `received` INTEGER NOT NULL, `stored` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `flagged` INTEGER NOT NULL, `ui_seen` INTEGER NOT NULL, `ui_flagged` INTEGER NOT NULL, `ui_hide` INTEGER NOT NULL, `ui_found` INTEGER NOT NULL, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`identity`) REFERENCES `identity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`replying`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -480,8 +480,14 @@ "notNull": false }, { - "fieldPath": "downloaded", - "columnName": "downloaded", + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "content", + "columnName": "content", "affinity": "INTEGER", "notNull": true }, @@ -964,7 +970,7 @@ ], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"bac90fa06592121fae18c1305a2e5b25\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"95bd1e083056fa7625f21430f7d53e2d\")" ] } } \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index ac14e013d3..1065405251 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -301,6 +301,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB draft.msgid = EntityMessage.generateMessageId(); draft.to = new Address[]{Helper.myAddress()}; draft.subject = context.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME + " crash log"; + draft.content = true; draft.received = new Date().getTime(); draft.seen = false; draft.ui_seen = false; @@ -757,7 +758,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB try { db.beginTransaction(); - if (!message.downloaded) + if (!message.content) EntityOperation.queue(db, message, EntityOperation.BODY); for (EntityMessage tmessage : db.message().getMessageByThread(message.account, message.thread)) { diff --git a/app/src/main/java/eu/faircode/email/AdapterAttachment.java b/app/src/main/java/eu/faircode/email/AdapterAttachment.java index eb923fd8d6..31fa417502 100644 --- a/app/src/main/java/eu/faircode/email/AdapterAttachment.java +++ b/app/src/main/java/eu/faircode/email/AdapterAttachment.java @@ -98,7 +98,7 @@ public class AdapterAttachment extends RecyclerView.Adapter 0 ? View.VISIBLE : View.GONE); + tvSubject.setText(message.subject); if (viewType == ViewType.UNIFIED) tvFolder.setText(message.accountName); diff --git a/app/src/main/java/eu/faircode/email/DB.java b/app/src/main/java/eu/faircode/email/DB.java index 3a396898fa..5dbf1eee29 100644 --- a/app/src/main/java/eu/faircode/email/DB.java +++ b/app/src/main/java/eu/faircode/email/DB.java @@ -225,7 +225,8 @@ public abstract class DB extends RoomDatabase { @Override public void migrate(SupportSQLiteDatabase db) { Log.i(Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion); - db.execSQL("ALTER TABLE `message` ADD COLUMN `downloaded` INTEGER NOT NULL DEFAULT 1"); + db.execSQL("ALTER TABLE `message` ADD COLUMN `size` INTEGER"); + db.execSQL("ALTER TABLE `message` ADD COLUMN `content` INTEGER NOT NULL DEFAULT 1"); } }) .build(); diff --git a/app/src/main/java/eu/faircode/email/DaoMessage.java b/app/src/main/java/eu/faircode/email/DaoMessage.java index ff820f5b19..ac91072883 100644 --- a/app/src/main/java/eu/faircode/email/DaoMessage.java +++ b/app/src/main/java/eu/faircode/email/DaoMessage.java @@ -200,8 +200,8 @@ public interface DaoMessage { @Query("UPDATE message SET ui_found = 0 WHERE folder = :folder") int resetFound(long folder); - @Query("UPDATE message SET downloaded = :downloaded WHERE id = :id") - int setMessageDownloaded(long id, boolean downloaded); + @Query("UPDATE message SET content = :content WHERE id = :id") + int setMessageContent(long id, boolean content); @Query("UPDATE message SET headers = :headers WHERE id = :id") int setMessageHeaders(long id, String headers); diff --git a/app/src/main/java/eu/faircode/email/EntityAttachment.java b/app/src/main/java/eu/faircode/email/EntityAttachment.java index 470acc315f..24657d8625 100644 --- a/app/src/main/java/eu/faircode/email/EntityAttachment.java +++ b/app/src/main/java/eu/faircode/email/EntityAttachment.java @@ -20,10 +20,17 @@ package eu.faircode.email; */ import android.content.Context; +import android.util.Log; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import javax.mail.BodyPart; +import javax.mail.MessagingException; import androidx.annotation.NonNull; import androidx.room.Entity; @@ -47,6 +54,7 @@ import static androidx.room.ForeignKey.CASCADE; ) public class EntityAttachment { static final String TABLE_NAME = "attachment"; + static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes @PrimaryKey(autoGenerate = true) public Long id; @@ -73,6 +81,54 @@ public class EntityAttachment { return new File(dir, Long.toString(id)); } + void download(Context context, DB db) throws MessagingException, IOException { + // Build filename + File file = EntityAttachment.getFile(context, this.id); + + // Download attachment + InputStream is = null; + OutputStream os = null; + try { + this.progress = null; + db.attachment().updateAttachment(this); + + is = this.part.getInputStream(); + os = new BufferedOutputStream(new FileOutputStream(file)); + + int size = 0; + byte[] buffer = new byte[ATTACHMENT_BUFFER_SIZE]; + for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { + size += len; + os.write(buffer, 0, len); + + // Update progress + if (this.size != null) + db.attachment().setProgress(this.id, size * 100 / this.size); + } + + // Store attachment data + this.size = size; + this.progress = null; + this.available = true; + db.attachment().updateAttachment(this); + + Log.i(Helper.TAG, "Downloaded attachment size=" + this.size); + } catch (IOException ex) { + // Reset progress on failure + this.progress = null; + db.attachment().updateAttachment(this); + throw ex; + } finally { + try { + if (is != null) + is.close(); + } finally { + if (os != null) + os.close(); + } + } + } + @Override public boolean equals(Object obj) { if (obj instanceof EntityAttachment) { diff --git a/app/src/main/java/eu/faircode/email/EntityMessage.java b/app/src/main/java/eu/faircode/email/EntityMessage.java index 288225f8e7..1fa52c32c6 100644 --- a/app/src/main/java/eu/faircode/email/EntityMessage.java +++ b/app/src/main/java/eu/faircode/email/EntityMessage.java @@ -90,8 +90,9 @@ public class EntityMessage implements Serializable { public Address[] reply; public String headers; public String subject; + public Integer size; @NonNull - public Boolean downloaded = false; + public Boolean content = false; public Long sent; // compose = null @NonNull public Long received; // compose = stored @@ -137,7 +138,6 @@ public class EntityMessage implements Serializable { this.body = (body == null ? "" : body); out = new BufferedWriter(new FileWriter(file)); out.write(this.body); - this.downloaded = true; } finally { if (out != null) try { diff --git a/app/src/main/java/eu/faircode/email/EntityOperation.java b/app/src/main/java/eu/faircode/email/EntityOperation.java index 427bc894a1..ec40c3879a 100644 --- a/app/src/main/java/eu/faircode/email/EntityOperation.java +++ b/app/src/main/java/eu/faircode/email/EntityOperation.java @@ -70,9 +70,9 @@ public class EntityOperation { public static final String MOVE = "move"; public static final String DELETE = "delete"; public static final String SEND = "send"; - public static final String ATTACHMENT = "attachment"; public static final String HEADERS = "headers"; public static final String BODY = "body"; + public static final String ATTACHMENT = "attachment"; public static final String FLAG = "flag"; private static List queue = new ArrayList<>(); diff --git a/app/src/main/java/eu/faircode/email/FragmentAbout.java b/app/src/main/java/eu/faircode/email/FragmentAbout.java index 18ec23c05c..f5ad7f848f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAbout.java +++ b/app/src/main/java/eu/faircode/email/FragmentAbout.java @@ -119,6 +119,7 @@ public class FragmentAbout extends FragmentEx { draft.msgid = EntityMessage.generateMessageId(); draft.to = new Address[]{Helper.myAddress()}; draft.subject = context.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME + " debug info"; + draft.content = true; draft.received = new Date().getTime(); draft.seen = false; draft.ui_seen = false; diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index c1bc5f6081..9222981bc1 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -670,7 +670,7 @@ public class FragmentCompose extends FragmentEx { os = new BufferedOutputStream(new FileOutputStream(file)); int size = 0; - byte[] buffer = new byte[Helper.ATTACHMENT_BUFFER_SIZE]; + byte[] buffer = new byte[EntityAttachment.ATTACHMENT_BUFFER_SIZE]; for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { size += len; os.write(buffer, 0, len); @@ -877,6 +877,7 @@ public class FragmentCompose extends FragmentEx { body = "
" + account.signature + body; } + draft.content = true; draft.received = new Date().getTime(); draft.seen = false; draft.ui_seen = false; diff --git a/app/src/main/java/eu/faircode/email/FragmentMessage.java b/app/src/main/java/eu/faircode/email/FragmentMessage.java index f315701eb2..4e9a843fc1 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessage.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessage.java @@ -483,7 +483,7 @@ public class FragmentMessage extends FragmentEx { pbBody.setVisibility(View.VISIBLE); - if (message.downloaded) + if (message.content) bodyTask.load(FragmentMessage.this, args); btnImages.setOnClickListener(new View.OnClickListener() { @@ -536,6 +536,7 @@ public class FragmentMessage extends FragmentEx { // Observe message db.message().liveMessage(message.id).observe(getViewLifecycleOwner(), new Observer() { + private boolean observing = false; @Override public void onChanged(@Nullable final TupleMessageEx message) { @@ -555,7 +556,7 @@ public class FragmentMessage extends FragmentEx { pbRawHeaders.setVisibility(!free && headers && message.headers == null ? View.VISIBLE : View.GONE); // Body can be downloaded - if (message.downloaded) { + if (message.content) { Bundle args = new Bundle(); args.putLong("id", message.id); args.putBoolean("show_images", show_images); @@ -572,7 +573,9 @@ public class FragmentMessage extends FragmentEx { setSubtitle(Helper.localizeFolderName(getContext(), message.folderName)); // Observe folders - db.folder().liveFolders(message.account).removeObservers(getViewLifecycleOwner()); + if (observing) + return; + observing = true; db.folder().liveFolders(message.account).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List folders) { @@ -605,7 +608,7 @@ public class FragmentMessage extends FragmentEx { bottom_navigation.getMenu().findItem(R.id.action_delete).setVisible((message.uid != null && hasTrash) || (inOutbox && !TextUtils.isEmpty(message.error))); bottom_navigation.getMenu().findItem(R.id.action_move).setVisible(message.uid != null && (!inInbox || hasUser)); bottom_navigation.getMenu().findItem(R.id.action_archive).setVisible(message.uid != null && !inArchive && hasArchive); - bottom_navigation.getMenu().findItem(R.id.action_reply).setVisible(message.downloaded && !inOutbox); + bottom_navigation.getMenu().findItem(R.id.action_reply).setVisible(message.content && !inOutbox); bottom_navigation.setVisibility(View.VISIBLE); } }); @@ -623,7 +626,7 @@ public class FragmentMessage extends FragmentEx { adapter.set(attachments); grpAttachments.setVisibility(!free && attachments.size() > 0 ? View.VISIBLE : View.GONE); - if (message.downloaded) { + if (message.content) { Bundle args = new Bundle(); args.putLong("id", message.id); args.putBoolean("show_images", show_images); @@ -664,12 +667,12 @@ public class FragmentMessage extends FragmentEx { menu.findItem(R.id.menu_addresses).setVisible(!free); menu.findItem(R.id.menu_thread).setVisible(message.count > 1); - menu.findItem(R.id.menu_forward).setVisible(message.downloaded && !inOutbox); + menu.findItem(R.id.menu_forward).setVisible(message.content && !inOutbox); menu.findItem(R.id.menu_show_headers).setChecked(headers); menu.findItem(R.id.menu_show_headers).setEnabled(message.uid != null); menu.findItem(R.id.menu_show_headers).setVisible(!free); - menu.findItem(R.id.menu_show_html).setEnabled(message.downloaded && Helper.classExists("android.webkit.WebView")); - menu.findItem(R.id.menu_reply_all).setVisible(message.downloaded && !inOutbox); + menu.findItem(R.id.menu_show_html).setEnabled(message.content && Helper.classExists("android.webkit.WebView")); + menu.findItem(R.id.menu_reply_all).setVisible(message.content && !inOutbox); } @Override diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index 1397023a54..8f3edf9dfa 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -46,6 +46,7 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.text.DecimalFormat; import java.util.concurrent.ThreadFactory; import javax.mail.Address; @@ -64,8 +65,6 @@ public class Helper { static final int AUTH_TYPE_PASSWORD = 1; static final int AUTH_TYPE_GMAIL = 2; - static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes - static ThreadFactory backgroundThreadFactory = new ThreadFactory() { @Override public Thread newThread(@NonNull Runnable runnable) { @@ -137,7 +136,7 @@ public class Helper { if (bytes < unit) return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); - return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + return new DecimalFormat("@@").format(bytes / Math.pow(unit, exp)) + " " + pre + "B"; } static boolean classExists(String className) { diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index 4d7b3238b3..01be807ec5 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -92,6 +92,8 @@ public class MessageHelper { //props.put("mail.imaps.compress.strategy", "0"); } + props.put("mail.imaps.fetchsize", Integer.toString(64 * 1024)); // default 16K + // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties props.put("mail.smtps.ssl.checkserveridentity", "true"); props.put("mail.smtps.ssl.trust", "*"); @@ -270,6 +272,11 @@ public class MessageHelper { return null; } + Integer getSize() throws MessagingException { + int size = imessage.getSize(); + return (size < 0 ? null : size); + } + static String getFormattedAddresses(Address[] addresses, boolean full) { if (addresses == null || addresses.length == 0) return ""; diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index b03d61014d..50f00c17a1 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -54,12 +54,7 @@ import com.sun.mail.util.MailConnectException; import org.json.JSONArray; import org.json.JSONException; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; @@ -127,6 +122,7 @@ public class ServiceSynchronize extends LifecycleService { private static final int CONNECT_BACKOFF_START = 8; // seconds private static final int CONNECT_BACKOFF_MAX = 1024; // seconds (1024 sec ~ 17 min) private static final long STORE_NOOP_INTERVAL = 9 * 60 * 1000L; // ms + private static final int MESSAGE_AUTO_DOWNLOAD_SIZE = 32 * 1024; // bytes private static final int ATTACHMENT_AUTO_DOWNLOAD_SIZE = 32 * 1024; // bytes static final String ACTION_SYNCHRONIZE_FOLDER = BuildConfig.APPLICATION_ID + ".SYNCHRONIZE_FOLDER"; @@ -925,15 +921,15 @@ public class ServiceSynchronize extends LifecycleService { else if (EntityOperation.SEND.equals(op.name)) doSend(message, db); - else if (EntityOperation.ATTACHMENT.equals(op.name)) - doAttachment(folder, op, ifolder, message, jargs, db); - else if (EntityOperation.HEADERS.equals(op.name)) doHeaders(folder, ifolder, message, db); else if (EntityOperation.BODY.equals(op.name)) doBody(folder, ifolder, message, db); + else if (EntityOperation.ATTACHMENT.equals(op.name)) + doAttachment(folder, op, ifolder, message, jargs, db); + else throw new MessagingException("Unknown operation name=" + op.name); @@ -1142,68 +1138,6 @@ public class ServiceSynchronize extends LifecycleService { } } - private void doAttachment(EntityFolder folder, EntityOperation op, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws JSONException, MessagingException, IOException { - // Download attachment - int sequence = jargs.getInt(0); - - EntityAttachment attachment = db.attachment().getAttachment(op.message, sequence); - if (attachment == null) - return; - - try { - // Get message - Message imessage = ifolder.getMessageByUID(message.uid); - if (imessage == null) - throw new MessageRemovedException(); - - // Get attachment - MessageHelper helper = new MessageHelper((MimeMessage) imessage); - EntityAttachment a = helper.getAttachments().get(sequence - 1); - - // Build filename - File file = EntityAttachment.getFile(this, attachment.id); - - // Download attachment - InputStream is = null; - OutputStream os = null; - try { - is = a.part.getInputStream(); - os = new BufferedOutputStream(new FileOutputStream(file)); - - int size = 0; - byte[] buffer = new byte[Helper.ATTACHMENT_BUFFER_SIZE]; - for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { - size += len; - os.write(buffer, 0, len); - - // Update progress - if (attachment.size != null) - db.attachment().setProgress(attachment.id, size * 100 / attachment.size); - } - - // Store attachment data - attachment.size = size; - attachment.progress = null; - attachment.available = true; - db.attachment().updateAttachment(attachment); - } finally { - try { - if (is != null) - is.close(); - } finally { - if (os != null) - os.close(); - } - } - Log.i(Helper.TAG, folder.name + " downloaded bytes=" + attachment.size); - } catch (Throwable ex) { - // Reset progress on failure - attachment.progress = null; - db.attachment().updateAttachment(attachment); - throw ex; - } - } - private void doHeaders(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, DB db) throws MessagingException { Message imessage = ifolder.getMessageByUID(message.uid); Enumeration
headers = imessage.getAllHeaders(); @@ -1216,10 +1150,39 @@ public class ServiceSynchronize extends LifecycleService { } private void doBody(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, DB db) throws MessagingException, IOException { + // Download message body + if (message.content) + return; + + // Get message Message imessage = ifolder.getMessageByUID(message.uid); + if (imessage == null) + throw new MessageRemovedException(); + MessageHelper helper = new MessageHelper((MimeMessage) imessage); message.write(this, helper.getHtml()); - db.message().setMessageDownloaded(message.id, true); + db.message().setMessageContent(message.id, true); + } + + private void doAttachment(EntityFolder folder, EntityOperation op, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws JSONException, MessagingException, IOException { + // Download attachment + int sequence = jargs.getInt(0); + + // Get attachment + EntityAttachment attachment = db.attachment().getAttachment(op.message, sequence); + if (attachment.available) + return; + + // Get message + Message imessage = ifolder.getMessageByUID(message.uid); + if (imessage == null) + throw new MessageRemovedException(); + + // Download attachment + MessageHelper helper = new MessageHelper((MimeMessage) imessage); + EntityAttachment a = helper.getAttachments().get(sequence - 1); + attachment.part = a.part; + attachment.download(this, db); } private void synchronizeFolders(EntityAccount account, IMAPStore istore, ServiceState state) throws MessagingException { @@ -1501,7 +1464,8 @@ public class ServiceSynchronize extends LifecycleService { message.bcc = helper.getBcc(); message.reply = helper.getReply(); message.subject = imessage.getSubject(); - message.downloaded = download; + message.size = helper.getSize(); + message.content = false; message.received = imessage.getReceivedDate().getTime(); message.sent = (imessage.getSentDate() == null ? null : imessage.getSentDate().getTime()); message.seen = seen; @@ -1513,22 +1477,29 @@ public class ServiceSynchronize extends LifecycleService { message.id = db.message().insertMessage(message); - if (download) - message.write(context, helper.getHtml()); + if (download) { + ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); + boolean metered = cm.isActiveNetworkMetered(); + if (!metered || (message.size != null && message.size < MESSAGE_AUTO_DOWNLOAD_SIZE)) { + message.write(context, helper.getHtml()); + db.message().setMessageContent(message.id, true); + } + } Log.i(Helper.TAG, folder.name + " added id=" + message.id + " uid=" + message.uid); int sequence = 0; for (EntityAttachment attachment : helper.getAttachments()) { sequence++; - Log.i(Helper.TAG, "attachment seq=" + sequence + - " name=" + attachment.name + " type=" + attachment.type); + Log.i(Helper.TAG, folder.name + " attachment" + + " seq=" + sequence + " name=" + attachment.name + " type=" + attachment.type); attachment.message = message.id; attachment.sequence = sequence; attachment.id = db.attachment().insertAttachment(attachment); - if (attachment.size != null && attachment.size < ATTACHMENT_AUTO_DOWNLOAD_SIZE) - EntityOperation.queue(db, message, EntityOperation.ATTACHMENT, sequence); + if (download) + if (attachment.size != null && attachment.size < ATTACHMENT_AUTO_DOWNLOAD_SIZE) + attachment.download(context, db); } result = 1; diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml index d162ced444..45c5e39b22 100644 --- a/app/src/main/res/layout/item_message.xml +++ b/app/src/main/res/layout/item_message.xml @@ -45,10 +45,21 @@ android:maxLines="1" android:text="From" android:textAppearance="@style/TextAppearance.AppCompat.Medium" - app:layout_constraintEnd_toStartOf="@+id/tvTime" + app:layout_constraintEnd_toStartOf="@+id/tvSize" app:layout_constraintStart_toEndOf="@id/ivAvatar" app:layout_constraintTop_toTopOf="parent" /> + +