Merge pull request #2715 from k9mail/GH-2714_fix_ActivityListener_crash

Fix ActivityListener crash due to concurrent modification
This commit is contained in:
cketti 2017-09-01 01:11:52 +02:00 committed by GitHub
commit 66b5154b7d
2 changed files with 154 additions and 114 deletions

View file

@ -35,6 +35,7 @@ dependencies {
compile 'com.github.amlcurran.showcaseview:library:5.4.1' compile 'com.github.amlcurran.showcaseview:library:5.4.1'
compile 'com.squareup.moshi:moshi:1.2.0' compile 'com.squareup.moshi:moshi:1.2.0'
compile "com.jakewharton.timber:timber:${timberVersion}" compile "com.jakewharton.timber:timber:${timberVersion}"
compile 'net.jcip:jcip-annotations:1.0'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'

View file

@ -1,5 +1,6 @@
package com.fsck.k9.activity; package com.fsck.k9.activity;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -12,39 +13,45 @@ import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.controller.SimpleMessagingListener; import com.fsck.k9.controller.SimpleMessagingListener;
import com.fsck.k9.service.MailService; import com.fsck.k9.service.MailService;
import net.jcip.annotations.GuardedBy;
public class ActivityListener extends SimpleMessagingListener { public class ActivityListener extends SimpleMessagingListener {
private Account mAccount = null; private final Object lock = new Object();
private String mLoadingFolderName = null;
private String mLoadingHeaderFolderName = null;
private String mLoadingAccountDescription = null;
private String mSendingAccountDescription = null;
private int mFolderCompleted = 0;
private int mFolderTotal = 0;
private String mProcessingAccountDescription = null;
private String mProcessingCommandTitle = null;
private BroadcastReceiver mTickReceiver = new BroadcastReceiver() { @GuardedBy("lock") private Account account = null;
@GuardedBy("lock") private String loadingFolderName = null;
@GuardedBy("lock") private String loadingHeaderFolderName = null;
@GuardedBy("lock") private String loadingAccountDescription = null;
@GuardedBy("lock") private String sendingAccountDescription = null;
@GuardedBy("lock") private int folderCompleted = 0;
@GuardedBy("lock") private int folderTotal = 0;
@GuardedBy("lock") private String processingAccountDescription = null;
@GuardedBy("lock") private String processingCommandTitle = null;
private BroadcastReceiver tickReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
informUserOfStatus(); informUserOfStatus();
} }
}; };
public String getOperation(Context context) { public String getOperation(Context context) {
if (mLoadingAccountDescription != null synchronized (lock) {
|| mSendingAccountDescription != null if (loadingAccountDescription != null ||
|| mLoadingHeaderFolderName != null sendingAccountDescription != null ||
|| mProcessingAccountDescription != null) { loadingHeaderFolderName != null ||
processingAccountDescription != null) {
return getActionInProgressOperation(context); return getActionInProgressOperation(context);
}
}
} else {
long nextPollTime = MailService.getNextPollTime(); long nextPollTime = MailService.getNextPollTime();
if (nextPollTime != -1) { if (nextPollTime != -1) {
return context.getString(R.string.status_next_poll, CharSequence relativeTimeSpanString = DateUtils.getRelativeTimeSpanString(
DateUtils.getRelativeTimeSpanString(nextPollTime, System.currentTimeMillis(), nextPollTime, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, 0);
DateUtils.MINUTE_IN_MILLIS, 0)); return context.getString(R.string.status_next_poll, relativeTimeSpanString);
} else if (K9.isDebug() && MailService.isSyncDisabled()) { } else if (K9.isDebug() && MailService.isSyncDisabled()) {
if (MailService.hasNoConnectivity()) { if (MailService.hasNoConnectivity()) {
return context.getString(R.string.status_no_network); return context.getString(R.string.status_no_network);
@ -63,165 +70,195 @@ public class ActivityListener extends SimpleMessagingListener {
return ""; return "";
} }
} }
}
@GuardedBy("lock")
private String getActionInProgressOperation(Context context) { private String getActionInProgressOperation(Context context) {
String progress = (mFolderTotal > 0 ? String progress = folderTotal > 0 ?
context.getString(R.string.folder_progress, mFolderCompleted, mFolderTotal) : ""); context.getString(R.string.folder_progress, folderCompleted, folderTotal) : "";
if (mLoadingFolderName != null || mLoadingHeaderFolderName != null) { if (loadingFolderName != null || loadingHeaderFolderName != null) {
String displayName = null; String displayName;
if (mLoadingHeaderFolderName != null) { if (loadingHeaderFolderName != null) {
displayName = mLoadingHeaderFolderName; displayName = loadingHeaderFolderName;
} else if (mLoadingFolderName != null) { } else {
displayName = mLoadingFolderName; displayName = loadingFolderName;
} }
if ((mAccount != null) && (mAccount.getInboxFolderName() != null)
&& mAccount.getInboxFolderName().equalsIgnoreCase(displayName)) { if (account != null) {
if (displayName.equalsIgnoreCase(account.getInboxFolderName())) {
displayName = context.getString(R.string.special_mailbox_name_inbox); displayName = context.getString(R.string.special_mailbox_name_inbox);
} else if ((mAccount != null) && (mAccount.getOutboxFolderName() != null) } else if (displayName.equalsIgnoreCase(account.getOutboxFolderName())) {
&& mAccount.getOutboxFolderName().equals(displayName)) {
displayName = context.getString(R.string.special_mailbox_name_outbox); displayName = context.getString(R.string.special_mailbox_name_outbox);
} }
}
if (mLoadingHeaderFolderName != null) { if (loadingHeaderFolderName != null) {
return context.getString(R.string.status_loading_account_folder_headers, return context.getString(R.string.status_loading_account_folder_headers,
mLoadingAccountDescription, displayName, progress); loadingAccountDescription, displayName, progress);
} else { } else {
return context.getString(R.string.status_loading_account_folder, return context.getString(R.string.status_loading_account_folder,
mLoadingAccountDescription, displayName, progress); loadingAccountDescription, displayName, progress);
} }
} } else if (sendingAccountDescription != null) {
return context.getString(R.string.status_sending_account, sendingAccountDescription, progress);
else if (mSendingAccountDescription != null) { } else if (processingAccountDescription != null) {
return context.getString(R.string.status_sending_account, mSendingAccountDescription, progress); return context.getString(R.string.status_processing_account, processingAccountDescription,
} else if (mProcessingAccountDescription != null) { processingCommandTitle != null ? processingCommandTitle : "", progress);
return context.getString(R.string.status_processing_account, mProcessingAccountDescription,
mProcessingCommandTitle != null ? mProcessingCommandTitle : "",
progress);
} else { } else {
return ""; return "";
} }
} }
public void onResume(Context context) { public void onResume(Context context) {
context.registerReceiver(mTickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); context.registerReceiver(tickReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
} }
public void onPause(Context context) { public void onPause(Context context) {
context.unregisterReceiver(mTickReceiver); context.unregisterReceiver(tickReceiver);
} }
public void informUserOfStatus() { public void informUserOfStatus() {
} }
@Override @Override
public void synchronizeMailboxFinished( public void synchronizeMailboxFinished(Account account, String folder, int totalMessagesInMailbox,
Account account,
String folder,
int totalMessagesInMailbox,
int numNewMessages) { int numNewMessages) {
mLoadingAccountDescription = null; synchronized (lock) {
mLoadingFolderName = null; loadingAccountDescription = null;
mAccount = null; loadingFolderName = null;
this.account = null;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void synchronizeMailboxStarted(Account account, String folder) { public void synchronizeMailboxStarted(Account account, String folder) {
mLoadingAccountDescription = account.getDescription(); synchronized (lock) {
mLoadingFolderName = folder; loadingAccountDescription = account.getDescription();
mAccount = account; loadingFolderName = folder;
mFolderCompleted = 0; this.account = account;
mFolderTotal = 0; folderCompleted = 0;
informUserOfStatus(); folderTotal = 0;
} }
informUserOfStatus();
}
@Override @Override
public void synchronizeMailboxHeadersStarted(Account account, String folder) { public void synchronizeMailboxHeadersStarted(Account account, String folder) {
mLoadingAccountDescription = account.getDescription(); synchronized (lock) {
mLoadingHeaderFolderName = folder; loadingAccountDescription = account.getDescription();
informUserOfStatus(); loadingHeaderFolderName = folder;
} }
informUserOfStatus();
}
@Override @Override
public void synchronizeMailboxHeadersProgress(Account account, String folder, int completed, int total) { public void synchronizeMailboxHeadersProgress(Account account, String folder, int completed, int total) {
mFolderCompleted = completed; synchronized (lock) {
mFolderTotal = total; folderCompleted = completed;
folderTotal = total;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void synchronizeMailboxHeadersFinished(Account account, String folder, public void synchronizeMailboxHeadersFinished(Account account, String folder, int total, int completed) {
int total, int completed) { synchronized (lock) {
mLoadingHeaderFolderName = null; loadingHeaderFolderName = null;
mFolderCompleted = 0; folderCompleted = 0;
mFolderTotal = 0; folderTotal = 0;
informUserOfStatus();
} }
informUserOfStatus();
}
@Override @Override
public void synchronizeMailboxProgress(Account account, String folder, int completed, int total) { public void synchronizeMailboxProgress(Account account, String folder, int completed, int total) {
mFolderCompleted = completed; synchronized (lock) {
mFolderTotal = total; folderCompleted = completed;
folderTotal = total;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void synchronizeMailboxFailed(Account account, String folder, public void synchronizeMailboxFailed(Account account, String folder, String message) {
String message) { synchronized (lock) {
mLoadingAccountDescription = null; loadingAccountDescription = null;
mLoadingHeaderFolderName = null; loadingHeaderFolderName = null;
mLoadingFolderName = null; loadingFolderName = null;
mAccount = null; this.account = null;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void sendPendingMessagesStarted(Account account) { public void sendPendingMessagesStarted(Account account) {
mSendingAccountDescription = account.getDescription(); synchronized (lock) {
sendingAccountDescription = account.getDescription();
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void sendPendingMessagesCompleted(Account account) { public void sendPendingMessagesCompleted(Account account) {
mSendingAccountDescription = null; synchronized (lock) {
sendingAccountDescription = null;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void sendPendingMessagesFailed(Account account) { public void sendPendingMessagesFailed(Account account) {
mSendingAccountDescription = null; synchronized (lock) {
sendingAccountDescription = null;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void pendingCommandsProcessing(Account account) { public void pendingCommandsProcessing(Account account) {
mProcessingAccountDescription = account.getDescription(); synchronized (lock) {
mFolderCompleted = 0; processingAccountDescription = account.getDescription();
mFolderTotal = 0; folderCompleted = 0;
folderTotal = 0;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void pendingCommandsFinished(Account account) { public void pendingCommandsFinished(Account account) {
mProcessingAccountDescription = null; synchronized (lock) {
processingAccountDescription = null;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void pendingCommandStarted(Account account, String commandTitle) { public void pendingCommandStarted(Account account, String commandTitle) {
mProcessingCommandTitle = commandTitle; synchronized (lock) {
processingCommandTitle = commandTitle;
}
informUserOfStatus(); informUserOfStatus();
} }
@Override @Override
public void pendingCommandCompleted(Account account, String commandTitle) { public void pendingCommandCompleted(Account account, String commandTitle) {
mProcessingCommandTitle = null; synchronized (lock) {
processingCommandTitle = null;
}
informUserOfStatus(); informUserOfStatus();
} }
@ -241,12 +278,14 @@ public class ActivityListener extends SimpleMessagingListener {
} }
public int getFolderCompleted() { public int getFolderCompleted() {
return mFolderCompleted; synchronized (lock) {
return folderCompleted;
}
} }
public int getFolderTotal() { public int getFolderTotal() {
return mFolderTotal; synchronized (lock) {
return folderTotal;
}
} }
} }