Show account/folder connectivity

This commit is contained in:
M66B 2018-08-13 14:44:47 +00:00
parent 6bba997cce
commit e72b1a7a3d
142 changed files with 210 additions and 111 deletions

13
FAQ.md
View File

@ -10,6 +10,7 @@ Frequently Asked Questions
* Full network access (INTERNET): to send and receive email
* View network connections (ACCESS_NETWORK_STATE): to monitor internet connectivity changes
* Run at startup (RECEIVE_BOOT_COMPLETED): to start monitoring on device start
* Optional: read your contacts (READ_CONTACTS): to autocomplete addresses
<a name="FAQ2"></a>
**(2) Why is there a permanent notification shown?**
@ -24,12 +25,12 @@ 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:
* Mark message as seen/unseen in remote folder
* Add message to remote folder
* Move message to another remote folder
* Delete message from remote folder
* Send message
* Download attachment
* 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
<a name="FAQ4"></a>
**(4) What is a valid security certificate?**

View File

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "0b5e9888b548ea410934b7082b08a3b6",
"identityHash": "fc698ded287d449f2781e8efa338554d",
"entities": [
{
"tableName": "identity",
@ -113,7 +113,7 @@
},
{
"tableName": "account",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `primary` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `seen_until` INTEGER, `error` TEXT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `primary` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `seen_until` INTEGER, `state` TEXT, `error` TEXT)",
"fields": [
{
"fieldPath": "id",
@ -169,6 +169,12 @@
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "state",
"columnName": "state",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "error",
"columnName": "error",
@ -187,7 +193,7 @@
},
{
"tableName": "folder",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `synchronize` INTEGER NOT NULL, `after` INTEGER NOT NULL, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `synchronize` INTEGER NOT NULL, `after` INTEGER NOT NULL, `state` TEXT, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
@ -225,6 +231,12 @@
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "state",
"columnName": "state",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "error",
"columnName": "error",
@ -752,7 +764,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, \"0b5e9888b548ea410934b7082b08a3b6\")"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"fc698ded287d449f2781e8efa338554d\")"
]
}
}

View File

@ -358,10 +358,8 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
try {
db.beginTransaction();
for (EntityAccount account : db.account().getAccounts(true)) {
account.seen_until = time;
db.account().updateAccount(account);
}
for (EntityAccount account : db.account().getAccounts(true))
db.account().setAccountSeenUntil(account.id, time);
db.setTransactionSuccessful();
} finally {
@ -500,8 +498,7 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
EntityFolder folder = db.folder().getFolder(message.folder);
if (!EntityFolder.OUTBOX.equals(folder.type))
for (EntityMessage tmessage : db.message().getMessageByThread(message.account, message.thread)) {
tmessage.ui_seen = true;
db.message().updateMessage(tmessage);
db.message().setMessageUiSeen(tmessage.id, true);
EntityOperation.queue(db, tmessage, EntityOperation.SEEN, tmessage.ui_seen);
}

View File

@ -54,6 +54,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
ImageView ivSync;
TextView tvHost;
TextView tvUser;
ImageView ivState;
TextView tvError;
ViewHolder(View itemView) {
@ -65,6 +66,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
ivSync = itemView.findViewById(R.id.ivSync);
tvHost = itemView.findViewById(R.id.tvHost);
tvUser = itemView.findViewById(R.id.tvUser);
ivState = itemView.findViewById(R.id.ivState);
tvError = itemView.findViewById(R.id.tvError);
}
@ -82,6 +84,14 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
ivSync.setVisibility(account.synchronize ? View.VISIBLE : View.INVISIBLE);
tvHost.setText(String.format("%s:%d", account.host, account.port));
tvUser.setText(account.user);
if ("connected".equals(account.state))
ivState.setImageResource(R.drawable.baseline_cloud_24);
else if ("connecting".equals(account.state))
ivState.setImageResource(R.drawable.baseline_cloud_queue_24);
else
ivState.setImageResource(R.drawable.baseline_cloud_off_24);
ivState.setVisibility(account.synchronize ? View.VISIBLE : View.INVISIBLE);
tvError.setText(account.error);
tvError.setVisibility(account.error == null ? View.GONE : View.VISIBLE);
}

View File

@ -56,6 +56,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
TextView tvType;
TextView tvAfter;
ImageView ivSync;
ImageView ivState;
TextView tvError;
ViewHolder(View itemView) {
@ -69,6 +70,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
tvAfter = itemView.findViewById(R.id.tvAfter);
ivSync = itemView.findViewById(R.id.ivSync);
tvError = itemView.findViewById(R.id.tvError);
ivState = itemView.findViewById(R.id.ivState);
}
private void wire(boolean properties) {
@ -103,6 +105,14 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
ivSync.setVisibility(folder.synchronize ? View.VISIBLE : View.INVISIBLE);
if ("connected".equals(folder.state))
ivState.setImageResource(R.drawable.baseline_cloud_24);
else if ("connecting".equals(folder.state))
ivState.setImageResource(R.drawable.baseline_cloud_queue_24);
else
ivState.setImageResource(R.drawable.baseline_cloud_off_24);
ivState.setVisibility(folder.synchronize ? View.VISIBLE : View.INVISIBLE);
tvError.setText(folder.error);
tvError.setVisibility(folder.error == null ? View.GONE : View.VISIBLE);
}

View File

@ -68,6 +68,15 @@ public interface DaoAccount {
@Update
void updateAccount(EntityAccount account);
@Query("UPDATE account SET seen_until = :time WHERE id = :id")
int setAccountSeenUntil(long id, long time);
@Query("UPDATE account SET state = :state WHERE id = :id")
int setAccountState(long id, String state);
@Query("UPDATE account SET error = :error WHERE id = :id")
int setAccountError(long id, String error);
@Query("UPDATE account SET `primary` = 0")
void resetPrimary();

View File

@ -50,7 +50,7 @@ public interface DaoAttachment {
EntityAttachment getAttachment(long message, int sequence);
@Query("UPDATE attachment SET progress = :progress WHERE id = :id")
void setProgress(long id, int progress);
void setProgress(long id, Integer progress);
@Insert
long insertAttachment(EntityAttachment attachment);

View File

@ -25,7 +25,6 @@ import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
@Dao
public interface DaoFolder {
@ -82,8 +81,17 @@ public interface DaoFolder {
@Insert
long insertFolder(EntityFolder folder);
@Update
void updateFolder(EntityFolder folder);
@Query("UPDATE folder SET state = :state WHERE id = :id")
int setFolderState(long id, String state);
@Query("UPDATE folder SET error = :error WHERE id = :id")
int setFolderError(long id, String error);
@Query("UPDATE folder SET type = :type WHERE id = :id")
int setFolderType(long id, String type);
@Query("UPDATE folder SET synchronize = :synchronize, after = :after WHERE id = :id")
int setFolderProperties(long id, boolean synchronize, int after);
@Query("DELETE FROM folder WHERE account= :account AND name = :name")
void deleteFolder(Long account, String name);

View File

@ -112,6 +112,21 @@ public interface DaoMessage {
@Update
void updateMessage(EntityMessage message);
@Query("UPDATE message SET uid = :uid WHERE id = :id")
int setMessageUid(long id, Long uid);
@Query("UPDATE message SET seen = :seen WHERE id = :id")
int setMessageSeen(long id, boolean seen);
@Query("UPDATE message SET ui_seen = :ui_seen WHERE id = :id")
int setMessageUiSeen(long id, boolean ui_seen);
@Query("UPDATE message SET ui_hide = :ui_hide WHERE id = :id")
int setMessageUiHide(long id, boolean ui_hide);
@Query("UPDATE message SET error = :error WHERE id = :id")
int setMessageError(long id, String error);
@Query("DELETE FROM message WHERE id = :id")
int deleteMessage(long id);

View File

@ -47,6 +47,7 @@ public class EntityAccount {
@NonNull
public Boolean synchronize;
public Long seen_until;
public String state;
public String error;
@Override
@ -60,6 +61,7 @@ public class EntityAccount {
this.password.equals(other.password) &&
this.primary.equals(other.primary) &&
this.synchronize.equals(other.synchronize) &&
(this.state == null ? other.state == null : this.state.equals(other.state)) &&
(this.error == null ? other.error == null : this.error.equals(other.error)));
} else
return false;

View File

@ -57,6 +57,7 @@ public class EntityFolder implements Serializable {
public Boolean synchronize;
@NonNull
public Integer after; // days
public String state;
public String error;
static final String INBOX = "Inbox";
@ -114,6 +115,7 @@ public class EntityFolder implements Serializable {
this.type.equals(other.type) &&
this.synchronize.equals(other.synchronize) &&
this.after.equals(other.after) &&
(this.state == null ? other.state == null : this.state.equals(other.state)) &&
(this.error == null ? other.error == null : this.error.equals(other.error)));
} else
return false;

View File

@ -488,10 +488,8 @@ public class FragmentAccount extends FragmentEx {
folder.account = account.id;
Log.i(Helper.TAG, "Creating folder=" + folder.name + " (" + folder.type + ")");
folder.id = db.folder().insertFolder(folder);
} else {
existing.type = folder.type;
db.folder().updateFolder(existing);
}
} else
db.folder().setFolderType(existing.id, folder.type);
}
db.setTransactionSuccessful();

View File

@ -453,15 +453,14 @@ public class FragmentCompose extends FragmentEx {
os.write(buffer, 0, len);
// Update progress
if (attachment.size != null) {
attachment.progress = size * 100 / attachment.size;
db.attachment().updateAttachment(attachment);
}
if (attachment.size != null)
db.attachment().setProgress(attachment.id, size * 100 / attachment.size);
}
attachment.size = size;
attachment.progress = null;
attachment.filename = file.getName();
db.attachment().updateAttachment(attachment);
} finally {
try {
if (is != null)
@ -471,8 +470,6 @@ public class FragmentCompose extends FragmentEx {
os.close();
}
}
db.attachment().updateAttachment(attachment);
} catch (Throwable ex) {
// Reset progress on failure
attachment.progress = null;

View File

@ -88,11 +88,9 @@ public class FragmentFolder extends FragmentEx {
try {
db.beginTransaction();
EntityFolder folder = db.folder().getFolder(id);
folder.synchronize = synchronize;
folder.after = days;
db.folder().updateFolder(folder);
db.folder().setFolderProperties(id, synchronize, days);
EntityFolder folder = db.folder().getFolder(id);
if (!folder.synchronize)
db.message().deleteMessages(folder.id);

View File

@ -405,8 +405,7 @@ public class FragmentMessage extends FragmentEx {
EntityMessage message = db.message().getMessage(id);
for (EntityMessage tmessage : db.message().getMessageByThread(message.account, message.thread)) {
tmessage.ui_seen = !message.ui_seen;
db.message().updateMessage(tmessage);
db.message().setMessageUiSeen(tmessage.id, !message.ui_seen);
EntityOperation.queue(db, tmessage, EntityOperation.SEEN, tmessage.ui_seen);
}
@ -518,10 +517,9 @@ public class FragmentMessage extends FragmentEx {
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
message.ui_hide = true;
db.message().updateMessage(message);
db.message().setMessageUiHide(id, true);
EntityMessage message = db.message().getMessage(id);
EntityFolder spam = db.folder().getFolderByType(message.account, EntityFolder.JUNK);
EntityOperation.queue(db, message, EntityOperation.MOVE, spam.id);
@ -575,9 +573,9 @@ public class FragmentMessage extends FragmentEx {
try {
db.beginTransaction();
db.message().setMessageUiHide(id, true);
EntityMessage message = db.message().getMessage(id);
message.ui_hide = true;
db.message().updateMessage(message);
EntityOperation.queue(db, message, EntityOperation.DELETE);
db.setTransactionSuccessful();
@ -621,10 +619,9 @@ public class FragmentMessage extends FragmentEx {
if (debug && BuildConfig.DEBUG)
db.message().deleteMessage(id);
else {
EntityMessage message = db.message().getMessage(id);
message.ui_hide = true;
db.message().updateMessage(message);
db.message().setMessageUiHide(id, true);
EntityMessage message = db.message().getMessage(id);
EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TRASH);
EntityOperation.queue(db, message, EntityOperation.MOVE, trash.id);
}
@ -727,10 +724,8 @@ public class FragmentMessage extends FragmentEx {
EntityMessage message = db.message().getMessage(id);
EntityFolder folder = db.folder().getFolder(message.folder);
if (!EntityFolder.ARCHIVE.equals(folder.type)) {
message.ui_hide = true;
db.message().updateMessage(message);
}
if (!EntityFolder.ARCHIVE.equals(folder.type))
db.message().setMessageUiHide(message.id, true);
EntityOperation.queue(db, message, EntityOperation.MOVE, target);
@ -780,10 +775,9 @@ public class FragmentMessage extends FragmentEx {
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
message.ui_hide = true;
db.message().updateMessage(message);
db.message().setMessageUiHide(id, true);
EntityMessage message = db.message().getMessage(id);
EntityFolder archive = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE);
EntityOperation.queue(db, message, EntityOperation.MOVE, archive.id);

View File

@ -194,10 +194,8 @@ public class ServiceSynchronize extends LifecycleService {
try {
db.beginTransaction();
for (EntityAccount account : db.account().getAccounts(true)) {
account.seen_until = time;
db.account().updateAccount(account);
}
for (EntityAccount account : db.account().getAccounts(true))
db.account().setAccountSeenUntil(account.id, time);
db.setTransactionSuccessful();
} finally {
@ -327,6 +325,9 @@ public class ServiceSynchronize extends LifecycleService {
boolean debug = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("debug", false);
Log.i(Helper.TAG, account.name + " start ");
final DB db = DB.getInstance(ServiceSynchronize.this);
db.account().setAccountState(account.id, "connecting");
while (state.running) {
IMAPStore istore = null;
try {
@ -335,6 +336,7 @@ public class ServiceSynchronize extends LifecycleService {
props.setProperty("mail.mime.address.strict", "false");
props.setProperty("mail.mime.decodetext.strict", "false");
//props.put("mail.imaps.minidletime", "5000");
final Session isession = Session.getInstance(props, null);
isession.setDebug(debug);
// adb -t 1 logcat | grep "eu.faircode.email\|System.out"
@ -347,11 +349,6 @@ public class ServiceSynchronize extends LifecycleService {
@Override
public void notification(StoreEvent e) {
Log.i(Helper.TAG, account.name + " event: " + e.getMessage());
// Check connection
synchronized (state) {
state.notifyAll();
}
}
});
istore.addFolderListener(new FolderAdapter() {
@ -379,9 +376,8 @@ public class ServiceSynchronize extends LifecycleService {
public void opened(ConnectionEvent e) {
Log.i(Helper.TAG, account.name + " opened");
DB db = DB.getInstance(ServiceSynchronize.this);
account.error = null;
db.account().updateAccount(account);
db.account().setAccountState(account.id, "connected");
db.account().setAccountError(account.id, null);
try {
synchronizeFolders(account, fstore);
@ -397,24 +393,25 @@ public class ServiceSynchronize extends LifecycleService {
try {
Log.i(Helper.TAG, folder.name + " start");
db.folder().setFolderState(folder.id, "connecting");
ifolder = (IMAPFolder) fstore.getFolder(folder.name);
ifolder.open(Folder.READ_WRITE);
db.folder().setFolderState(folder.id, "connected");
db.folder().setFolderError(folder.id, null);
synchronized (mapFolder) {
mapFolder.put(folder.id, ifolder);
}
folder.error = null;
DB.getInstance(ServiceSynchronize.this).folder().updateFolder(folder);
monitorFolder(account, folder, fstore, ifolder, state);
} catch (Throwable ex) {
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
folder.error = Helper.formatThrowable(ex);
DB.getInstance(ServiceSynchronize.this).folder().updateFolder(folder);
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
// Cascade up
if (!(ex instanceof FolderNotFoundException))
@ -431,7 +428,10 @@ public class ServiceSynchronize extends LifecycleService {
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
}
}
Log.i(Helper.TAG, folder.name + " stop");
db.folder().setFolderState(folder.id, null);
Log.i(Helper.TAG, folder.name + " stopped");
}
}
}, "sync.folder." + folder.id).start();
@ -455,8 +455,7 @@ public class ServiceSynchronize extends LifecycleService {
Log.e(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, null, ex);
account.error = Helper.formatThrowable(ex);
db.account().updateAccount(account);
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
// Cascade up
try {
@ -471,6 +470,8 @@ public class ServiceSynchronize extends LifecycleService {
public void disconnected(ConnectionEvent e) {
Log.e(Helper.TAG, account.name + " disconnected");
db.account().setAccountState(account.id, null);
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(ServiceSynchronize.this);
lbm.unregisterReceiver(processReceiver);
@ -582,8 +583,7 @@ public class ServiceSynchronize extends LifecycleService {
Log.e(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, null, ex);
account.error = Helper.formatThrowable(ex);
DB.getInstance(this).account().updateAccount(account);
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
} finally {
if (istore != null) {
try {
@ -603,6 +603,8 @@ public class ServiceSynchronize extends LifecycleService {
}
}
db.account().setAccountState(account.id, null);
Log.i(Helper.TAG, account.name + " stopped");
}
@ -685,8 +687,7 @@ public class ServiceSynchronize extends LifecycleService {
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
folder.error = Helper.formatThrowable(ex);
DB.getInstance(ServiceSynchronize.this).folder().updateFolder(folder);
DB.getInstance(ServiceSynchronize.this).folder().setFolderError(folder.id, Helper.formatThrowable(ex));
// Cascade up
try {
@ -721,8 +722,7 @@ public class ServiceSynchronize extends LifecycleService {
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
folder.error = Helper.formatThrowable(ex);
DB.getInstance(ServiceSynchronize.this).folder().updateFolder(folder);
DB.getInstance(ServiceSynchronize.this).folder().setFolderError(folder.id, Helper.formatThrowable(ex));
// Cascade up
try {
@ -798,8 +798,7 @@ public class ServiceSynchronize extends LifecycleService {
// Operation succeeded
db.operation().deleteOperation(op.id);
} catch (Throwable ex) {
message.error = Helper.formatThrowable(ex);
db.message().updateMessage(message);
db.message().setMessageError(message.id, Helper.formatThrowable(ex));
if (BuildConfig.DEBUG && ex instanceof NullPointerException) {
db.operation().deleteOperation(op.id);
@ -843,8 +842,7 @@ public class ServiceSynchronize extends LifecycleService {
imessage.setFlag(Flags.Flag.SEEN, seen);
message.seen = seen;
db.message().updateMessage(message);
db.message().setMessageSeen(message.id, seen);
}
private void doAdd(EntityFolder folder, Session isession, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException, JSONException {
@ -863,8 +861,7 @@ public class ServiceSynchronize extends LifecycleService {
}
}
message.uid = uid[0].uid;
db.message().updateMessage(message);
db.message().setMessageUid(message.id, uid[0].uid);
Log.i(Helper.TAG, "Appended uid=" + message.uid);
}
@ -950,8 +947,7 @@ public class ServiceSynchronize extends LifecycleService {
" to " + TextUtils.join(", ", to));
} catch (SMTPSendFailedException ex) {
// TODO: response codes: https://www.ietf.org/rfc/rfc821.txt
message.error = Helper.formatThrowable(ex);
db.message().updateMessage(message);
db.message().setMessageError(message.id, Helper.formatThrowable(ex));
throw ex;
}
@ -1024,17 +1020,15 @@ public class ServiceSynchronize extends LifecycleService {
os.write(buffer, 0, len);
// Update progress
if (attachment.size != null) {
attachment.progress = size * 100 / attachment.size;
db.attachment().updateAttachment(attachment);
Log.i(Helper.TAG, folder.name + " progress %=" + attachment.progress);
}
if (attachment.size != null)
db.attachment().setProgress(attachment.id, size * 100 / attachment.size);
}
// Store attachment data
attachment.size = size;
attachment.progress = null;
attachment.filename = file.getName();
db.attachment().updateAttachment(attachment);
} finally {
try {
if (is != null)
@ -1044,7 +1038,6 @@ public class ServiceSynchronize extends LifecycleService {
os.close();
}
}
db.attachment().updateAttachment(attachment);
Log.i(Helper.TAG, folder.name + " downloaded bytes=" + attachment.size);
} catch (Throwable ex) {
// Reset progress on failure
@ -1350,6 +1343,7 @@ public class ServiceSynchronize extends LifecycleService {
} else
for (final EntityAccount account : accounts) {
Log.i(Helper.TAG, account.host + "/" + account.user + " run");
new Thread(new Runnable() {
@Override
public void run() {

View File

@ -29,7 +29,7 @@ public class TupleFolderEx extends EntityFolder {
if (obj instanceof TupleFolderEx) {
TupleFolderEx other = (TupleFolderEx) obj;
return (super.equals(obj) &&
this.accountName == null ? other.accountName == null : accountName.equals(other.accountName) &&
(this.accountName == null ? other.accountName == null : accountName.equals(other.accountName)) &&
this.messages == other.messages &&
this.unseen == other.unseen);
} else

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More