Loading app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +6 −33 Original line number Diff line number Diff line Loading @@ -60,7 +60,6 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.FolderClass; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessageDownloadState; import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.ServerSettings; Loading Loading @@ -401,48 +400,22 @@ public class MessagingController { /** * Find all messages in any local account which match the query 'query' */ public void searchLocalMessages(final LocalSearch search, final MessagingListener listener) { threadPool.execute(new Runnable() { @Override public void run() { searchLocalMessagesSynchronous(search, listener); } }); } @VisibleForTesting void searchLocalMessagesSynchronous(final LocalSearch search, final MessagingListener listener) { public List<LocalMessage> searchLocalMessages(final LocalSearch search) { List<Account> searchAccounts = getAccountsFromLocalSearch(search, preferences); for (final Account account : searchAccounts) { // Collecting statistics of the search result MessageRetrievalListener<LocalMessage> retrievalListener = new MessageRetrievalListener<LocalMessage>() { @Override public void messageFinished(LocalMessage message) { if (!isMessageSuppressed(message)) { List<LocalMessage> messages = new ArrayList<>(); messages.add(message); if (listener != null) { listener.listLocalMessagesAddMessages(account, null, messages); } } } }; // build and do the query in the localstore for (final Account account : searchAccounts) { try { LocalStore localStore = localStoreProvider.getInstance(account); localStore.searchForMessages(retrievalListener, search); List<LocalMessage> localMessages = localStore.searchForMessages(search); messages.addAll(localMessages); } catch (Exception e) { Timber.e(e); } } if (listener != null) { listener.listLocalMessagesFinished(); } return messages; } public Future<?> searchRemoteMessages(String acctUuid, long folderId, String query, Set<Flag> requiredFlags, Loading app/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java +3 −5 Original line number Diff line number Diff line Loading @@ -359,9 +359,7 @@ public class LocalStore { }); } public List<LocalMessage> searchForMessages(MessageRetrievalListener<LocalMessage> retrievalListener, LocalSearch search) throws MessagingException { public List<LocalMessage> searchForMessages(LocalSearch search) throws MessagingException { StringBuilder query = new StringBuilder(); List<String> queryArgs = new ArrayList<>(); SqlQueryBuilder.buildWhereClause(account, search.getConditions(), query, queryArgs); Loading @@ -382,7 +380,7 @@ public class LocalStore { Timber.d("Query = %s", sqlQuery); return getMessages(retrievalListener, null, sqlQuery, selectionArgs); return getMessages(null, null, sqlQuery, selectionArgs); } /* Loading Loading @@ -443,7 +441,7 @@ public class LocalStore { LocalSearch search = new LocalSearch(); search.and(SearchField.THREAD_ID, rootIdString, Attribute.EQUALS); return searchForMessages(null, search); return searchForMessages(search); } public AttachmentInfo getAttachmentInfo(final String attachmentId) throws MessagingException { Loading app/core/src/test/java/com/fsck/k9/controller/MessagingControllerTest.java +5 −19 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import org.mockito.stubbing.Answer; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowLog; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.eq; Loading Loading @@ -178,32 +179,17 @@ public class MessagingControllerTest extends K9RobolectricTest { } @Test public void searchLocalMessagesSynchronous_shouldCallSearchForMessagesOnLocalStore() throws Exception { when(search.searchAllAccounts()).thenReturn(true); when(search.getAccountUuids()).thenReturn(new String[0]); controller.searchLocalMessagesSynchronous(search, listener); verify(localStore).searchForMessages(nullable(MessageRetrievalListener.class), eq(search)); } @Test public void searchLocalMessagesSynchronous_shouldNotifyWhenStoreFinishesRetrievingAMessage() public void searchLocalMessages_shouldIgnoreExceptions() throws Exception { LocalMessage localMessage = mock(LocalMessage.class); when(localMessage.getFolder()).thenReturn(localFolder); when(search.searchAllAccounts()).thenReturn(true); when(search.getAccountUuids()).thenReturn(new String[0]); when(localStore.searchForMessages(nullable(MessageRetrievalListener.class), eq(search))) .thenThrow(new MessagingException("Test")); when(localStore.searchForMessages(search)).thenThrow(new MessagingException("Test")); controller.searchLocalMessagesSynchronous(search, listener); List<LocalMessage> messages = controller.searchLocalMessages(search); verify(localStore).searchForMessages(messageRetrievalListenerCaptor.capture(), eq(search)); messageRetrievalListenerCaptor.getValue().messageFinished(localMessage); verify(listener).listLocalMessagesAddMessages(eq(account), eq((String) null), eq(Collections.singletonList(localMessage))); assertThat(messages).isEmpty(); } private void setupRemoteSearch() throws Exception { Loading app/k9mail/src/main/java/com/fsck/k9/external/MessageProvider.java +16 −40 Original line number Diff line number Diff line Loading @@ -478,16 +478,12 @@ public class MessageProvider extends ContentProvider { } protected MatrixCursor getMessages(String[] projection) throws InterruptedException { BlockingQueue<List<MessageInfoHolder>> queue = new SynchronousQueue<>(); // new code for integrated inbox, only execute this once as it will be processed afterwards via the listener SearchAccount integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(); MessagingController msgController = MessagingController.getInstance(getContext()); msgController.searchLocalMessages(integratedInboxAccount.getRelatedSearch(), new MessageInfoHolderRetrieverListener(queue)); List<MessageInfoHolder> holders = queue.take(); List<LocalMessage> messages = msgController.searchLocalMessages(integratedInboxAccount.getRelatedSearch()); List<MessageInfoHolder> holders = convertToMessageInfoHolder(messages); // TODO add sort order parameter Collections.sort(holders, new ReverseDateComparator()); Loading Loading @@ -521,6 +517,20 @@ public class MessageProvider extends ContentProvider { return cursor; } private List<MessageInfoHolder> convertToMessageInfoHolder(List<LocalMessage> messages) { List<MessageInfoHolder> holders = new ArrayList<>(); Context context = getContext(); for (LocalMessage message : messages) { Account messageAccount = message.getAccount(); MessageInfoHolder messageInfoHolder = MessageInfoHolder.create(context, message, messageAccount); holders.add(messageInfoHolder); } return holders; } protected LinkedHashMap<String, FieldExtractor<MessageInfoHolder, ?>> resolveMessageExtractors( String[] projection, int count) { LinkedHashMap<String, FieldExtractor<MessageInfoHolder, ?>> extractors = new LinkedHashMap<>(); Loading Loading @@ -1033,38 +1043,4 @@ public class MessageProvider extends ContentProvider { return wrapped; } } /** * Synchronized listener used to retrieve {@link MessageInfoHolder}s using a given {@link BlockingQueue}. */ protected class MessageInfoHolderRetrieverListener extends SimpleMessagingListener { private final BlockingQueue<List<MessageInfoHolder>> queue; private List<MessageInfoHolder> holders = new ArrayList<>(); public MessageInfoHolderRetrieverListener(BlockingQueue<List<MessageInfoHolder>> queue) { this.queue = queue; } @Override public void listLocalMessagesAddMessages(Account account, String folderServerId, List<LocalMessage> messages) { Context context = getContext(); for (LocalMessage message : messages) { Account messageAccount = message.getAccount(); MessageInfoHolder messageInfoHolder = MessageInfoHolder.create(context, message, messageAccount); holders.add(messageInfoHolder); } } @Override public void listLocalMessagesFinished() { try { queue.put(holders); } catch (InterruptedException e) { Timber.e(e, "Unable to return message list back to caller"); } } } } Loading
app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +6 −33 Original line number Diff line number Diff line Loading @@ -60,7 +60,6 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.FolderClass; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessageDownloadState; import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.ServerSettings; Loading Loading @@ -401,48 +400,22 @@ public class MessagingController { /** * Find all messages in any local account which match the query 'query' */ public void searchLocalMessages(final LocalSearch search, final MessagingListener listener) { threadPool.execute(new Runnable() { @Override public void run() { searchLocalMessagesSynchronous(search, listener); } }); } @VisibleForTesting void searchLocalMessagesSynchronous(final LocalSearch search, final MessagingListener listener) { public List<LocalMessage> searchLocalMessages(final LocalSearch search) { List<Account> searchAccounts = getAccountsFromLocalSearch(search, preferences); for (final Account account : searchAccounts) { // Collecting statistics of the search result MessageRetrievalListener<LocalMessage> retrievalListener = new MessageRetrievalListener<LocalMessage>() { @Override public void messageFinished(LocalMessage message) { if (!isMessageSuppressed(message)) { List<LocalMessage> messages = new ArrayList<>(); messages.add(message); if (listener != null) { listener.listLocalMessagesAddMessages(account, null, messages); } } } }; // build and do the query in the localstore for (final Account account : searchAccounts) { try { LocalStore localStore = localStoreProvider.getInstance(account); localStore.searchForMessages(retrievalListener, search); List<LocalMessage> localMessages = localStore.searchForMessages(search); messages.addAll(localMessages); } catch (Exception e) { Timber.e(e); } } if (listener != null) { listener.listLocalMessagesFinished(); } return messages; } public Future<?> searchRemoteMessages(String acctUuid, long folderId, String query, Set<Flag> requiredFlags, Loading
app/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java +3 −5 Original line number Diff line number Diff line Loading @@ -359,9 +359,7 @@ public class LocalStore { }); } public List<LocalMessage> searchForMessages(MessageRetrievalListener<LocalMessage> retrievalListener, LocalSearch search) throws MessagingException { public List<LocalMessage> searchForMessages(LocalSearch search) throws MessagingException { StringBuilder query = new StringBuilder(); List<String> queryArgs = new ArrayList<>(); SqlQueryBuilder.buildWhereClause(account, search.getConditions(), query, queryArgs); Loading @@ -382,7 +380,7 @@ public class LocalStore { Timber.d("Query = %s", sqlQuery); return getMessages(retrievalListener, null, sqlQuery, selectionArgs); return getMessages(null, null, sqlQuery, selectionArgs); } /* Loading Loading @@ -443,7 +441,7 @@ public class LocalStore { LocalSearch search = new LocalSearch(); search.and(SearchField.THREAD_ID, rootIdString, Attribute.EQUALS); return searchForMessages(null, search); return searchForMessages(search); } public AttachmentInfo getAttachmentInfo(final String attachmentId) throws MessagingException { Loading
app/core/src/test/java/com/fsck/k9/controller/MessagingControllerTest.java +5 −19 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import org.mockito.stubbing.Answer; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowLog; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.eq; Loading Loading @@ -178,32 +179,17 @@ public class MessagingControllerTest extends K9RobolectricTest { } @Test public void searchLocalMessagesSynchronous_shouldCallSearchForMessagesOnLocalStore() throws Exception { when(search.searchAllAccounts()).thenReturn(true); when(search.getAccountUuids()).thenReturn(new String[0]); controller.searchLocalMessagesSynchronous(search, listener); verify(localStore).searchForMessages(nullable(MessageRetrievalListener.class), eq(search)); } @Test public void searchLocalMessagesSynchronous_shouldNotifyWhenStoreFinishesRetrievingAMessage() public void searchLocalMessages_shouldIgnoreExceptions() throws Exception { LocalMessage localMessage = mock(LocalMessage.class); when(localMessage.getFolder()).thenReturn(localFolder); when(search.searchAllAccounts()).thenReturn(true); when(search.getAccountUuids()).thenReturn(new String[0]); when(localStore.searchForMessages(nullable(MessageRetrievalListener.class), eq(search))) .thenThrow(new MessagingException("Test")); when(localStore.searchForMessages(search)).thenThrow(new MessagingException("Test")); controller.searchLocalMessagesSynchronous(search, listener); List<LocalMessage> messages = controller.searchLocalMessages(search); verify(localStore).searchForMessages(messageRetrievalListenerCaptor.capture(), eq(search)); messageRetrievalListenerCaptor.getValue().messageFinished(localMessage); verify(listener).listLocalMessagesAddMessages(eq(account), eq((String) null), eq(Collections.singletonList(localMessage))); assertThat(messages).isEmpty(); } private void setupRemoteSearch() throws Exception { Loading
app/k9mail/src/main/java/com/fsck/k9/external/MessageProvider.java +16 −40 Original line number Diff line number Diff line Loading @@ -478,16 +478,12 @@ public class MessageProvider extends ContentProvider { } protected MatrixCursor getMessages(String[] projection) throws InterruptedException { BlockingQueue<List<MessageInfoHolder>> queue = new SynchronousQueue<>(); // new code for integrated inbox, only execute this once as it will be processed afterwards via the listener SearchAccount integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(); MessagingController msgController = MessagingController.getInstance(getContext()); msgController.searchLocalMessages(integratedInboxAccount.getRelatedSearch(), new MessageInfoHolderRetrieverListener(queue)); List<MessageInfoHolder> holders = queue.take(); List<LocalMessage> messages = msgController.searchLocalMessages(integratedInboxAccount.getRelatedSearch()); List<MessageInfoHolder> holders = convertToMessageInfoHolder(messages); // TODO add sort order parameter Collections.sort(holders, new ReverseDateComparator()); Loading Loading @@ -521,6 +517,20 @@ public class MessageProvider extends ContentProvider { return cursor; } private List<MessageInfoHolder> convertToMessageInfoHolder(List<LocalMessage> messages) { List<MessageInfoHolder> holders = new ArrayList<>(); Context context = getContext(); for (LocalMessage message : messages) { Account messageAccount = message.getAccount(); MessageInfoHolder messageInfoHolder = MessageInfoHolder.create(context, message, messageAccount); holders.add(messageInfoHolder); } return holders; } protected LinkedHashMap<String, FieldExtractor<MessageInfoHolder, ?>> resolveMessageExtractors( String[] projection, int count) { LinkedHashMap<String, FieldExtractor<MessageInfoHolder, ?>> extractors = new LinkedHashMap<>(); Loading Loading @@ -1033,38 +1043,4 @@ public class MessageProvider extends ContentProvider { return wrapped; } } /** * Synchronized listener used to retrieve {@link MessageInfoHolder}s using a given {@link BlockingQueue}. */ protected class MessageInfoHolderRetrieverListener extends SimpleMessagingListener { private final BlockingQueue<List<MessageInfoHolder>> queue; private List<MessageInfoHolder> holders = new ArrayList<>(); public MessageInfoHolderRetrieverListener(BlockingQueue<List<MessageInfoHolder>> queue) { this.queue = queue; } @Override public void listLocalMessagesAddMessages(Account account, String folderServerId, List<LocalMessage> messages) { Context context = getContext(); for (LocalMessage message : messages) { Account messageAccount = message.getAccount(); MessageInfoHolder messageInfoHolder = MessageInfoHolder.create(context, message, messageAccount); holders.add(messageInfoHolder); } } @Override public void listLocalMessagesFinished() { try { queue.put(holders); } catch (InterruptedException e) { Timber.e(e, "Unable to return message list back to caller"); } } } }