Merge pull request #2186 from philipwhiuk/json-pending-command-rebase
Use json for serializing pending commands
This commit is contained in:
commit
da2012be1d
13 changed files with 1033 additions and 414 deletions
|
@ -33,6 +33,7 @@ dependencies {
|
||||||
compile 'com.splitwise:tokenautocomplete:2.0.7'
|
compile 'com.splitwise:tokenautocomplete:2.0.7'
|
||||||
compile 'de.cketti.safecontentresolver:safe-content-resolver-v14:0.9.0'
|
compile 'de.cketti.safecontentresolver:safe-content-resolver-v14:0.9.0'
|
||||||
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'
|
||||||
|
|
||||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
|
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,6 @@ import com.fsck.k9.Account;
|
||||||
class MemorizingMessagingListener extends MessagingListener {
|
class MemorizingMessagingListener extends MessagingListener {
|
||||||
Map<String, Memory> memories = new HashMap<>(31);
|
Map<String, Memory> memories = new HashMap<>(31);
|
||||||
|
|
||||||
private Memory getMemory(Account account, String folderName) {
|
|
||||||
Memory memory = memories.get(getMemoryKey(account, folderName));
|
|
||||||
if (memory == null) {
|
|
||||||
memory = new Memory(account, folderName);
|
|
||||||
memories.put(getMemoryKey(memory.account, memory.folderName), memory);
|
|
||||||
}
|
|
||||||
return memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void removeAccount(Account account) {
|
synchronized void removeAccount(Account account) {
|
||||||
Iterator<Entry<String, Memory>> memIt = memories.entrySet().iterator();
|
Iterator<Entry<String, Memory>> memIt = memories.entrySet().iterator();
|
||||||
|
|
||||||
|
@ -35,32 +26,6 @@ class MemorizingMessagingListener extends MessagingListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void synchronizeMailboxStarted(Account account, String folder) {
|
|
||||||
Memory memory = getMemory(account, folder);
|
|
||||||
memory.syncingState = MemorizingState.STARTED;
|
|
||||||
memory.folderCompleted = 0;
|
|
||||||
memory.folderTotal = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void synchronizeMailboxFinished(Account account, String folder,
|
|
||||||
int totalMessagesInMailbox, int numNewMessages) {
|
|
||||||
Memory memory = getMemory(account, folder);
|
|
||||||
memory.syncingState = MemorizingState.FINISHED;
|
|
||||||
memory.syncingTotalMessagesInMailbox = totalMessagesInMailbox;
|
|
||||||
memory.syncingNumNewMessages = numNewMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void synchronizeMailboxFailed(Account account, String folder,
|
|
||||||
String message) {
|
|
||||||
|
|
||||||
Memory memory = getMemory(account, folder);
|
|
||||||
memory.syncingState = MemorizingState.FAILED;
|
|
||||||
memory.failureMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void refreshOther(MessagingListener other) {
|
synchronized void refreshOther(MessagingListener other) {
|
||||||
if (other != null) {
|
if (other != null) {
|
||||||
|
|
||||||
|
@ -151,6 +116,32 @@ class MemorizingMessagingListener extends MessagingListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void synchronizeMailboxStarted(Account account, String folder) {
|
||||||
|
Memory memory = getMemory(account, folder);
|
||||||
|
memory.syncingState = MemorizingState.STARTED;
|
||||||
|
memory.folderCompleted = 0;
|
||||||
|
memory.folderTotal = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void synchronizeMailboxFinished(Account account, String folder,
|
||||||
|
int totalMessagesInMailbox, int numNewMessages) {
|
||||||
|
Memory memory = getMemory(account, folder);
|
||||||
|
memory.syncingState = MemorizingState.FINISHED;
|
||||||
|
memory.syncingTotalMessagesInMailbox = totalMessagesInMailbox;
|
||||||
|
memory.syncingNumNewMessages = numNewMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void synchronizeMailboxFailed(Account account, String folder,
|
||||||
|
String message) {
|
||||||
|
|
||||||
|
Memory memory = getMemory(account, folder);
|
||||||
|
memory.syncingState = MemorizingState.FAILED;
|
||||||
|
memory.failureMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setPushActive(Account account, String folderName, boolean active) {
|
public synchronized void setPushActive(Account account, String folderName, boolean active) {
|
||||||
Memory memory = getMemory(account, folderName);
|
Memory memory = getMemory(account, folderName);
|
||||||
|
@ -213,6 +204,15 @@ class MemorizingMessagingListener extends MessagingListener {
|
||||||
memory.processingCommandTitle = null;
|
memory.processingCommandTitle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Memory getMemory(Account account, String folderName) {
|
||||||
|
Memory memory = memories.get(getMemoryKey(account, folderName));
|
||||||
|
if (memory == null) {
|
||||||
|
memory = new Memory(account, folderName);
|
||||||
|
memories.put(getMemoryKey(memory.account, memory.folderName), memory);
|
||||||
|
}
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
private static String getMemoryKey(Account taccount, String tfolderName) {
|
private static String getMemoryKey(Account taccount, String tfolderName) {
|
||||||
return taccount.getDescription() + ":" + tfolderName;
|
return taccount.getDescription() + ":" + tfolderName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,13 @@ import com.fsck.k9.R;
|
||||||
import com.fsck.k9.activity.MessageReference;
|
import com.fsck.k9.activity.MessageReference;
|
||||||
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||||
import com.fsck.k9.cache.EmailProviderCache;
|
import com.fsck.k9.cache.EmailProviderCache;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
||||||
import com.fsck.k9.helper.Contacts;
|
import com.fsck.k9.helper.Contacts;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.AuthenticationFailedException;
|
import com.fsck.k9.mail.AuthenticationFailedException;
|
||||||
|
@ -86,7 +93,6 @@ import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mailstore.LocalFolder.MoreMessages;
|
import com.fsck.k9.mailstore.LocalFolder.MoreMessages;
|
||||||
import com.fsck.k9.mailstore.LocalMessage;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.mailstore.LocalStore;
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.mailstore.LocalStore.PendingCommand;
|
|
||||||
import com.fsck.k9.mailstore.MessageRemovalListener;
|
import com.fsck.k9.mailstore.MessageRemovalListener;
|
||||||
import com.fsck.k9.mailstore.UnavailableStorageException;
|
import com.fsck.k9.mailstore.UnavailableStorageException;
|
||||||
import com.fsck.k9.notification.NotificationController;
|
import com.fsck.k9.notification.NotificationController;
|
||||||
|
@ -115,19 +121,8 @@ import com.fsck.k9.search.SqlQueryBuilder;
|
||||||
public class MessagingController {
|
public class MessagingController {
|
||||||
public static final long INVALID_MESSAGE_ID = -1;
|
public static final long INVALID_MESSAGE_ID = -1;
|
||||||
|
|
||||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|
||||||
private static final Set<Flag> SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED);
|
private static final Set<Flag> SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED);
|
||||||
|
|
||||||
private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy";
|
|
||||||
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk";
|
|
||||||
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew";
|
|
||||||
private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash";
|
|
||||||
private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk";
|
|
||||||
private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag";
|
|
||||||
private static final String PENDING_COMMAND_APPEND = "com.fsck.k9.MessagingController.append";
|
|
||||||
private static final String PENDING_COMMAND_MARK_ALL_AS_READ = "com.fsck.k9.MessagingController.markAllAsRead";
|
|
||||||
private static final String PENDING_COMMAND_EXPUNGE = "com.fsck.k9.MessagingController.expunge";
|
|
||||||
|
|
||||||
|
|
||||||
private static MessagingController inst = null;
|
private static MessagingController inst = null;
|
||||||
|
|
||||||
|
@ -1729,13 +1724,11 @@ public class MessagingController {
|
||||||
try {
|
try {
|
||||||
for (PendingCommand command : commands) {
|
for (PendingCommand command : commands) {
|
||||||
processingCommand = command;
|
processingCommand = command;
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "Processing pending command '" + command + "'");
|
Log.d(K9.LOG_TAG, "Processing pending command '" + command + "'");
|
||||||
|
}
|
||||||
String[] components = command.command.split("\\.");
|
|
||||||
String commandTitle = components[components.length - 1];
|
|
||||||
for (MessagingListener l : getListeners()) {
|
for (MessagingListener l : getListeners()) {
|
||||||
l.pendingCommandStarted(account, commandTitle);
|
l.pendingCommandStarted(account, command.getCommandName());
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* We specifically do not catch any exceptions here. If a command fails it is
|
* We specifically do not catch any exceptions here. If a command fails it is
|
||||||
|
@ -1743,28 +1736,12 @@ public class MessagingController {
|
||||||
* other command processes. This maintains the order of the commands.
|
* other command processes. This maintains the order of the commands.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
if (PENDING_COMMAND_APPEND.equals(command.command)) {
|
command.execute(this, account);
|
||||||
processPendingAppend(command, account);
|
|
||||||
} else if (PENDING_COMMAND_SET_FLAG_BULK.equals(command.command)) {
|
|
||||||
processPendingSetFlag(command, account);
|
|
||||||
} else if (PENDING_COMMAND_SET_FLAG.equals(command.command)) {
|
|
||||||
processPendingSetFlagOld(command, account);
|
|
||||||
} else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) {
|
|
||||||
processPendingMarkAllAsRead(command, account);
|
|
||||||
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK.equals(command.command)) {
|
|
||||||
processPendingMoveOrCopyOld2(command, account);
|
|
||||||
} else if (PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW.equals(command.command)) {
|
|
||||||
processPendingMoveOrCopy(command, account);
|
|
||||||
} else if (PENDING_COMMAND_MOVE_OR_COPY.equals(command.command)) {
|
|
||||||
processPendingMoveOrCopyOld(command, account);
|
|
||||||
} else if (PENDING_COMMAND_EMPTY_TRASH.equals(command.command)) {
|
|
||||||
processPendingEmptyTrash(command, account);
|
|
||||||
} else if (PENDING_COMMAND_EXPUNGE.equals(command.command)) {
|
|
||||||
processPendingExpunge(command, account);
|
|
||||||
}
|
|
||||||
localStore.removePendingCommand(command);
|
localStore.removePendingCommand(command);
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "Done processing pending command '" + command + "'");
|
Log.d(K9.LOG_TAG, "Done processing pending command '" + command + "'");
|
||||||
|
}
|
||||||
} catch (MessagingException me) {
|
} catch (MessagingException me) {
|
||||||
if (me.isPermanentFailure()) {
|
if (me.isPermanentFailure()) {
|
||||||
addErrorMessage(account, null, me);
|
addErrorMessage(account, null, me);
|
||||||
|
@ -1777,7 +1754,7 @@ public class MessagingController {
|
||||||
progress++;
|
progress++;
|
||||||
for (MessagingListener l : getListeners()) {
|
for (MessagingListener l : getListeners()) {
|
||||||
l.synchronizeMailboxProgress(account, null, progress, todo);
|
l.synchronizeMailboxProgress(account, null, progress, todo);
|
||||||
l.pendingCommandCompleted(account, commandTitle);
|
l.pendingCommandCompleted(account, command.getCommandName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1801,13 +1778,13 @@ public class MessagingController {
|
||||||
* created.
|
* created.
|
||||||
* TODO update the local message UID instead of deleteing it
|
* TODO update the local message UID instead of deleteing it
|
||||||
*/
|
*/
|
||||||
private void processPendingAppend(PendingCommand command, Account account) throws MessagingException {
|
void processPendingAppend(PendingAppend command, Account account) throws MessagingException {
|
||||||
Folder remoteFolder = null;
|
Folder remoteFolder = null;
|
||||||
LocalFolder localFolder = null;
|
LocalFolder localFolder = null;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
String folder = command.arguments[0];
|
String folder = command.folder;
|
||||||
String uid = command.arguments[1];
|
String uid = command.uid;
|
||||||
|
|
||||||
if (account.getErrorFolderName().equals(folder)) {
|
if (account.getErrorFolderName().equals(folder)) {
|
||||||
return;
|
return;
|
||||||
|
@ -1923,97 +1900,38 @@ public class MessagingController {
|
||||||
closeFolder(localFolder);
|
closeFolder(localFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, boolean isCopy, String uids[]) {
|
private void queueMoveOrCopy(Account account, String srcFolder, String destFolder, boolean isCopy, List<String> uids) {
|
||||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uids);
|
||||||
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
|
||||||
|
|
||||||
int length = 4 + uids.length;
|
|
||||||
command.arguments = new String[length];
|
|
||||||
command.arguments[0] = srcFolder;
|
|
||||||
command.arguments[1] = destFolder;
|
|
||||||
command.arguments[2] = Boolean.toString(isCopy);
|
|
||||||
command.arguments[3] = Boolean.toString(false);
|
|
||||||
System.arraycopy(uids, 0, command.arguments, 4, uids.length);
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueMoveOrCopy(Account account, String srcFolder, String destFolder,
|
private void queueMoveOrCopy(Account account, String srcFolder, String destFolder,
|
||||||
boolean isCopy, String uids[], Map<String, String> uidMap) {
|
boolean isCopy, List<String> uids, Map<String, String> uidMap) {
|
||||||
if (uidMap == null || uidMap.isEmpty()) {
|
if (uidMap == null || uidMap.isEmpty()) {
|
||||||
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, uids);
|
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, uids);
|
||||||
} else {
|
} else {
|
||||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uidMap);
|
||||||
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
|
||||||
|
|
||||||
int length = 4 + uidMap.keySet().size() + uidMap.values().size();
|
|
||||||
command.arguments = new String[length];
|
|
||||||
command.arguments[0] = srcFolder;
|
|
||||||
command.arguments[1] = destFolder;
|
|
||||||
command.arguments[2] = Boolean.toString(isCopy);
|
|
||||||
command.arguments[3] = Boolean.toString(true);
|
|
||||||
System.arraycopy(uidMap.keySet().toArray(EMPTY_STRING_ARRAY), 0, command.arguments, 4, uidMap.keySet().size());
|
|
||||||
System.arraycopy(uidMap.values().toArray(EMPTY_STRING_ARRAY), 0, command.arguments, 4 + uidMap.keySet().size(), uidMap.values().size());
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void processPendingMoveOrCopy(PendingMoveOrCopy command, Account account) throws MessagingException {
|
||||||
* Convert pending command to new format and call
|
|
||||||
* {@link #processPendingMoveOrCopy(PendingCommand, Account)}.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* TODO: This method is obsolete and is only for transition from K-9 4.0 to K-9 4.2
|
|
||||||
* Eventually, it should be removed.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param command
|
|
||||||
* Pending move/copy command in old format.
|
|
||||||
* @param account
|
|
||||||
* The account the pending command belongs to.
|
|
||||||
*
|
|
||||||
* @throws MessagingException
|
|
||||||
* In case of an error.
|
|
||||||
*/
|
|
||||||
private void processPendingMoveOrCopyOld2(PendingCommand command, Account account) throws MessagingException {
|
|
||||||
PendingCommand newCommand = new PendingCommand();
|
|
||||||
int len = command.arguments.length;
|
|
||||||
newCommand.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
|
||||||
newCommand.arguments = new String[len + 1];
|
|
||||||
newCommand.arguments[0] = command.arguments[0];
|
|
||||||
newCommand.arguments[1] = command.arguments[1];
|
|
||||||
newCommand.arguments[2] = command.arguments[2];
|
|
||||||
newCommand.arguments[3] = Boolean.toString(false);
|
|
||||||
System.arraycopy(command.arguments, 3, newCommand.arguments, 4, len - 3);
|
|
||||||
|
|
||||||
processPendingMoveOrCopy(newCommand, account);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a pending trash message command.
|
|
||||||
*/
|
|
||||||
private void processPendingMoveOrCopy(PendingCommand command, Account account) throws MessagingException {
|
|
||||||
Folder remoteSrcFolder = null;
|
Folder remoteSrcFolder = null;
|
||||||
Folder remoteDestFolder = null;
|
Folder remoteDestFolder = null;
|
||||||
LocalFolder localDestFolder;
|
LocalFolder localDestFolder;
|
||||||
try {
|
try {
|
||||||
String srcFolder = command.arguments[0];
|
String srcFolder = command.srcFolder;
|
||||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
if (account.getErrorFolderName().equals(srcFolder)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String destFolder = command.arguments[1];
|
String destFolder = command.destFolder;
|
||||||
String isCopyS = command.arguments[2];
|
boolean isCopy = command.isCopy;
|
||||||
String hasNewUidsS = command.arguments[3];
|
|
||||||
|
|
||||||
boolean hasNewUids = false;
|
|
||||||
if (hasNewUidsS != null) {
|
|
||||||
hasNewUids = Boolean.parseBoolean(hasNewUidsS);
|
|
||||||
}
|
|
||||||
|
|
||||||
Store remoteStore = account.getRemoteStore();
|
Store remoteStore = account.getRemoteStore();
|
||||||
remoteSrcFolder = remoteStore.getFolder(srcFolder);
|
remoteSrcFolder = remoteStore.getFolder(srcFolder);
|
||||||
|
@ -2022,34 +1940,11 @@ public class MessagingController {
|
||||||
localDestFolder = (LocalFolder) localStore.getFolder(destFolder);
|
localDestFolder = (LocalFolder) localStore.getFolder(destFolder);
|
||||||
List<Message> messages = new ArrayList<>();
|
List<Message> messages = new ArrayList<>();
|
||||||
|
|
||||||
/*
|
Collection<String> uids = command.newUidMap != null ? command.newUidMap.keySet() : command.uids;
|
||||||
* We split up the localUidMap into two parts while sending the command, here we assemble it back.
|
for (String uid : uids) {
|
||||||
*/
|
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||||
Map<String, String> localUidMap = new HashMap<>();
|
messages.add(remoteSrcFolder.getMessage(uid));
|
||||||
if (hasNewUids) {
|
|
||||||
int offset = (command.arguments.length - 4) / 2;
|
|
||||||
|
|
||||||
for (int i = 4; i < 4 + offset; i++) {
|
|
||||||
localUidMap.put(command.arguments[i], command.arguments[i + offset]);
|
|
||||||
|
|
||||||
String uid = command.arguments[i];
|
|
||||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
|
||||||
messages.add(remoteSrcFolder.getMessage(uid));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
for (int i = 4; i < command.arguments.length; i++) {
|
|
||||||
String uid = command.arguments[i];
|
|
||||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
|
||||||
messages.add(remoteSrcFolder.getMessage(uid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isCopy = false;
|
|
||||||
if (isCopyS != null) {
|
|
||||||
isCopy = Boolean.parseBoolean(isCopyS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!remoteSrcFolder.exists()) {
|
if (!remoteSrcFolder.exists()) {
|
||||||
|
@ -2095,11 +1990,14 @@ public class MessagingController {
|
||||||
* This next part is used to bring the local UIDs of the local destination folder
|
* This next part is used to bring the local UIDs of the local destination folder
|
||||||
* upto speed with the remote UIDs of remote destination folder.
|
* upto speed with the remote UIDs of remote destination folder.
|
||||||
*/
|
*/
|
||||||
if (!localUidMap.isEmpty() && remoteUidMap != null && !remoteUidMap.isEmpty()) {
|
if (command.newUidMap != null && remoteUidMap != null && !remoteUidMap.isEmpty()) {
|
||||||
for (Map.Entry<String, String> entry : remoteUidMap.entrySet()) {
|
for (Map.Entry<String, String> entry : remoteUidMap.entrySet()) {
|
||||||
String remoteSrcUid = entry.getKey();
|
String remoteSrcUid = entry.getKey();
|
||||||
String localDestUid = localUidMap.get(remoteSrcUid);
|
|
||||||
String newUid = entry.getValue();
|
String newUid = entry.getValue();
|
||||||
|
String localDestUid = command.newUidMap.get(remoteSrcUid);
|
||||||
|
if (localDestUid == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Message localDestMessage = localDestFolder.getMessage(localDestUid);
|
Message localDestMessage = localDestFolder.getMessage(localDestUid);
|
||||||
if (localDestMessage != null) {
|
if (localDestMessage != null) {
|
||||||
|
@ -2118,18 +2016,11 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueSetFlag(final Account account, final String folderName,
|
private void queueSetFlag(final Account account, final String folderName,
|
||||||
final String newState, final String flag, final String[] uids) {
|
final boolean newState, final Flag flag, final List<String> uids) {
|
||||||
putBackground("queueSetFlag " + account.getDescription() + ":" + folderName, null, new Runnable() {
|
putBackground("queueSetFlag " + account.getDescription() + ":" + folderName, null, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingSetFlag.create(folderName, newState, flag, uids);
|
||||||
command.command = PENDING_COMMAND_SET_FLAG_BULK;
|
|
||||||
int length = 3 + uids.length;
|
|
||||||
command.arguments = new String[length];
|
|
||||||
command.arguments[0] = folderName;
|
|
||||||
command.arguments[1] = newState;
|
|
||||||
command.arguments[2] = flag;
|
|
||||||
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
|
@ -2138,16 +2029,15 @@ public class MessagingController {
|
||||||
/**
|
/**
|
||||||
* Processes a pending mark read or unread command.
|
* Processes a pending mark read or unread command.
|
||||||
*/
|
*/
|
||||||
private void processPendingSetFlag(PendingCommand command, Account account) throws MessagingException {
|
void processPendingSetFlag(PendingSetFlag command, Account account) throws MessagingException {
|
||||||
String folder = command.arguments[0];
|
String folder = command.folder;
|
||||||
|
|
||||||
if (account.getErrorFolderName().equals(folder) || account.getOutboxFolderName().equals(folder)) {
|
if (account.getErrorFolderName().equals(folder) || account.getOutboxFolderName().equals(folder)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean newState = Boolean.parseBoolean(command.arguments[1]);
|
boolean newState = command.newState;
|
||||||
|
Flag flag = command.flag;
|
||||||
Flag flag = Flag.valueOf(command.arguments[2]);
|
|
||||||
|
|
||||||
Store remoteStore = account.getRemoteStore();
|
Store remoteStore = account.getRemoteStore();
|
||||||
Folder remoteFolder = remoteStore.getFolder(folder);
|
Folder remoteFolder = remoteStore.getFolder(folder);
|
||||||
|
@ -2161,8 +2051,7 @@ public class MessagingController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<Message> messages = new ArrayList<>();
|
List<Message> messages = new ArrayList<>();
|
||||||
for (int i = 3; i < command.arguments.length; i++) {
|
for (String uid : command.uids) {
|
||||||
String uid = command.arguments[i];
|
|
||||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
||||||
messages.add(remoteFolder.getMessage(uid));
|
messages.add(remoteFolder.getMessage(uid));
|
||||||
}
|
}
|
||||||
|
@ -2177,61 +2066,18 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This method is obsolete and is only for transition from K-9 2.0 to K-9 2.1
|
|
||||||
// Eventually, it should be removed
|
|
||||||
private void processPendingSetFlagOld(PendingCommand command, Account account) throws MessagingException {
|
|
||||||
String folder = command.arguments[0];
|
|
||||||
String uid = command.arguments[1];
|
|
||||||
|
|
||||||
if (account.getErrorFolderName().equals(folder) || account.getOutboxFolderName().equals(folder)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (K9.DEBUG)
|
|
||||||
Log.d(K9.LOG_TAG, "processPendingSetFlagOld: folder = " + folder + ", uid = " + uid);
|
|
||||||
|
|
||||||
boolean newState = Boolean.parseBoolean(command.arguments[2]);
|
|
||||||
|
|
||||||
Flag flag = Flag.valueOf(command.arguments[3]);
|
|
||||||
Folder remoteFolder = null;
|
|
||||||
try {
|
|
||||||
Store remoteStore = account.getRemoteStore();
|
|
||||||
remoteFolder = remoteStore.getFolder(folder);
|
|
||||||
if (!remoteFolder.exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
remoteFolder.open(Folder.OPEN_MODE_RW);
|
|
||||||
if (remoteFolder.getMode() != Folder.OPEN_MODE_RW) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Message remoteMessage = null;
|
|
||||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
|
||||||
remoteMessage = remoteFolder.getMessage(uid);
|
|
||||||
}
|
|
||||||
if (remoteMessage == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
remoteMessage.setFlag(flag, newState);
|
|
||||||
} finally {
|
|
||||||
closeFolder(remoteFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void queueExpunge(final Account account, final String folderName) {
|
private void queueExpunge(final Account account, final String folderName) {
|
||||||
putBackground("queueExpunge " + account.getDescription() + ":" + folderName, null, new Runnable() {
|
putBackground("queueExpunge " + account.getDescription() + ":" + folderName, null, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingExpunge.create(folderName);
|
||||||
command.command = PENDING_COMMAND_EXPUNGE;
|
|
||||||
|
|
||||||
command.arguments = new String[1];
|
|
||||||
|
|
||||||
command.arguments[0] = folderName;
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private void processPendingExpunge(PendingCommand command, Account account) throws MessagingException {
|
void processPendingExpunge(PendingExpunge command, Account account) throws MessagingException {
|
||||||
String folder = command.arguments[0];
|
String folder = command.folder;
|
||||||
|
|
||||||
if (account.getErrorFolderName().equals(folder)) {
|
if (account.getErrorFolderName().equals(folder)) {
|
||||||
return;
|
return;
|
||||||
|
@ -2257,73 +2103,8 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void processPendingMarkAllAsRead(PendingMarkAllAsRead command, Account account) throws MessagingException {
|
||||||
// TODO: This method is obsolete and is only for transition from K-9 2.0 to K-9 2.1
|
String folder = command.folder;
|
||||||
// Eventually, it should be removed
|
|
||||||
private void processPendingMoveOrCopyOld(PendingCommand command, Account account) throws MessagingException {
|
|
||||||
String srcFolder = command.arguments[0];
|
|
||||||
String uid = command.arguments[1];
|
|
||||||
String destFolder = command.arguments[2];
|
|
||||||
String isCopyS = command.arguments[3];
|
|
||||||
|
|
||||||
boolean isCopy = false;
|
|
||||||
if (isCopyS != null) {
|
|
||||||
isCopy = Boolean.parseBoolean(isCopyS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.getErrorFolderName().equals(srcFolder)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Store remoteStore = account.getRemoteStore();
|
|
||||||
Folder remoteSrcFolder = remoteStore.getFolder(srcFolder);
|
|
||||||
Folder remoteDestFolder = remoteStore.getFolder(destFolder);
|
|
||||||
|
|
||||||
if (!remoteSrcFolder.exists()) {
|
|
||||||
throw new MessagingException("processPendingMoveOrCopyOld: remoteFolder " + srcFolder + " does not exist", true);
|
|
||||||
}
|
|
||||||
remoteSrcFolder.open(Folder.OPEN_MODE_RW);
|
|
||||||
if (remoteSrcFolder.getMode() != Folder.OPEN_MODE_RW) {
|
|
||||||
throw new MessagingException("processPendingMoveOrCopyOld: could not open remoteSrcFolder " + srcFolder + " read/write", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Message remoteMessage = null;
|
|
||||||
if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
|
|
||||||
remoteMessage = remoteSrcFolder.getMessage(uid);
|
|
||||||
}
|
|
||||||
if (remoteMessage == null) {
|
|
||||||
throw new MessagingException("processPendingMoveOrCopyOld: remoteMessage " + uid + " does not exist", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (K9.DEBUG)
|
|
||||||
Log.d(K9.LOG_TAG, "processPendingMoveOrCopyOld: source folder = " + srcFolder
|
|
||||||
+ ", uid = " + uid + ", destination folder = " + destFolder + ", isCopy = " + isCopy);
|
|
||||||
|
|
||||||
if (!isCopy && destFolder.equals(account.getTrashFolderName())) {
|
|
||||||
if (K9.DEBUG)
|
|
||||||
Log.d(K9.LOG_TAG, "processPendingMoveOrCopyOld doing special case for deleting message");
|
|
||||||
|
|
||||||
remoteMessage.delete(account.getTrashFolderName());
|
|
||||||
remoteSrcFolder.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteDestFolder.open(Folder.OPEN_MODE_RW);
|
|
||||||
if (remoteDestFolder.getMode() != Folder.OPEN_MODE_RW) {
|
|
||||||
throw new MessagingException("processPendingMoveOrCopyOld: could not open remoteDestFolder " + srcFolder + " read/write", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCopy) {
|
|
||||||
remoteSrcFolder.copyMessages(Collections.singletonList(remoteMessage), remoteDestFolder);
|
|
||||||
} else {
|
|
||||||
remoteSrcFolder.moveMessages(Collections.singletonList(remoteMessage), remoteDestFolder);
|
|
||||||
}
|
|
||||||
remoteSrcFolder.close();
|
|
||||||
remoteDestFolder.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processPendingMarkAllAsRead(PendingCommand command, Account account) throws MessagingException {
|
|
||||||
String folder = command.arguments[0];
|
|
||||||
Folder remoteFolder = null;
|
Folder remoteFolder = null;
|
||||||
LocalFolder localFolder = null;
|
LocalFolder localFolder = null;
|
||||||
try {
|
try {
|
||||||
|
@ -2446,9 +2227,7 @@ public class MessagingController {
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.i(K9.LOG_TAG, "Marking all messages in " + account.getDescription() + ":" + folder + " as read");
|
Log.i(K9.LOG_TAG, "Marking all messages in " + account.getDescription() + ":" + folder + " as read");
|
||||||
}
|
}
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingMarkAllAsRead.create(folder);
|
||||||
command.command = PENDING_COMMAND_MARK_ALL_AS_READ;
|
|
||||||
command.arguments = new String[] { folder };
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
|
@ -2535,9 +2314,7 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send flag change to server
|
// Send flag change to server
|
||||||
List<String> value = entry.getValue();
|
queueSetFlag(account, folderName, newState, flag, entry.getValue());
|
||||||
String[] uids = value.toArray(new String[value.size()]);
|
|
||||||
queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids);
|
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2600,12 +2377,8 @@ public class MessagingController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] uids = new String[messages.size()];
|
List<String> uids = getUidsFromMessages(messages);
|
||||||
for (int i = 0, end = uids.length; i < end; i++) {
|
queueSetFlag(account, folderName, newState, flag, uids);
|
||||||
uids[i] = messages.get(i).getUid();
|
|
||||||
}
|
|
||||||
|
|
||||||
queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids);
|
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
} catch (MessagingException me) {
|
} catch (MessagingException me) {
|
||||||
addErrorMessage(account, null, me);
|
addErrorMessage(account, null, me);
|
||||||
|
@ -3037,9 +2810,7 @@ public class MessagingController {
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.i(K9.LOG_TAG, "Moved sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") ");
|
Log.i(K9.LOG_TAG, "Moved sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") ");
|
||||||
|
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingAppend.create(localSentFolder.getName(), message.getUid());
|
||||||
command.command = PENDING_COMMAND_APPEND;
|
|
||||||
command.arguments = new String[] { localSentFolder.getName(), message.getUid() };
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
|
@ -3449,8 +3220,8 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> origUidKeys = origUidMap.keySet();
|
List<String> origUidKeys = new ArrayList<>(origUidMap.keySet());
|
||||||
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidKeys.toArray(new String[origUidKeys.size()]), uidMap);
|
queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidKeys, uidMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
|
@ -3593,7 +3364,7 @@ public class MessagingController {
|
||||||
MessagingListener listener) {
|
MessagingListener listener) {
|
||||||
Folder localFolder = null;
|
Folder localFolder = null;
|
||||||
Folder localTrashFolder = null;
|
Folder localTrashFolder = null;
|
||||||
String[] uids = getUidsFromMessages(messages);
|
List<String> uids = getUidsFromMessages(messages);
|
||||||
try {
|
try {
|
||||||
//We need to make these callbacks before moving the messages to the trash
|
//We need to make these callbacks before moving the messages to the trash
|
||||||
//as messages get a new UID after being moved
|
//as messages get a new UID after being moved
|
||||||
|
@ -3638,25 +3409,19 @@ public class MessagingController {
|
||||||
for (Message message : messages) {
|
for (Message message : messages) {
|
||||||
// If the message was in the Outbox, then it has been copied to local Trash, and has
|
// If the message was in the Outbox, then it has been copied to local Trash, and has
|
||||||
// to be copied to remote trash
|
// to be copied to remote trash
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingAppend.create(account.getTrashFolderName(), message.getUid());
|
||||||
command.command = PENDING_COMMAND_APPEND;
|
|
||||||
command.arguments =
|
|
||||||
new String[] {
|
|
||||||
account.getTrashFolderName(),
|
|
||||||
message.getUid()
|
|
||||||
};
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
}
|
}
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
} else if (account.getDeletePolicy() == DeletePolicy.ON_DELETE) {
|
} else if (account.getDeletePolicy() == DeletePolicy.ON_DELETE) {
|
||||||
if (folder.equals(account.getTrashFolderName())) {
|
if (folder.equals(account.getTrashFolderName())) {
|
||||||
queueSetFlag(account, folder, Boolean.toString(true), Flag.DELETED.toString(), uids);
|
queueSetFlag(account, folder, true, Flag.DELETED, uids);
|
||||||
} else {
|
} else {
|
||||||
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids, uidMap);
|
queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids, uidMap);
|
||||||
}
|
}
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
} else if (account.getDeletePolicy() == DeletePolicy.MARK_AS_READ) {
|
} else if (account.getDeletePolicy() == DeletePolicy.MARK_AS_READ) {
|
||||||
queueSetFlag(account, folder, Boolean.toString(true), Flag.SEEN.toString(), uids);
|
queueSetFlag(account, folder, true, Flag.SEEN, uids);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
} else {
|
} else {
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
|
@ -3677,16 +3442,15 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] getUidsFromMessages(List <? extends Message> messages) {
|
private static List<String> getUidsFromMessages(List <? extends Message> messages) {
|
||||||
String[] uids = new String[messages.size()];
|
List<String> uids = new ArrayList<>(messages.size());
|
||||||
for (int i = 0; i < messages.size(); i++) {
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
uids[i] = messages.get(i).getUid();
|
uids.add(messages.get(i).getUid());
|
||||||
}
|
}
|
||||||
return uids;
|
return uids;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("UnusedParameters") // for consistency with other PendingCommand methods
|
void processPendingEmptyTrash(Account account) throws MessagingException {
|
||||||
private void processPendingEmptyTrash(PendingCommand command, Account account) throws MessagingException {
|
|
||||||
Store remoteStore = account.getRemoteStore();
|
Store remoteStore = account.getRemoteStore();
|
||||||
|
|
||||||
Folder remoteFolder = remoteStore.getFolder(account.getTrashFolderName());
|
Folder remoteFolder = remoteStore.getFolder(account.getTrashFolderName());
|
||||||
|
@ -3732,9 +3496,7 @@ public class MessagingController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTrashLocalOnly) {
|
if (!isTrashLocalOnly) {
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingEmptyTrash.create();
|
||||||
command.command = PENDING_COMMAND_EMPTY_TRASH;
|
|
||||||
command.arguments = EMPTY_STRING_ARRAY;
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
|
@ -4227,12 +3989,7 @@ public class MessagingController {
|
||||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||||
|
|
||||||
if (saveRemotely) {
|
if (saveRemotely) {
|
||||||
PendingCommand command = new PendingCommand();
|
PendingCommand command = PendingAppend.create(localFolder.getName(), localMessage.getUid());
|
||||||
command.command = PENDING_COMMAND_APPEND;
|
|
||||||
command.arguments = new String[] {
|
|
||||||
localFolder.getName(),
|
|
||||||
localMessage.getUid()
|
|
||||||
};
|
|
||||||
queuePendingCommand(account, command);
|
queuePendingCommand(account, command);
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
package com.fsck.k9.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.fsck.k9.Account;
|
||||||
|
import com.fsck.k9.mail.Flag;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
|
||||||
|
public class MessagingControllerCommands {
|
||||||
|
static final String COMMAND_APPEND = "append";
|
||||||
|
static final String COMMAND_MARK_ALL_AS_READ = "mark_all_as_read";
|
||||||
|
static final String COMMAND_SET_FLAG = "set_flag";
|
||||||
|
static final String COMMAND_EXPUNGE = "expunge";
|
||||||
|
static final String COMMAND_MOVE_OR_COPY = "move_or_copy";
|
||||||
|
static final String COMMAND_EMPTY_TRASH = "empty_trash";
|
||||||
|
|
||||||
|
|
||||||
|
public abstract static class PendingCommand {
|
||||||
|
public long databaseId;
|
||||||
|
|
||||||
|
|
||||||
|
PendingCommand() { }
|
||||||
|
|
||||||
|
public abstract String getCommandName();
|
||||||
|
public abstract void execute(MessagingController controller, Account account) throws MessagingException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingMoveOrCopy extends PendingCommand {
|
||||||
|
public final String srcFolder;
|
||||||
|
public final String destFolder;
|
||||||
|
public final boolean isCopy;
|
||||||
|
public final List<String> uids;
|
||||||
|
public final Map<String, String> newUidMap;
|
||||||
|
|
||||||
|
|
||||||
|
public static PendingMoveOrCopy create(String srcFolder, String destFolder, boolean isCopy,
|
||||||
|
Map<String, String> uidMap) {
|
||||||
|
return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, null, uidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PendingMoveOrCopy create(String srcFolder, String destFolder, boolean isCopy, List<String> uids) {
|
||||||
|
return new PendingMoveOrCopy(srcFolder, destFolder, isCopy, uids, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, List<String> uids,
|
||||||
|
Map<String, String> newUidMap) {
|
||||||
|
this.srcFolder = srcFolder;
|
||||||
|
this.destFolder = destFolder;
|
||||||
|
this.isCopy = isCopy;
|
||||||
|
this.uids = uids;
|
||||||
|
this.newUidMap = newUidMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return COMMAND_MOVE_OR_COPY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MessagingController controller, Account account) throws MessagingException {
|
||||||
|
controller.processPendingMoveOrCopy(this, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingEmptyTrash extends PendingCommand {
|
||||||
|
public static PendingEmptyTrash create() {
|
||||||
|
return new PendingEmptyTrash();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return COMMAND_EMPTY_TRASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MessagingController controller, Account account) throws MessagingException {
|
||||||
|
controller.processPendingEmptyTrash(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingSetFlag extends PendingCommand {
|
||||||
|
public final String folder;
|
||||||
|
public final boolean newState;
|
||||||
|
public final Flag flag;
|
||||||
|
public final List<String> uids;
|
||||||
|
|
||||||
|
|
||||||
|
public static PendingSetFlag create(String folder, boolean newState, Flag flag, List<String> uids) {
|
||||||
|
return new PendingSetFlag(folder, newState, flag, uids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingSetFlag(String folder, boolean newState, Flag flag, List<String> uids) {
|
||||||
|
this.folder = folder;
|
||||||
|
this.newState = newState;
|
||||||
|
this.flag = flag;
|
||||||
|
this.uids = uids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return COMMAND_SET_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MessagingController controller, Account account) throws MessagingException {
|
||||||
|
controller.processPendingSetFlag(this, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingAppend extends PendingCommand {
|
||||||
|
public final String folder;
|
||||||
|
public final String uid;
|
||||||
|
|
||||||
|
|
||||||
|
public static PendingAppend create(String folderName, String uid) {
|
||||||
|
return new PendingAppend(folderName, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingAppend(String folder, String uid) {
|
||||||
|
this.folder = folder;
|
||||||
|
this.uid = uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return COMMAND_APPEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MessagingController controller, Account account) throws MessagingException {
|
||||||
|
controller.processPendingAppend(this, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingMarkAllAsRead extends PendingCommand {
|
||||||
|
public final String folder;
|
||||||
|
|
||||||
|
|
||||||
|
public static PendingMarkAllAsRead create(String folder) {
|
||||||
|
return new PendingMarkAllAsRead(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingMarkAllAsRead(String folder) {
|
||||||
|
this.folder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return COMMAND_MARK_ALL_AS_READ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MessagingController controller, Account account) throws MessagingException {
|
||||||
|
controller.processPendingMarkAllAsRead(this, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PendingExpunge extends PendingCommand {
|
||||||
|
public final String folder;
|
||||||
|
|
||||||
|
|
||||||
|
public static PendingExpunge create(String folderName) {
|
||||||
|
return new PendingExpunge(folderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingExpunge(String folder) {
|
||||||
|
this.folder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return COMMAND_EXPUNGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MessagingController controller, Account account) throws MessagingException {
|
||||||
|
controller.processPendingExpunge(this, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.fsck.k9.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
||||||
|
import com.squareup.moshi.JsonAdapter;
|
||||||
|
import com.squareup.moshi.Moshi;
|
||||||
|
|
||||||
|
|
||||||
|
public class PendingCommandSerializer {
|
||||||
|
private static final PendingCommandSerializer INSTANCE = new PendingCommandSerializer();
|
||||||
|
|
||||||
|
|
||||||
|
private final Map<String, JsonAdapter<? extends PendingCommand>> adapters;
|
||||||
|
|
||||||
|
|
||||||
|
private PendingCommandSerializer() {
|
||||||
|
Moshi moshi = new Moshi.Builder().build();
|
||||||
|
HashMap<String, JsonAdapter<? extends PendingCommand>> adapters = new HashMap<>();
|
||||||
|
|
||||||
|
adapters.put(MessagingControllerCommands.COMMAND_MOVE_OR_COPY, moshi.adapter(PendingMoveOrCopy.class));
|
||||||
|
adapters.put(MessagingControllerCommands.COMMAND_APPEND, moshi.adapter(PendingAppend.class));
|
||||||
|
adapters.put(MessagingControllerCommands.COMMAND_EMPTY_TRASH, moshi.adapter(PendingEmptyTrash.class));
|
||||||
|
adapters.put(MessagingControllerCommands.COMMAND_EXPUNGE, moshi.adapter(PendingExpunge.class));
|
||||||
|
adapters.put(MessagingControllerCommands.COMMAND_MARK_ALL_AS_READ, moshi.adapter(PendingMarkAllAsRead.class));
|
||||||
|
adapters.put(MessagingControllerCommands.COMMAND_SET_FLAG, moshi.adapter(PendingSetFlag.class));
|
||||||
|
|
||||||
|
this.adapters = Collections.unmodifiableMap(adapters);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static PendingCommandSerializer getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <T extends PendingCommand> String serialize(T command) {
|
||||||
|
// noinspection unchecked, we know the map has correctly matching adapters
|
||||||
|
JsonAdapter<T> adapter = (JsonAdapter<T>) adapters.get(command.getCommandName());
|
||||||
|
if (adapter == null) {
|
||||||
|
throw new IllegalArgumentException("Unsupported pending command type!");
|
||||||
|
}
|
||||||
|
return adapter.toJson(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingCommand unserialize(long databaseId, String commandName, String data) {
|
||||||
|
JsonAdapter<? extends PendingCommand> adapter = adapters.get(commandName);
|
||||||
|
if (adapter == null) {
|
||||||
|
throw new IllegalArgumentException("Unsupported pending command type!");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
PendingCommand command = adapter.fromJson(data);
|
||||||
|
command.databaseId = databaseId;
|
||||||
|
return command;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -128,40 +128,6 @@ public class Utility {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A fast version of URLDecoder.decode() that works only with UTF-8 and does only two
|
|
||||||
* allocations. This version is around 3x as fast as the standard one and I'm using it
|
|
||||||
* hundreds of times in places that slow down the UI, so it helps.
|
|
||||||
*/
|
|
||||||
public static String fastUrlDecode(String s) {
|
|
||||||
|
|
||||||
byte[] bytes = s.getBytes(Charset.forName("UTF-8"));
|
|
||||||
byte ch;
|
|
||||||
int length = 0;
|
|
||||||
for (int i = 0, count = bytes.length; i < count; i++) {
|
|
||||||
ch = bytes[i];
|
|
||||||
if (ch == '%') {
|
|
||||||
int h = (bytes[i + 1] - '0');
|
|
||||||
int l = (bytes[i + 2] - '0');
|
|
||||||
if (h > 9) {
|
|
||||||
h -= 7;
|
|
||||||
}
|
|
||||||
if (l > 9) {
|
|
||||||
l -= 7;
|
|
||||||
}
|
|
||||||
bytes[length] = (byte)((h << 4) | l);
|
|
||||||
i += 2;
|
|
||||||
} else if (ch == '+') {
|
|
||||||
bytes[length] = ' ';
|
|
||||||
} else {
|
|
||||||
bytes[length] = bytes[i];
|
|
||||||
}
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
return new String(bytes, 0, length, Charset.forName("UTF-8"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO disabled this method globally. It is used in all the settings screens but I just
|
* TODO disabled this method globally. It is used in all the settings screens but I just
|
||||||
* noticed that an unrelated icon was dimmed. Android must share drawables internally.
|
* noticed that an unrelated icon was dimmed. Android must share drawables internally.
|
||||||
|
|
|
@ -34,7 +34,8 @@ import android.util.Log;
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
import com.fsck.k9.controller.PendingCommandSerializer;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
@ -153,7 +154,7 @@ public class LocalStore extends Store implements Serializable {
|
||||||
*/
|
*/
|
||||||
private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 500;
|
private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 500;
|
||||||
|
|
||||||
public static final int DB_VERSION = 59;
|
public static final int DB_VERSION = 60;
|
||||||
|
|
||||||
|
|
||||||
public static String getColumnNameForFlag(Flag flag) {
|
public static String getColumnNameForFlag(Flag flag) {
|
||||||
|
@ -188,6 +189,7 @@ public class LocalStore extends Store implements Serializable {
|
||||||
private final MessagePreviewCreator messagePreviewCreator;
|
private final MessagePreviewCreator messagePreviewCreator;
|
||||||
private final MessageFulltextCreator messageFulltextCreator;
|
private final MessageFulltextCreator messageFulltextCreator;
|
||||||
private final AttachmentCounter attachmentCounter;
|
private final AttachmentCounter attachmentCounter;
|
||||||
|
private final PendingCommandSerializer pendingCommandSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* local://localhost/path/to/database/uuid.db
|
* local://localhost/path/to/database/uuid.db
|
||||||
|
@ -206,6 +208,7 @@ public class LocalStore extends Store implements Serializable {
|
||||||
messagePreviewCreator = MessagePreviewCreator.newInstance();
|
messagePreviewCreator = MessagePreviewCreator.newInstance();
|
||||||
messageFulltextCreator = MessageFulltextCreator.newInstance();
|
messageFulltextCreator = MessageFulltextCreator.newInstance();
|
||||||
attachmentCounter = AttachmentCounter.newInstance();
|
attachmentCounter = AttachmentCounter.newInstance();
|
||||||
|
pendingCommandSerializer = PendingCommandSerializer.getInstance();
|
||||||
|
|
||||||
database.open();
|
database.open();
|
||||||
}
|
}
|
||||||
|
@ -491,7 +494,7 @@ public class LocalStore extends Store implements Serializable {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = db.query("pending_commands",
|
cursor = db.query("pending_commands",
|
||||||
new String[] { "id", "command", "arguments" },
|
new String[] { "id", "command", "data" },
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
@ -499,14 +502,11 @@ public class LocalStore extends Store implements Serializable {
|
||||||
"id ASC");
|
"id ASC");
|
||||||
List<PendingCommand> commands = new ArrayList<>();
|
List<PendingCommand> commands = new ArrayList<>();
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
PendingCommand command = new PendingCommand();
|
long databaseId = cursor.getLong(0);
|
||||||
command.mId = cursor.getLong(0);
|
String commandName = cursor.getString(1);
|
||||||
command.command = cursor.getString(1);
|
String data = cursor.getString(2);
|
||||||
String arguments = cursor.getString(2);
|
PendingCommand command = pendingCommandSerializer.unserialize(
|
||||||
command.arguments = arguments.split(",");
|
databaseId, commandName, data);
|
||||||
for (int i = 0; i < command.arguments.length; i++) {
|
|
||||||
command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]);
|
|
||||||
}
|
|
||||||
commands.add(command);
|
commands.add(command);
|
||||||
}
|
}
|
||||||
return commands;
|
return commands;
|
||||||
|
@ -518,12 +518,9 @@ public class LocalStore extends Store implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPendingCommand(PendingCommand command) throws MessagingException {
|
public void addPendingCommand(PendingCommand command) throws MessagingException {
|
||||||
for (int i = 0; i < command.arguments.length; i++) {
|
|
||||||
command.arguments[i] = UrlEncodingHelper.encodeUtf8(command.arguments[i]);
|
|
||||||
}
|
|
||||||
final ContentValues cv = new ContentValues();
|
final ContentValues cv = new ContentValues();
|
||||||
cv.put("command", command.command);
|
cv.put("command", command.getCommandName());
|
||||||
cv.put("arguments", Utility.combine(command.arguments, ','));
|
cv.put("data", pendingCommandSerializer.serialize(command));
|
||||||
database.execute(false, new DbCallback<Void>() {
|
database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||||
|
@ -537,7 +534,7 @@ public class LocalStore extends Store implements Serializable {
|
||||||
database.execute(false, new DbCallback<Void>() {
|
database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||||
db.delete("pending_commands", "id = ?", new String[] { Long.toString(command.mId) });
|
db.delete("pending_commands", "id = ?", new String[] { Long.toString(command.databaseId) });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -553,25 +550,6 @@ public class LocalStore extends Store implements Serializable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PendingCommand {
|
|
||||||
private long mId;
|
|
||||||
public String command;
|
|
||||||
public String[] arguments;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(command);
|
|
||||||
sb.append(": ");
|
|
||||||
for (String argument : arguments) {
|
|
||||||
sb.append(", ");
|
|
||||||
sb.append(argument);
|
|
||||||
//sb.append("\n");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMoveCapable() {
|
public boolean isMoveCapable() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -195,7 +195,7 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
|
||||||
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
||||||
db.execSQL("CREATE TABLE pending_commands " +
|
db.execSQL("CREATE TABLE pending_commands " +
|
||||||
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
|
"(id INTEGER PRIMARY KEY, command TEXT, data TEXT)");
|
||||||
|
|
||||||
db.execSQL("DROP TRIGGER IF EXISTS delete_folder");
|
db.execSQL("DROP TRIGGER IF EXISTS delete_folder");
|
||||||
db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
|
db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.fsck.k9.mailstore.migrations;
|
package com.fsck.k9.mailstore.migrations;
|
||||||
|
|
||||||
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
package com.fsck.k9.mailstore.migrations;
|
||||||
|
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
||||||
|
import com.fsck.k9.controller.PendingCommandSerializer;
|
||||||
|
import com.fsck.k9.helper.Utility;
|
||||||
|
import com.fsck.k9.mail.Flag;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
|
||||||
|
class MigrationTo60 {
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy";
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk";
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew";
|
||||||
|
private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash";
|
||||||
|
private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk";
|
||||||
|
private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag";
|
||||||
|
private static final String PENDING_COMMAND_APPEND = "com.fsck.k9.MessagingController.append";
|
||||||
|
private static final String PENDING_COMMAND_MARK_ALL_AS_READ = "com.fsck.k9.MessagingController.markAllAsRead";
|
||||||
|
private static final String PENDING_COMMAND_EXPUNGE = "com.fsck.k9.MessagingController.expunge";
|
||||||
|
|
||||||
|
|
||||||
|
public static void migratePendingCommands(SQLiteDatabase db) {
|
||||||
|
List<PendingCommand> pendingCommands = new ArrayList<>();
|
||||||
|
|
||||||
|
if (columnExists(db, "pending_commands", "arguments")) {
|
||||||
|
for (OldPendingCommand oldPendingCommand : getPendingCommands(db)) {
|
||||||
|
PendingCommand newPendingCommand = migratePendingCommand(oldPendingCommand);
|
||||||
|
pendingCommands.add(newPendingCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
||||||
|
db.execSQL("CREATE TABLE pending_commands (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"command TEXT, " +
|
||||||
|
"data TEXT" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance();
|
||||||
|
for (PendingCommand pendingCommand : pendingCommands) {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("command", pendingCommand.getCommandName());
|
||||||
|
cv.put("data", pendingCommandSerializer.serialize(pendingCommand));
|
||||||
|
db.insert("pending_commands", "command", cv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean columnExists(SQLiteDatabase db, String table, String columnName) {
|
||||||
|
Cursor columnCursor = db.rawQuery("PRAGMA table_info(" + table + ")", null);
|
||||||
|
boolean foundColumn = false;
|
||||||
|
while (columnCursor.moveToNext()) {
|
||||||
|
String currentColumnName = columnCursor.getString(1);
|
||||||
|
if (currentColumnName.equals(columnName)) {
|
||||||
|
foundColumn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columnCursor.close();
|
||||||
|
return foundColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static PendingCommand migratePendingCommand(OldPendingCommand oldPendingCommand) {
|
||||||
|
switch (oldPendingCommand.command) {
|
||||||
|
case PENDING_COMMAND_APPEND: {
|
||||||
|
return migrateCommandAppend(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_SET_FLAG_BULK: {
|
||||||
|
return migrateCommandSetFlagBulk(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_SET_FLAG: {
|
||||||
|
return migrateCommandSetFlag(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_MARK_ALL_AS_READ: {
|
||||||
|
return migrateCommandMarkAllAsRead(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_MOVE_OR_COPY_BULK: {
|
||||||
|
return migrateCommandMoveOrCopyBulk(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW: {
|
||||||
|
return migrateCommandMoveOrCopyBulkNew(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_MOVE_OR_COPY: {
|
||||||
|
return migrateCommandMoveOrCopy(oldPendingCommand);
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_EMPTY_TRASH: {
|
||||||
|
return migrateCommandEmptyTrash();
|
||||||
|
}
|
||||||
|
case PENDING_COMMAND_EXPUNGE: {
|
||||||
|
return migrateCommandExpunge(oldPendingCommand);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new IllegalArgumentException("Tried to migrate unknown pending command!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandExpunge(OldPendingCommand command) {
|
||||||
|
String folder = command.arguments[0];
|
||||||
|
return PendingExpunge.create(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandEmptyTrash() {
|
||||||
|
return PendingEmptyTrash.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandMoveOrCopy(OldPendingCommand command) {
|
||||||
|
String srcFolder = command.arguments[0];
|
||||||
|
String uid = command.arguments[1];
|
||||||
|
String destFolder = command.arguments[2];
|
||||||
|
boolean isCopy = Boolean.parseBoolean(command.arguments[3]);
|
||||||
|
|
||||||
|
return PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, singletonList(uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandMoveOrCopyBulkNew(OldPendingCommand command) {
|
||||||
|
String srcFolder = command.arguments[0];
|
||||||
|
String destFolder = command.arguments[1];
|
||||||
|
boolean isCopy = Boolean.parseBoolean(command.arguments[2]);
|
||||||
|
boolean hasNewUids = Boolean.parseBoolean(command.arguments[3]);
|
||||||
|
|
||||||
|
if (hasNewUids) {
|
||||||
|
Map<String, String> uidMap = new HashMap<>();
|
||||||
|
int offset = (command.arguments.length - 4) / 2;
|
||||||
|
for (int i = 4; i < 4 + offset; i++) {
|
||||||
|
uidMap.put(command.arguments[i], command.arguments[i + offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uidMap);
|
||||||
|
} else {
|
||||||
|
List<String> uids = new ArrayList<>(command.arguments.length - 4);
|
||||||
|
uids.addAll(Arrays.asList(command.arguments).subList(4, command.arguments.length));
|
||||||
|
|
||||||
|
return PendingMoveOrCopy.create(srcFolder, destFolder, isCopy, uids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandMoveOrCopyBulk(OldPendingCommand command) {
|
||||||
|
int len = command.arguments.length;
|
||||||
|
|
||||||
|
OldPendingCommand newCommand = new OldPendingCommand();
|
||||||
|
newCommand.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||||
|
newCommand.arguments = new String[len + 1];
|
||||||
|
newCommand.arguments[0] = command.arguments[0];
|
||||||
|
newCommand.arguments[1] = command.arguments[1];
|
||||||
|
newCommand.arguments[2] = command.arguments[2];
|
||||||
|
newCommand.arguments[3] = Boolean.toString(false);
|
||||||
|
System.arraycopy(command.arguments, 3, newCommand.arguments, 4, len - 3);
|
||||||
|
|
||||||
|
return migratePendingCommand(newCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandMarkAllAsRead(OldPendingCommand command) {
|
||||||
|
return PendingMarkAllAsRead.create(command.arguments[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandSetFlag(OldPendingCommand command) {
|
||||||
|
String folder = command.arguments[0];
|
||||||
|
String uid = command.arguments[1];
|
||||||
|
boolean newState = Boolean.parseBoolean(command.arguments[2]);
|
||||||
|
Flag flag = Flag.valueOf(command.arguments[3]);
|
||||||
|
|
||||||
|
return PendingSetFlag.create(folder, newState, flag, singletonList(uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandSetFlagBulk(OldPendingCommand command) {
|
||||||
|
String folder = command.arguments[0];
|
||||||
|
boolean newState = Boolean.parseBoolean(command.arguments[1]);
|
||||||
|
Flag flag = Flag.valueOf(command.arguments[2]);
|
||||||
|
|
||||||
|
List<String> uids = new ArrayList<>(command.arguments.length - 3);
|
||||||
|
uids.addAll(Arrays.asList(command.arguments).subList(3, command.arguments.length));
|
||||||
|
|
||||||
|
return PendingSetFlag.create(folder, newState, flag, uids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingCommand migrateCommandAppend(OldPendingCommand command) {
|
||||||
|
String folder = command.arguments[0];
|
||||||
|
String uid = command.arguments[1];
|
||||||
|
return PendingAppend.create(folder, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<OldPendingCommand> getPendingCommands(SQLiteDatabase db) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = db.query("pending_commands",
|
||||||
|
new String[] { "id", "command", "arguments" }, null, null, null, null, "id ASC");
|
||||||
|
List<OldPendingCommand> commands = new ArrayList<>();
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = cursor.getString(1);
|
||||||
|
String arguments = cursor.getString(2);
|
||||||
|
command.arguments = arguments.split(",");
|
||||||
|
for (int i = 0; i < command.arguments.length; i++) {
|
||||||
|
command.arguments[i] = fastUrlDecode(command.arguments[i]);
|
||||||
|
}
|
||||||
|
commands.add(command);
|
||||||
|
}
|
||||||
|
return commands;
|
||||||
|
} finally {
|
||||||
|
Utility.closeQuietly(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fastUrlDecode(String s) {
|
||||||
|
byte[] bytes = s.getBytes(Charset.forName("UTF-8"));
|
||||||
|
byte ch;
|
||||||
|
int length = 0;
|
||||||
|
for (int i = 0, count = bytes.length; i < count; i++) {
|
||||||
|
ch = bytes[i];
|
||||||
|
if (ch == '%') {
|
||||||
|
int h = (bytes[i + 1] - '0');
|
||||||
|
int l = (bytes[i + 2] - '0');
|
||||||
|
if (h > 9) {
|
||||||
|
h -= 7;
|
||||||
|
}
|
||||||
|
if (l > 9) {
|
||||||
|
l -= 7;
|
||||||
|
}
|
||||||
|
bytes[length] = (byte) ((h << 4) | l);
|
||||||
|
i += 2;
|
||||||
|
} else if (ch == '+') {
|
||||||
|
bytes[length] = ' ';
|
||||||
|
} else {
|
||||||
|
bytes[length] = bytes[i];
|
||||||
|
}
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
return new String(bytes, 0, length, Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class OldPendingCommand {
|
||||||
|
public String command;
|
||||||
|
public String[] arguments;
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,8 @@ public class Migrations {
|
||||||
MigrationTo58.createDeleteMessageTrigger(db);
|
MigrationTo58.createDeleteMessageTrigger(db);
|
||||||
case 58:
|
case 58:
|
||||||
MigrationTo59.addMissingIndexes(db);
|
MigrationTo59.addMissingIndexes(db);
|
||||||
|
case 59:
|
||||||
|
MigrationTo60.migratePendingCommands(db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.fsck.k9.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class PendingCommandSerializerTest {
|
||||||
|
static final int DATABASE_ID = 123;
|
||||||
|
static final String UID = "uid";
|
||||||
|
static final String SOURCE_FOLDER = "source_folder";
|
||||||
|
static final String DEST_FOLDER = "dest_folder";
|
||||||
|
static final HashMap<String, String> UID_MAP = new HashMap<>();
|
||||||
|
public static final boolean IS_COPY = true;
|
||||||
|
|
||||||
|
static {
|
||||||
|
UID_MAP.put("uid_1", "uid_other_1");
|
||||||
|
UID_MAP.put("uid_2", "uid_other_2");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PendingCommandSerializer pendingCommandSerializer = PendingCommandSerializer.getInstance();
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializeDeserialize__withoutArguments() {
|
||||||
|
PendingCommand pendingCommand = PendingEmptyTrash.create();
|
||||||
|
|
||||||
|
String serializedCommand = pendingCommandSerializer.serialize(pendingCommand);
|
||||||
|
PendingEmptyTrash unserializedCommand = (PendingEmptyTrash) pendingCommandSerializer.unserialize(
|
||||||
|
DATABASE_ID, pendingCommand.getCommandName(), serializedCommand);
|
||||||
|
|
||||||
|
assertEquals(DATABASE_ID, unserializedCommand.databaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializeDeserialize__withArguments() {
|
||||||
|
PendingCommand pendingCommand = PendingAppend.create(SOURCE_FOLDER, UID);
|
||||||
|
|
||||||
|
String serializedCommand = pendingCommandSerializer.serialize(pendingCommand);
|
||||||
|
PendingAppend unserializedCommand = (PendingAppend) pendingCommandSerializer.unserialize(
|
||||||
|
DATABASE_ID, pendingCommand.getCommandName(), serializedCommand);
|
||||||
|
|
||||||
|
assertEquals(DATABASE_ID, unserializedCommand.databaseId);
|
||||||
|
assertEquals(SOURCE_FOLDER, unserializedCommand.folder);
|
||||||
|
assertEquals(UID, unserializedCommand.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializeDeserialize__withComplexArguments() {
|
||||||
|
PendingCommand pendingCommand = PendingMoveOrCopy.create(
|
||||||
|
SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_MAP);
|
||||||
|
|
||||||
|
String serializedCommand = pendingCommandSerializer.serialize(pendingCommand);
|
||||||
|
PendingMoveOrCopy unserializedCommand = (PendingMoveOrCopy) pendingCommandSerializer.unserialize(
|
||||||
|
DATABASE_ID, pendingCommand.getCommandName(), serializedCommand);
|
||||||
|
|
||||||
|
assertEquals(DATABASE_ID, unserializedCommand.databaseId);
|
||||||
|
assertEquals(SOURCE_FOLDER, unserializedCommand.srcFolder);
|
||||||
|
assertEquals(DEST_FOLDER, unserializedCommand.destFolder);
|
||||||
|
assertEquals(UID_MAP, unserializedCommand.newUidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testDeserialize__withUnknownCommandName__shouldFail() {
|
||||||
|
PendingCommand pendingCommand = PendingEmptyTrash.create();
|
||||||
|
|
||||||
|
String serializedCommand = pendingCommandSerializer.serialize(pendingCommand);
|
||||||
|
pendingCommandSerializer.unserialize(DATABASE_ID, "BAD_COMMAND_NAME", serializedCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,325 @@
|
||||||
|
package com.fsck.k9.mailstore.migrations;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy;
|
||||||
|
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag;
|
||||||
|
import com.fsck.k9.mail.Flag;
|
||||||
|
import com.fsck.k9.mailstore.migrations.MigrationTo60.OldPendingCommand;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(manifest = Config.NONE)
|
||||||
|
public class MigrationTo60Test {
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY = "com.fsck.k9.MessagingController.moveOrCopy";
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK = "com.fsck.k9.MessagingController.moveOrCopyBulk";
|
||||||
|
private static final String PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW = "com.fsck.k9.MessagingController.moveOrCopyBulkNew";
|
||||||
|
private static final String PENDING_COMMAND_EMPTY_TRASH = "com.fsck.k9.MessagingController.emptyTrash";
|
||||||
|
private static final String PENDING_COMMAND_SET_FLAG_BULK = "com.fsck.k9.MessagingController.setFlagBulk";
|
||||||
|
private static final String PENDING_COMMAND_SET_FLAG = "com.fsck.k9.MessagingController.setFlag";
|
||||||
|
private static final String PENDING_COMMAND_APPEND = "com.fsck.k9.MessagingController.append";
|
||||||
|
private static final String PENDING_COMMAND_MARK_ALL_AS_READ = "com.fsck.k9.MessagingController.markAllAsRead";
|
||||||
|
private static final String PENDING_COMMAND_EXPUNGE = "com.fsck.k9.MessagingController.expunge";
|
||||||
|
|
||||||
|
private static final String SOURCE_FOLDER = "source_folder";
|
||||||
|
private static final String DEST_FOLDER = "dest_folder";
|
||||||
|
private static final boolean IS_COPY = true;
|
||||||
|
private static final String[] UID_ARRAY = new String[] { "uid_1", "uid_2" };
|
||||||
|
private static final boolean FLAG_STATE = true;
|
||||||
|
private static final Flag FLAG = Flag.X_DESTROYED;
|
||||||
|
private static final String UID = "uid";
|
||||||
|
private static final HashMap<String, String> UID_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
UID_MAP.put("uid_1", "uid_other_1");
|
||||||
|
UID_MAP.put("uid_2", "uid_other_2");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migratePendingCommands_shouldChangeTableStructure() {
|
||||||
|
SQLiteDatabase database = createV59Table();
|
||||||
|
|
||||||
|
MigrationTo60.migratePendingCommands(database);
|
||||||
|
|
||||||
|
List<String> columns = getColumnList(database, "pending_commands");
|
||||||
|
assertEquals(asList("id", "command", "data"), columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migratePendingCommands_withMultipleRuns_shouldNotThrow() {
|
||||||
|
SQLiteDatabase database = createV59Table();
|
||||||
|
MigrationTo60.migratePendingCommands(database);
|
||||||
|
|
||||||
|
MigrationTo60.migratePendingCommands(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateMoveOrCopy_withUidArray() {
|
||||||
|
OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY);
|
||||||
|
|
||||||
|
PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder);
|
||||||
|
assertEquals(DEST_FOLDER, pendingCommand.destFolder);
|
||||||
|
assertEquals(IS_COPY, pendingCommand.isCopy);
|
||||||
|
assertEquals(asList(UID_ARRAY), pendingCommand.uids);
|
||||||
|
assertNull(pendingCommand.newUidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateMoveOrCopy_withUidMap() {
|
||||||
|
OldPendingCommand command = queueMoveOrCopy(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_MAP);
|
||||||
|
|
||||||
|
PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder);
|
||||||
|
assertEquals(DEST_FOLDER, pendingCommand.destFolder);
|
||||||
|
assertEquals(IS_COPY, pendingCommand.isCopy);
|
||||||
|
assertEquals(UID_MAP, pendingCommand.newUidMap);
|
||||||
|
assertNull(pendingCommand.uids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateMoveOrCopy_withOldFormat() {
|
||||||
|
OldPendingCommand command = queueMoveOrCopyOld(SOURCE_FOLDER, DEST_FOLDER, IS_COPY, UID_ARRAY);
|
||||||
|
|
||||||
|
PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder);
|
||||||
|
assertEquals(DEST_FOLDER, pendingCommand.destFolder);
|
||||||
|
assertEquals(IS_COPY, pendingCommand.isCopy);
|
||||||
|
assertEquals(asList(UID_ARRAY), pendingCommand.uids);
|
||||||
|
assertNull(pendingCommand.newUidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateMoveOrCopy__withEvenOlderFormat() {
|
||||||
|
OldPendingCommand command = queueMoveOrCopyEvenOlder(SOURCE_FOLDER, DEST_FOLDER, UID, IS_COPY);
|
||||||
|
|
||||||
|
PendingMoveOrCopy pendingCommand = (PendingMoveOrCopy) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.srcFolder);
|
||||||
|
assertEquals(DEST_FOLDER, pendingCommand.destFolder);
|
||||||
|
assertEquals(IS_COPY, pendingCommand.isCopy);
|
||||||
|
assertEquals(Collections.singletonList(UID), pendingCommand.uids);
|
||||||
|
assertNull(pendingCommand.newUidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateSetFlag() {
|
||||||
|
OldPendingCommand command = queueSetFlagBulk(SOURCE_FOLDER, FLAG_STATE, FLAG, UID_ARRAY);
|
||||||
|
|
||||||
|
PendingSetFlag pendingCommand = (PendingSetFlag) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.folder);
|
||||||
|
assertEquals(FLAG_STATE, pendingCommand.newState);
|
||||||
|
assertEquals(FLAG, pendingCommand.flag);
|
||||||
|
assertEquals(asList(UID_ARRAY), pendingCommand.uids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateSetFlag_oldFormat() {
|
||||||
|
OldPendingCommand command = queueSetFlagOld(SOURCE_FOLDER, FLAG_STATE, FLAG, UID);
|
||||||
|
|
||||||
|
PendingSetFlag pendingCommand = (PendingSetFlag) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.folder);
|
||||||
|
assertEquals(FLAG_STATE, pendingCommand.newState);
|
||||||
|
assertEquals(FLAG, pendingCommand.flag);
|
||||||
|
assertEquals(Collections.singletonList(UID), pendingCommand.uids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateExpunge() {
|
||||||
|
OldPendingCommand command = queueExpunge(SOURCE_FOLDER);
|
||||||
|
|
||||||
|
PendingExpunge pendingCommand = (PendingExpunge) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateEmptyTrash() {
|
||||||
|
OldPendingCommand command = queueEmptyTrash();
|
||||||
|
|
||||||
|
PendingCommand pendingCommand = MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertTrue(pendingCommand instanceof PendingEmptyTrash);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateMarkAllMessagesRead() {
|
||||||
|
OldPendingCommand command = queueMarkAllMessagesRead(SOURCE_FOLDER);
|
||||||
|
|
||||||
|
PendingMarkAllAsRead pendingCommand = (PendingMarkAllAsRead) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void migrateAppend() {
|
||||||
|
OldPendingCommand command = queueAppend(SOURCE_FOLDER, UID);
|
||||||
|
|
||||||
|
PendingAppend pendingCommand = (PendingAppend) MigrationTo60.migratePendingCommand(command);
|
||||||
|
|
||||||
|
assertEquals(SOURCE_FOLDER, pendingCommand.folder);
|
||||||
|
assertEquals(UID, pendingCommand.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueAppend(String srcFolder, String uid) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_APPEND;
|
||||||
|
command.arguments = new String[] { srcFolder, uid };
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, String uids[]) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||||
|
|
||||||
|
int length = 4 + uids.length;
|
||||||
|
command.arguments = new String[length];
|
||||||
|
command.arguments[0] = srcFolder;
|
||||||
|
command.arguments[1] = destFolder;
|
||||||
|
command.arguments[2] = Boolean.toString(isCopy);
|
||||||
|
command.arguments[3] = Boolean.toString(false);
|
||||||
|
System.arraycopy(uids, 0, command.arguments, 4, uids.length);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueMoveOrCopy(String srcFolder, String destFolder, boolean isCopy, Map<String, String> uidMap) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK_NEW;
|
||||||
|
|
||||||
|
int length = 4 + uidMap.keySet().size() + uidMap.values().size();
|
||||||
|
command.arguments = new String[length];
|
||||||
|
command.arguments[0] = srcFolder;
|
||||||
|
command.arguments[1] = destFolder;
|
||||||
|
command.arguments[2] = Boolean.toString(isCopy);
|
||||||
|
command.arguments[3] = Boolean.toString(true);
|
||||||
|
Set<String> strings = uidMap.keySet();
|
||||||
|
System.arraycopy(strings.toArray(new String[strings.size()]), 0, command.arguments, 4, uidMap.keySet().size());
|
||||||
|
Collection<String> values = uidMap.values();
|
||||||
|
System.arraycopy(values.toArray(new String[values.size()]), 0, command.arguments, 4 + uidMap.keySet().size(),
|
||||||
|
uidMap.values().size());
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueMoveOrCopyOld(String srcFolder, String destFolder, boolean isCopy, String uids[]) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_MOVE_OR_COPY_BULK;
|
||||||
|
|
||||||
|
int length = 3 + uids.length;
|
||||||
|
command.arguments = new String[length];
|
||||||
|
command.arguments[0] = srcFolder;
|
||||||
|
command.arguments[1] = destFolder;
|
||||||
|
command.arguments[2] = Boolean.toString(isCopy);
|
||||||
|
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueMoveOrCopyEvenOlder(String srcFolder, String destFolder, String uid, boolean isCopy) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_MOVE_OR_COPY;
|
||||||
|
|
||||||
|
command.arguments = new String[4];
|
||||||
|
command.arguments[0] = srcFolder;
|
||||||
|
command.arguments[1] = uid;
|
||||||
|
command.arguments[2] = destFolder;
|
||||||
|
command.arguments[3] = Boolean.toString(isCopy);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueSetFlagBulk(String folderName, boolean newState, Flag flag, String[] uids) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_SET_FLAG_BULK;
|
||||||
|
int length = 3 + uids.length;
|
||||||
|
command.arguments = new String[length];
|
||||||
|
command.arguments[0] = folderName;
|
||||||
|
command.arguments[1] = Boolean.toString(newState);
|
||||||
|
command.arguments[2] = flag.toString();
|
||||||
|
System.arraycopy(uids, 0, command.arguments, 3, uids.length);
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueSetFlagOld(String folderName, boolean newState, Flag flag, String uid) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_SET_FLAG;
|
||||||
|
command.arguments = new String[4];
|
||||||
|
command.arguments[0] = folderName;
|
||||||
|
command.arguments[1] = uid;
|
||||||
|
command.arguments[2] = Boolean.toString(newState);
|
||||||
|
command.arguments[3] = flag.toString();
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueExpunge(String folderName) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_EXPUNGE;
|
||||||
|
command.arguments = new String[1];
|
||||||
|
command.arguments[0] = folderName;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueEmptyTrash() {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_EMPTY_TRASH;
|
||||||
|
command.arguments = new String[0];
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
OldPendingCommand queueMarkAllMessagesRead(final String folder) {
|
||||||
|
OldPendingCommand command = new OldPendingCommand();
|
||||||
|
command.command = PENDING_COMMAND_MARK_ALL_AS_READ;
|
||||||
|
command.arguments = new String[] { folder };
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getColumnList(SQLiteDatabase db, String table) {
|
||||||
|
List<String> columns = new ArrayList<>();
|
||||||
|
Cursor columnCursor = db.rawQuery("PRAGMA table_info(" + table + ")", null);
|
||||||
|
try {
|
||||||
|
while (columnCursor.moveToNext()) {
|
||||||
|
columns.add(columnCursor.getString(1));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
columnCursor.close();
|
||||||
|
}
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SQLiteDatabase createV59Table() {
|
||||||
|
SQLiteDatabase database = SQLiteDatabase.create(null);
|
||||||
|
database.execSQL("CREATE TABLE pending_commands (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"command TEXT, " +
|
||||||
|
"arguments TEXT" +
|
||||||
|
")");
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue