Browse Source

Download small messages on metered connection only, refactoring, improvements

main
M66B 6 years ago
parent
commit
265e7fe88f
17 changed files with 173 additions and 111 deletions
  1. +4
    -4
      FAQ.md
  2. +11
    -5
      app/schemas/eu.faircode.email.DB/16.json
  3. +2
    -1
      app/src/main/java/eu/faircode/email/ActivityView.java
  4. +1
    -1
      app/src/main/java/eu/faircode/email/AdapterAttachment.java
  5. +8
    -3
      app/src/main/java/eu/faircode/email/AdapterMessage.java
  6. +2
    -1
      app/src/main/java/eu/faircode/email/DB.java
  7. +2
    -2
      app/src/main/java/eu/faircode/email/DaoMessage.java
  8. +56
    -0
      app/src/main/java/eu/faircode/email/EntityAttachment.java
  9. +2
    -2
      app/src/main/java/eu/faircode/email/EntityMessage.java
  10. +1
    -1
      app/src/main/java/eu/faircode/email/EntityOperation.java
  11. +1
    -0
      app/src/main/java/eu/faircode/email/FragmentAbout.java
  12. +2
    -1
      app/src/main/java/eu/faircode/email/FragmentCompose.java
  13. +11
    -8
      app/src/main/java/eu/faircode/email/FragmentMessage.java
  14. +2
    -3
      app/src/main/java/eu/faircode/email/Helper.java
  15. +7
    -0
      app/src/main/java/eu/faircode/email/MessageHelper.java
  16. +49
    -78
      app/src/main/java/eu/faircode/email/ServiceSynchronize.java
  17. +12
    -1
      app/src/main/res/layout/item_message.xml

+ 4
- 4
FAQ.md View File

@ -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).


+ 11
- 5
app/schemas/eu.faircode.email.DB/16.json View File

@ -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\")"
]
}
}

+ 2
- 1
app/src/main/java/eu/faircode/email/ActivityView.java View File

@ -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)) {


+ 1
- 1
app/src/main/java/eu/faircode/email/AdapterAttachment.java View File

@ -98,7 +98,7 @@ public class AdapterAttachment extends RecyclerView.Adapter<AdapterAttachment.Vi
tvName.setText(attachment.name);
if (attachment.size != null)
tvSize.setText(Helper.humanReadableByteCount(attachment.size, false));
tvSize.setText(Helper.humanReadableByteCount(attachment.size, true));
tvSize.setVisibility(attachment.size == null ? View.GONE : View.VISIBLE);
if (attachment.available) {


+ 8
- 3
app/src/main/java/eu/faircode/email/AdapterMessage.java View File

@ -86,6 +86,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
ImageView ivAvatar;
ImageView ivFlagged;
TextView tvFrom;
TextView tvSize;
TextView tvTime;
ImageView ivAttachments;
TextView tvSubject;
@ -103,9 +104,10 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
this.itemView = itemView;
vwColor = itemView.findViewById(R.id.vwColor);
ivAvatar = itemView.findViewById(R.id.ivAvatar);
ivFlagged = itemView.findViewById(R.id.ivFlagged);
ivAvatar = itemView.findViewById(R.id.ivAvatar);
tvFrom = itemView.findViewById(R.id.tvFrom);
tvSize = itemView.findViewById(R.id.tvSize);
tvTime = itemView.findViewById(R.id.tvTime);
ivAttachments = itemView.findViewById(R.id.ivAttachments);
tvSubject = itemView.findViewById(R.id.tvSubject);
@ -130,8 +132,8 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
ivFlagged.setVisibility(View.GONE);
ivAvatar.setVisibility(View.GONE);
tvFrom.setText(null);
tvSize.setText(null);
tvTime.setText(null);
tvSubject.setText(null);
ivAttachments.setVisibility(View.GONE);
tvSubject.setText(null);
tvFolder.setText(null);
@ -201,8 +203,11 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
tvTime.setText(DateUtils.getRelativeTimeSpanString(context, message.received));
}
tvSubject.setText(message.subject);
tvSize.setText(message.size == null ? null : Helper.humanReadableByteCount(message.size, true));
tvSize.setVisibility(message.size == null ? View.GONE : View.VISIBLE);
ivAttachments.setVisibility(message.attachments > 0 ? View.VISIBLE : View.GONE);
tvSubject.setText(message.subject);
if (viewType == ViewType.UNIFIED)
tvFolder.setText(message.accountName);


+ 2
- 1
app/src/main/java/eu/faircode/email/DB.java View File

@ -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();


+ 2
- 2
app/src/main/java/eu/faircode/email/DaoMessage.java View File

@ -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);


+ 56
- 0
app/src/main/java/eu/faircode/email/EntityAttachment.java View File

@ -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) {


+ 2
- 2
app/src/main/java/eu/faircode/email/EntityMessage.java View File

@ -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 {


+ 1
- 1
app/src/main/java/eu/faircode/email/EntityOperation.java View File

@ -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<Intent> queue = new ArrayList<>();


+ 1
- 0
app/src/main/java/eu/faircode/email/FragmentAbout.java View File

@ -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;


+ 2
- 1
app/src/main/java/eu/faircode/email/FragmentCompose.java View File

@ -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 = "<br />" + account.signature + body;
}
draft.content = true;
draft.received = new Date().getTime();
draft.seen = false;
draft.ui_seen = false;


+ 11
- 8
app/src/main/java/eu/faircode/email/FragmentMessage.java View File

@ -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<TupleMessageEx>() {
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<List<TupleFolderEx>>() {
@Override
public void onChanged(@Nullable List<TupleFolderEx> 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


+ 2
- 3
app/src/main/java/eu/faircode/email/Helper.java View File

@ -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) {


+ 7
- 0
app/src/main/java/eu/faircode/email/MessageHelper.java View File

@ -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 "";


+ 49
- 78
app/src/main/java/eu/faircode/email/ServiceSynchronize.java View File

@ -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<Header> 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;


+ 12
- 1
app/src/main/res/layout/item_message.xml View File

@ -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" />
<TextView
android:id="@+id/tvSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:maxLines="1"
android:text="123 KB"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@id/tvFrom"
app:layout_constraintEnd_toStartOf="@id/tvTime" />
<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"


Loading…
Cancel
Save