diff --git a/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java b/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java index 2587cb652c..74e6555c62 100644 --- a/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java +++ b/app/src/main/java/com/sun/mail/smtp/SMTPTransport.java @@ -1300,6 +1300,16 @@ public class SMTPTransport extends Transport { try { mailFrom(); rcptTo(); + eu.faircode.email.ObjectHolder total = new eu.faircode.email.ObjectHolder<>(); + total.value = 0; + this.message.writeTo(new OutputStream(){ + @Override + public void write(int b) throws IOException { + total.value++; + } + }); + IProgress progress = (IProgress) session.getProperties() + .get("mail." + name + ".progress"); if (chunkSize > 0 && supportsExtension("CHUNKING")) { /* * Use BDAT to send the data in chunks. @@ -1310,12 +1320,34 @@ public class SMTPTransport extends Transport { * from the message content, and b) the message content is * encoded before we even know that we can use BDAT. */ - this.message.writeTo(bdat(), ignoreList); + this.message.writeTo(new FilterOutputStream(bdat()) { + private int size = 0; + + @Override + public void write(int b) throws IOException { + super.write(b); + size++; + if (progress != null && (size % 1024) == 0) + progress.report(size, total.value); + } + }, ignoreList); finishBdat(); } else { - this.message.writeTo(data(), ignoreList); + this.message.writeTo(new FilterOutputStream(data()) { + private int size = 0; + + @Override + public void write(int b) throws IOException { + super.write(b); + size++; + if (progress != null && (size % 1024) == 0) + progress.report(size, total.value); + } + }, ignoreList); finishData(); } + if (progress != null) + progress.finished(); if (sendPartiallyFailed) { // throw the exception, // fire TransportEvent.MESSAGE_PARTIALLY_DELIVERED event @@ -1377,6 +1409,12 @@ public class SMTPTransport extends Transport { sendMessageEnd(); } + public interface IProgress { + void report(int size, int total); + + void finished(); + } + /** * The send failed, fix the address arrays to report the failure correctly. */ diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java index fb413ee1bb..1c6f3afdf6 100644 --- a/app/src/main/java/eu/faircode/email/EmailService.java +++ b/app/src/main/java/eu/faircode/email/EmailService.java @@ -26,7 +26,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.Network; -import android.net.NetworkInfo; import android.os.Build; import android.os.ParcelFileDescriptor; import android.security.KeyChain; @@ -301,6 +300,10 @@ public class EmailService implements AutoCloseable { properties.put("mail." + protocol + ".dsn.notify", what); } + void setProgress(SMTPTransport.IProgress listener) { + properties.put("mail." + protocol + ".progress", listener); + } + void setListener(StoreListener listener) { this.listener = listener; } diff --git a/app/src/main/java/eu/faircode/email/ServiceSend.java b/app/src/main/java/eu/faircode/email/ServiceSend.java index 5b28bbc189..b988ae521b 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSend.java +++ b/app/src/main/java/eu/faircode/email/ServiceSend.java @@ -32,6 +32,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.PowerManager; +import android.os.SystemClock; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -41,6 +42,7 @@ import androidx.lifecycle.Observer; import androidx.preference.PreferenceManager; import com.sun.mail.smtp.SMTPSendFailedException; +import com.sun.mail.smtp.SMTPTransport; import java.io.File; import java.io.FileNotFoundException; @@ -67,6 +69,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar private TupleUnsent lastUnsent = null; private Network lastActive = null; private boolean lastSuitable = false; + private int lastProgress = -1; private TwoStateOwner owner; private PowerManager.WakeLock wlOutbox; @@ -76,6 +79,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar private static final int RETRY_MAX = 3; private static final int CONNECTIVITY_DELAY = 5000; // milliseconds + private static final int PROGRESS_UPDATE_INTERVAL = 1000; // milliseconds static final int PI_SEND = 1; @@ -232,6 +236,8 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar builder.setSubText(getString(R.string.title_notification_idle)); if (!lastSuitable) builder.setSubText(getString(R.string.title_notification_waiting)); + if (lastProgress >= 0) + builder.setProgress(100, lastProgress, false); return builder; } @@ -514,6 +520,9 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar db.message().setMessageLastAttempt(message.id, message.last_attempt); } + NotificationManager nm = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean reply_move = prefs.getBoolean("reply_move", false); boolean debug = prefs.getBoolean("debug", false); @@ -675,6 +684,30 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar String via = "via " + ident.host + "/" + ident.user + " to " + (to == null ? null : TextUtils.join(", ", to)); + iservice.setProgress(new SMTPTransport.IProgress() { + private int progress = -1; + private long last = SystemClock.elapsedRealtime(); + + @Override + public void report(int size, int total) { + int p = (total == 0 ? 0 : 100 * size / total); + if (p > progress) { + progress = p; + long now = SystemClock.elapsedRealtime(); + if (now > last + PROGRESS_UPDATE_INTERVAL) { + last = now; + lastProgress = progress; + nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService().build()); + } + } + } + + @Override + public void finished() { + lastProgress = -1; + } + }); + // Send message EntityLog.log(this, "Sending " + via); start = new Date().getTime(); @@ -703,6 +736,10 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar throw ex; } finally { + if (lastProgress >= 0) { + lastProgress = -1; + nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService().build()); + } db.identity().setIdentityState(ident.id, null); } @@ -745,8 +782,6 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar db.endTransaction(); } - NotificationManager nm = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); nm.cancel("send:" + message.id, NotificationHelper.NOTIFICATION_TAGGED); // Check sent message