Show account/folder connectivity
13
FAQ.md
|
@ -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?**
|
||||
|
|
|
@ -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\")"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
After Width: | Height: | Size: 241 B |
After Width: | Height: | Size: 296 B |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 504 B |
After Width: | Height: | Size: 373 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 642 B |
After Width: | Height: | Size: 818 B |
After Width: | Height: | Size: 375 B |
After Width: | Height: | Size: 456 B |
After Width: | Height: | Size: 643 B |
After Width: | Height: | Size: 807 B |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 595 B |
After Width: | Height: | Size: 760 B |
After Width: | Height: | Size: 339 B |
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 588 B |
After Width: | Height: | Size: 749 B |
After Width: | Height: | Size: 241 B |
After Width: | Height: | Size: 293 B |
After Width: | Height: | Size: 385 B |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 208 B |
After Width: | Height: | Size: 296 B |
After Width: | Height: | Size: 356 B |
After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 305 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 567 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 312 B |
After Width: | Height: | Size: 456 B |
After Width: | Height: | Size: 563 B |
After Width: | Height: | Size: 228 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 522 B |
After Width: | Height: | Size: 233 B |
After Width: | Height: | Size: 275 B |
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 512 B |
After Width: | Height: | Size: 186 B |
After Width: | Height: | Size: 215 B |
After Width: | Height: | Size: 293 B |
After Width: | Height: | Size: 358 B |
After Width: | Height: | Size: 296 B |
After Width: | Height: | Size: 356 B |
After Width: | Height: | Size: 504 B |
After Width: | Height: | Size: 646 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 567 B |
After Width: | Height: | Size: 818 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 456 B |
After Width: | Height: | Size: 563 B |
After Width: | Height: | Size: 807 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 522 B |
After Width: | Height: | Size: 760 B |
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 396 B |
After Width: | Height: | Size: 512 B |
After Width: | Height: | Size: 749 B |
After Width: | Height: | Size: 982 B |
After Width: | Height: | Size: 293 B |
After Width: | Height: | Size: 358 B |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 642 B |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 504 B |
After Width: | Height: | Size: 714 B |
After Width: | Height: | Size: 973 B |
After Width: | Height: | Size: 642 B |
After Width: | Height: | Size: 818 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 643 B |
After Width: | Height: | Size: 807 B |
After Width: | Height: | Size: 1.1 KiB |