messageview: move all loading logic into MessageLoaderHelper (breaks MessageCompose)
This commit is contained in:
parent
0df44a1457
commit
b72dba67df
9 changed files with 575 additions and 345 deletions
|
@ -1954,21 +1954,19 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageForViewFinished(Account account, String folder, String uid, LocalMessage message) {
|
||||
public void loadMessageForViewFinished(Account account, String folder, String uid) {
|
||||
if (mMessageReference == null || !mMessageReference.getUid().equals(uid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
||||
final LocalMessage message) {
|
||||
if (mMessageReference == null || !mMessageReference.getUid().equals(uid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LocalMessage message = null; // TODO this isn't working at the moment!
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
|
@ -0,0 +1,425 @@
|
|||
package com.fsck.k9.activity;
|
||||
|
||||
|
||||
import android.app.FragmentManager;
|
||||
import android.app.LoaderManager;
|
||||
import android.app.LoaderManager.LoaderCallbacks;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.Loader;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.helper.RetainFragment;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoCallback;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoHelper;
|
||||
import com.fsck.k9.ui.message.LocalMessageExtractorLoader;
|
||||
import com.fsck.k9.ui.message.LocalMessageLoader;
|
||||
|
||||
|
||||
/** This class is responsible for loading a message start to finish, and
|
||||
* retaining or reloading the loading state on configuration changes.
|
||||
*
|
||||
* In particular, it takes care of the following:
|
||||
* - load raw message data from the database, using LocalMessageLoader
|
||||
* - download partial message content if it is missing using MessagingController
|
||||
* - apply crypto operations if applicable, using MessageCryptoHelper
|
||||
* - extract MessageViewInfo from the message and crypto data using DecodeMessageLoader
|
||||
* - download complete message content for partially downloaded messages if requested
|
||||
*
|
||||
* No state is retained in this object itself. Instead, state is stored in the
|
||||
* message loaders and the MessageCryptoHelper which is stored in a
|
||||
* RetainFragment. The public interface is intended for use by an Activity or
|
||||
* Fragment, which should construct a new instance of this class in onCreate,
|
||||
* then call asyncStartOrResumeLoadingMessage to start or resume loading the
|
||||
* message, receiving callbacks when it is loaded.
|
||||
*
|
||||
* When the Activity or Fragment is ultimately destroyed, it should call
|
||||
* onDestroy, which stops loading and deletes all state kept in loaders and
|
||||
* fragments by this object. If it is only destroyed for a configuration
|
||||
* change, it should call onDestroyChangingConfigurations, which cancels any
|
||||
* further callbacks from this object but retains the loading state to resume
|
||||
* from at the next call to asyncStartOrResumeLoadingMessage.
|
||||
*
|
||||
* If the message is already loaded, a call to asyncStartOrResumeLoadingMessage
|
||||
* will typically load by starting the decode message loader, retrieving the
|
||||
* already cached LocalMessage. This message will be passed to the retained
|
||||
* CryptoMessageHelper instance, returning the already cached
|
||||
* MessageCryptoAnnotations. These two objects will be checked against the
|
||||
* retained DecodeMessageLoader, returning the final result. At each
|
||||
* intermediate step, the input of the respective loaders will be checked for
|
||||
* consistency, reloading if there is a mismatch.
|
||||
*
|
||||
*/
|
||||
public class MessageLoaderHelper {
|
||||
private static final int LOCAL_MESSAGE_LOADER_ID = 1;
|
||||
private static final int DECODE_MESSAGE_LOADER_ID = 2;
|
||||
|
||||
|
||||
// injected state
|
||||
private final Context context;
|
||||
private final FragmentManager fragmentManager;
|
||||
private final LoaderManager loaderManager;
|
||||
@Nullable // may be cleared
|
||||
private MessageLoaderCallbacks callback;
|
||||
|
||||
|
||||
// transient state
|
||||
private MessageReference messageReference;
|
||||
private Account account;
|
||||
|
||||
private LocalMessage localMessage;
|
||||
private MessageCryptoAnnotations messageCryptoAnnotations;
|
||||
|
||||
private MessageCryptoHelper messageCryptoHelper;
|
||||
|
||||
|
||||
public MessageLoaderHelper(Context context, LoaderManager loaderManager, FragmentManager fragmentManager,
|
||||
@NonNull MessageLoaderCallbacks callback) {
|
||||
this.context = context;
|
||||
this.loaderManager = loaderManager;
|
||||
this.fragmentManager = fragmentManager;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
|
||||
// public interface
|
||||
|
||||
@UiThread
|
||||
public void asyncStartOrResumeLoadingMessage(MessageReference messageReference) {
|
||||
this.messageReference = messageReference;
|
||||
this.account = Preferences.getPreferences(context).getAccount(messageReference.getAccountUuid());
|
||||
|
||||
startOrResumeLocalMessageLoader();
|
||||
}
|
||||
|
||||
/** Cancels all loading processes, prevents future callbacks, and destroys all loading state. */
|
||||
@UiThread
|
||||
public void onDestroy() {
|
||||
if (messageCryptoHelper != null) {
|
||||
messageCryptoHelper.cancelIfRunning();
|
||||
}
|
||||
|
||||
callback = null;
|
||||
}
|
||||
|
||||
/** Prevents future callbacks, but retains loading state to pick up from in a call to
|
||||
* asyncStartOrResumeLoadingMessage in a new instance of this class. */
|
||||
@UiThread
|
||||
public void onDestroyChangingConfigurations() {
|
||||
if (messageCryptoHelper != null) {
|
||||
messageCryptoHelper.detachCallback();
|
||||
}
|
||||
|
||||
callback = null;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void downloadCompleteMessage() {
|
||||
if (localMessage.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
startDownloadingMessageBody(true);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void restartMessageCryptoProcessing() {
|
||||
cancelAndClearCryptoOperation();
|
||||
cancelAndClearDecodeLoader();
|
||||
startOrResumeCryptoOperation();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
messageCryptoHelper.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
|
||||
// load from database
|
||||
|
||||
private void startOrResumeLocalMessageLoader() {
|
||||
LocalMessageLoader loader =
|
||||
(LocalMessageLoader) loaderManager.<LocalMessage>getLoader(LOCAL_MESSAGE_LOADER_ID);
|
||||
boolean isLoaderStale = (loader == null) || !loader.isCreatedFor(messageReference);
|
||||
|
||||
if (isLoaderStale) {
|
||||
Log.d(K9.LOG_TAG, "Creating new local message loader");
|
||||
cancelAndClearCryptoOperation();
|
||||
cancelAndClearDecodeLoader();
|
||||
loaderManager.restartLoader(LOCAL_MESSAGE_LOADER_ID, null, localMessageLoaderCallback);
|
||||
} else {
|
||||
Log.d(K9.LOG_TAG, "Reusing local message loader");
|
||||
loaderManager.initLoader(LOCAL_MESSAGE_LOADER_ID, null, localMessageLoaderCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void onLoadMessageFromDatabaseFinished() {
|
||||
if (callback == null) {
|
||||
throw new IllegalStateException("unexpected call when callback is already detached");
|
||||
}
|
||||
|
||||
callback.onMessageDataLoadFinished(localMessage);
|
||||
|
||||
if (localMessage.isBodyMissing()) {
|
||||
startDownloadingMessageBody(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (account.isOpenPgpProviderConfigured()) {
|
||||
startOrResumeCryptoOperation();
|
||||
return;
|
||||
}
|
||||
|
||||
startOrResumeDecodeMessage();
|
||||
}
|
||||
|
||||
private void onLoadMessageFromDatabaseFailed() {
|
||||
if (callback == null) {
|
||||
throw new IllegalStateException("unexpected call when callback is already detached");
|
||||
}
|
||||
callback.onMessageDataLoadFailed();
|
||||
}
|
||||
|
||||
private void cancelAndClearLocalMessageLoader() {
|
||||
loaderManager.destroyLoader(LOCAL_MESSAGE_LOADER_ID);
|
||||
}
|
||||
|
||||
private LoaderCallbacks<LocalMessage> localMessageLoaderCallback = new LoaderCallbacks<LocalMessage>() {
|
||||
@Override
|
||||
public Loader<LocalMessage> onCreateLoader(int id, Bundle args) {
|
||||
if (id != LOCAL_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message loader id");
|
||||
}
|
||||
|
||||
return new LocalMessageLoader(context, MessagingController.getInstance(context), account, messageReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<LocalMessage> loader, LocalMessage message) {
|
||||
if (loader.getId() != LOCAL_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message loader id");
|
||||
}
|
||||
|
||||
localMessage = message;
|
||||
if (message == null) {
|
||||
onLoadMessageFromDatabaseFailed();
|
||||
} else {
|
||||
onLoadMessageFromDatabaseFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<LocalMessage> loader) {
|
||||
if (loader.getId() != LOCAL_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message loader id");
|
||||
}
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// process with crypto helper
|
||||
|
||||
private void startOrResumeCryptoOperation() {
|
||||
RetainFragment<MessageCryptoHelper> retainCryptoHelperFragment = getMessageCryptoHelperRetainFragment();
|
||||
if (retainCryptoHelperFragment.hasData()) {
|
||||
messageCryptoHelper = retainCryptoHelperFragment.getData();
|
||||
} else {
|
||||
messageCryptoHelper = new MessageCryptoHelper(context, account.getOpenPgpProvider());
|
||||
retainCryptoHelperFragment.setData(messageCryptoHelper);
|
||||
}
|
||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(localMessage, messageCryptoCallback);
|
||||
}
|
||||
|
||||
private void cancelAndClearCryptoOperation() {
|
||||
RetainFragment<MessageCryptoHelper> retainCryptoHelperFragment = getMessageCryptoHelperRetainFragment();
|
||||
if (retainCryptoHelperFragment != null) {
|
||||
if (retainCryptoHelperFragment.hasData()) {
|
||||
messageCryptoHelper = retainCryptoHelperFragment.getData();
|
||||
messageCryptoHelper.cancelIfRunning();
|
||||
messageCryptoHelper = null;
|
||||
}
|
||||
retainCryptoHelperFragment.clearAndRemove(fragmentManager);
|
||||
}
|
||||
}
|
||||
|
||||
private RetainFragment<MessageCryptoHelper> getMessageCryptoHelperRetainFragment() {
|
||||
return RetainFragment.findOrCreate(fragmentManager, "crypto_helper_" + messageReference.hashCode());
|
||||
}
|
||||
|
||||
private MessageCryptoCallback messageCryptoCallback = new MessageCryptoCallback() {
|
||||
@Override
|
||||
public void onCryptoHelperProgress(int current, int max) {
|
||||
if (callback == null) {
|
||||
throw new IllegalStateException("unexpected call when callback is already detached");
|
||||
}
|
||||
|
||||
callback.setLoadingProgress(current, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationsFinished(MessageCryptoAnnotations annotations) {
|
||||
if (callback == null) {
|
||||
throw new IllegalStateException("unexpected call when callback is already detached");
|
||||
}
|
||||
|
||||
messageCryptoAnnotations = annotations;
|
||||
startOrResumeDecodeMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPendingIntentForCryptoHelper(IntentSender si, int requestCode, Intent fillIntent,
|
||||
int flagsMask, int flagValues, int extraFlags) {
|
||||
if (callback == null) {
|
||||
throw new IllegalStateException("unexpected call when callback is already detached");
|
||||
}
|
||||
|
||||
callback.startIntentSenderForMessageLoaderHelper(si, requestCode, fillIntent,
|
||||
flagsMask, flagValues, extraFlags);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// decode message
|
||||
|
||||
private void startOrResumeDecodeMessage() {
|
||||
LocalMessageExtractorLoader loader =
|
||||
(LocalMessageExtractorLoader) loaderManager.<MessageViewInfo>getLoader(DECODE_MESSAGE_LOADER_ID);
|
||||
boolean isLoaderStale = (loader == null) || !loader.isCreatedFor(localMessage, messageCryptoAnnotations);
|
||||
|
||||
if (isLoaderStale) {
|
||||
Log.d(K9.LOG_TAG, "Creating new decode message loader");
|
||||
loaderManager.restartLoader(DECODE_MESSAGE_LOADER_ID, null, decodeMessageLoaderCallback);
|
||||
} else {
|
||||
Log.d(K9.LOG_TAG, "Reusing decode message loader");
|
||||
loaderManager.initLoader(DECODE_MESSAGE_LOADER_ID, null, decodeMessageLoaderCallback);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDecodeMessageFinished(MessageViewInfo messageViewInfo) {
|
||||
if (callback == null) {
|
||||
throw new IllegalStateException("unexpected call when callback is already detached");
|
||||
}
|
||||
|
||||
if (messageViewInfo == null) {
|
||||
callback.onMessageViewInfoLoadFailed(localMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.onMessageViewInfoLoadFinished(localMessage, messageViewInfo);
|
||||
}
|
||||
|
||||
private void cancelAndClearDecodeLoader() {
|
||||
loaderManager.destroyLoader(DECODE_MESSAGE_LOADER_ID);
|
||||
}
|
||||
|
||||
private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new LoaderCallbacks<MessageViewInfo>() {
|
||||
@Override
|
||||
public Loader<MessageViewInfo> onCreateLoader(int id, Bundle args) {
|
||||
if (id != DECODE_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message decoder id");
|
||||
}
|
||||
return new LocalMessageExtractorLoader(context, localMessage, messageCryptoAnnotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<MessageViewInfo> loader, MessageViewInfo messageViewInfo) {
|
||||
if (loader.getId() != DECODE_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message decoder id");
|
||||
}
|
||||
onDecodeMessageFinished(messageViewInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<MessageViewInfo> loader) {
|
||||
if (loader.getId() != DECODE_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message decoder id");
|
||||
}
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// download missing body
|
||||
|
||||
private void startDownloadingMessageBody(boolean downloadComplete) {
|
||||
if (downloadComplete) {
|
||||
MessagingController.getInstance(context).loadMessageRemote(
|
||||
account, messageReference.getFolderName(), messageReference.getUid(), downloadMessageListener);
|
||||
} else {
|
||||
MessagingController.getInstance(context).loadMessageRemotePartial(
|
||||
account, messageReference.getFolderName(), messageReference.getUid(), downloadMessageListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMessageDownloadFinished() {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelAndClearLocalMessageLoader();
|
||||
cancelAndClearDecodeLoader();
|
||||
cancelAndClearCryptoOperation();
|
||||
|
||||
startOrResumeLocalMessageLoader();
|
||||
}
|
||||
|
||||
private void onDownloadMessageFailed(final Throwable t) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t instanceof IllegalArgumentException) {
|
||||
callback.onDownloadErrorMessageNotFound();
|
||||
} else {
|
||||
callback.onDownloadErrorNetworkError();
|
||||
}
|
||||
}
|
||||
|
||||
MessagingListener downloadMessageListener = new MessagingListener() {
|
||||
@Override
|
||||
public void loadMessageRemoteFinished(Account account, String folder, String uid) {
|
||||
onMessageDownloadFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageRemoteFailed(Account account, String folder, String uid, final Throwable t) {
|
||||
onDownloadMessageFailed(t);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// callback interface
|
||||
|
||||
public interface MessageLoaderCallbacks {
|
||||
void onMessageDataLoadFinished(LocalMessage message);
|
||||
void onMessageDataLoadFailed();
|
||||
|
||||
void onMessageViewInfoLoadFinished(LocalMessage localMessage, MessageViewInfo messageViewInfo);
|
||||
void onMessageViewInfoLoadFailed(LocalMessage localMessage);
|
||||
|
||||
void setLoadingProgress(int current, int max);
|
||||
|
||||
void startIntentSenderForMessageLoaderHelper(IntentSender si, int requestCode, Intent fillIntent, int flagsMask,
|
||||
int flagValues, int extraFlags);
|
||||
|
||||
void onDownloadErrorMessageNotFound();
|
||||
void onDownloadErrorNetworkError();
|
||||
}
|
||||
|
||||
}
|
|
@ -2696,28 +2696,28 @@ public class MessagingController implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
public void loadMessagePartialForViewRemote(final Account account, final String folder,
|
||||
public void loadMessageRemotePartial(final Account account, final String folder,
|
||||
final String uid, final MessagingListener listener) {
|
||||
put("loadMessageForViewRemote", listener, new Runnable() {
|
||||
put("loadMessageRemotePartial", listener, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadMessageForViewRemoteSynchronous(account, folder, uid, listener, true);
|
||||
loadMessageRemoteSynchronous(account, folder, uid, listener, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//TODO: Fix the callback mess. See GH-782
|
||||
public void loadMessageForViewRemote(final Account account, final String folder,
|
||||
public void loadMessageRemote(final Account account, final String folder,
|
||||
final String uid, final MessagingListener listener) {
|
||||
put("loadMessageForViewRemote", listener, new Runnable() {
|
||||
put("loadMessageRemote", listener, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadMessageForViewRemoteSynchronous(account, folder, uid, listener, false);
|
||||
loadMessageRemoteSynchronous(account, folder, uid, listener, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean loadMessageForViewRemoteSynchronous(final Account account, final String folder,
|
||||
public boolean loadMessageRemoteSynchronous(final Account account, final String folder,
|
||||
final String uid, final MessagingListener listener, final boolean loadPartialFromSearch) {
|
||||
Folder remoteFolder = null;
|
||||
LocalFolder localFolder = null;
|
||||
|
@ -2750,16 +2750,7 @@ public class MessagingController implements Runnable {
|
|||
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, false);
|
||||
}*/
|
||||
|
||||
if (message.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
/*
|
||||
* If the message has been synchronized since we were called we'll
|
||||
* just hand it back cause it's ready to go.
|
||||
*/
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
} else {
|
||||
if (!message.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
/*
|
||||
* At this point the message is not available, so we need to download it
|
||||
* fully if possible.
|
||||
|
@ -2784,36 +2775,25 @@ public class MessagingController implements Runnable {
|
|||
|
||||
message = localFolder.getMessage(uid);
|
||||
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
|
||||
if (!loadPartialFromSearch) {
|
||||
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that this message is now fully synched
|
||||
if (account.isMarkMessageAsReadOnView()) {
|
||||
message.setFlag(Flag.SEEN, true);
|
||||
}
|
||||
// Mark that this message is now fully synched
|
||||
if (account.isMarkMessageAsReadOnView()) {
|
||||
message.setFlag(Flag.SEEN, true);
|
||||
}
|
||||
|
||||
// now that we have the full message, refresh the headers
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewHeadersAvailable(account, folder, uid, message);
|
||||
l.loadMessageRemoteFinished(account, folder, uid);
|
||||
}
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewBodyAvailable(account, folder, uid, message);
|
||||
}
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewFinished(account, folder, uid, message);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewFailed(account, folder, uid, e);
|
||||
l.loadMessageRemoteFailed(account, folder, uid, e);
|
||||
}
|
||||
notifyUserIfCertificateProblem(account, e, true);
|
||||
addErrorMessage(account, null, e);
|
||||
|
@ -2847,17 +2827,12 @@ public class MessagingController implements Runnable {
|
|||
// TODO: limit by account.getMaximumAutoDownloadMessageSize().
|
||||
if (!message.isSet(Flag.X_DOWNLOADED_FULL) &&
|
||||
!message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
|
||||
if (loadMessageForViewRemoteSynchronous(account, folder, uid, listener, true)) {
|
||||
if (loadMessageRemoteSynchronous(account, folder, uid, listener, true)) {
|
||||
markMessageAsReadOnView(account, message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewHeadersAvailable(account, folder, uid, message);
|
||||
}
|
||||
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
|
@ -2865,11 +2840,7 @@ public class MessagingController implements Runnable {
|
|||
localFolder.close();
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewBodyAvailable(account, folder, uid, message);
|
||||
}
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.loadMessageForViewFinished(account, folder, uid, message);
|
||||
l.loadMessageForViewFinished(account, folder, uid);
|
||||
}
|
||||
markMessageAsReadOnView(account, message);
|
||||
|
||||
|
@ -3876,64 +3847,52 @@ public class MessagingController implements Runnable {
|
|||
return (account.getRemoteStore() instanceof Pop3Store);
|
||||
}
|
||||
|
||||
public void sendAlternate(final Context context, Account account, Message message) {
|
||||
public void sendAlternate(Context context, Account account, LocalMessage message) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "About to load message " + account.getDescription() + ":" + message.getFolder().getName()
|
||||
Log.d(K9.LOG_TAG, "Got message " + account.getDescription() + ":" + message.getFolder()
|
||||
+ ":" + message.getUid() + " for sendAlternate");
|
||||
|
||||
loadMessageForView(account, message.getFolder().getName(),
|
||||
message.getUid(), new MessagingListener() {
|
||||
@Override
|
||||
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
||||
LocalMessage message) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Got message " + account.getDescription() + ":" + folder
|
||||
+ ":" + message.getUid() + " for sendAlternate");
|
||||
|
||||
try {
|
||||
Intent msg = new Intent(Intent.ACTION_SEND);
|
||||
String quotedText = null;
|
||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (part == null) {
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null) {
|
||||
quotedText = MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
if (quotedText != null) {
|
||||
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_SUBJECT, message.getSubject());
|
||||
|
||||
Address[] from = message.getFrom();
|
||||
String[] senders = new String[from.length];
|
||||
for (int i = 0; i < from.length; i++) {
|
||||
senders[i] = from[i].toString();
|
||||
}
|
||||
msg.putExtra(Intents.Share.EXTRA_FROM, senders);
|
||||
|
||||
Address[] to = message.getRecipients(RecipientType.TO);
|
||||
String[] recipientsTo = new String[to.length];
|
||||
for (int i = 0; i < to.length; i++) {
|
||||
recipientsTo[i] = to[i].toString();
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_EMAIL, recipientsTo);
|
||||
|
||||
Address[] cc = message.getRecipients(RecipientType.CC);
|
||||
String[] recipientsCc = new String[cc.length];
|
||||
for (int i = 0; i < cc.length; i++) {
|
||||
recipientsCc[i] = cc[i].toString();
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_CC, recipientsCc);
|
||||
|
||||
msg.setType("text/plain");
|
||||
context.startActivity(Intent.createChooser(msg, context.getString(R.string.send_alternate_chooser_title)));
|
||||
} catch (MessagingException me) {
|
||||
Log.e(K9.LOG_TAG, "Unable to send email through alternate program", me);
|
||||
}
|
||||
try {
|
||||
Intent msg = new Intent(Intent.ACTION_SEND);
|
||||
String quotedText = null;
|
||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (part == null) {
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
});
|
||||
if (part != null) {
|
||||
quotedText = MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
if (quotedText != null) {
|
||||
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_SUBJECT, message.getSubject());
|
||||
|
||||
Address[] from = message.getFrom();
|
||||
String[] senders = new String[from.length];
|
||||
for (int i = 0; i < from.length; i++) {
|
||||
senders[i] = from[i].toString();
|
||||
}
|
||||
msg.putExtra(Intents.Share.EXTRA_FROM, senders);
|
||||
|
||||
Address[] to = message.getRecipients(RecipientType.TO);
|
||||
String[] recipientsTo = new String[to.length];
|
||||
for (int i = 0; i < to.length; i++) {
|
||||
recipientsTo[i] = to[i].toString();
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_EMAIL, recipientsTo);
|
||||
|
||||
Address[] cc = message.getRecipients(RecipientType.CC);
|
||||
String[] recipientsCc = new String[cc.length];
|
||||
for (int i = 0; i < cc.length; i++) {
|
||||
recipientsCc[i] = cc[i].toString();
|
||||
}
|
||||
msg.putExtra(Intent.EXTRA_CC, recipientsCc);
|
||||
|
||||
msg.setType("text/plain");
|
||||
context.startActivity(Intent.createChooser(msg, context.getString(R.string.send_alternate_chooser_title)));
|
||||
} catch (MessagingException me) {
|
||||
Log.e(K9.LOG_TAG, "Unable to send email through alternate program", me);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,27 +81,11 @@ public class MessagingListener {
|
|||
|
||||
public void synchronizeMailboxFailed(Account account, String folder, String message) {}
|
||||
|
||||
public void loadMessageRemoteFinished(Account account, String folder, String uid) {}
|
||||
|
||||
public void loadMessageForViewStarted(Account account, String folder, String uid) {}
|
||||
|
||||
public void loadMessageForViewHeadersAvailable(Account account, String folder, String uid,
|
||||
Message message) {}
|
||||
|
||||
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
|
||||
LocalMessage message) {}
|
||||
|
||||
public void loadMessageForViewFinished(Account account, String folder, String uid,
|
||||
LocalMessage message) {}
|
||||
|
||||
public void loadMessageForViewFailed(Account account, String folder, String uid,
|
||||
public void loadMessageRemoteFailed(Account account, String folder, String uid,
|
||||
Throwable t) {}
|
||||
|
||||
/**
|
||||
* Called when a message for view has been fully displayed on the screen.
|
||||
*/
|
||||
public void messageViewFinished() {}
|
||||
|
||||
|
||||
public void checkMailStarted(Context context, Account account) {}
|
||||
|
||||
public void checkMailFinished(Context context, Account account) {}
|
||||
|
|
|
@ -280,6 +280,7 @@ public class MessageCryptoHelper {
|
|||
}
|
||||
|
||||
public void cancelIfRunning() {
|
||||
detachCallback();
|
||||
isCancelled = true;
|
||||
if (cancelableBackgroundOperation != null) {
|
||||
cancelableBackgroundOperation.cancelOperation();
|
||||
|
@ -584,6 +585,9 @@ public class MessageCryptoHelper {
|
|||
throw new AssertionError("Callback may only be reattached for the same message!");
|
||||
}
|
||||
synchronized (callbackLock) {
|
||||
if (queuedResult != null) {
|
||||
Log.d(K9.LOG_TAG, "Returning cached result to reattached callback");
|
||||
}
|
||||
this.callback = callback;
|
||||
deliverResult();
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ package com.fsck.k9.ui.message;
|
|||
|
||||
import android.content.AsyncTaskLoader;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mailstore.MessageViewInfoExtractor;
|
||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||
|
@ -15,9 +17,11 @@ import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
|||
public class LocalMessageExtractorLoader extends AsyncTaskLoader<MessageViewInfo> {
|
||||
private final Message message;
|
||||
private MessageViewInfo messageViewInfo;
|
||||
@Nullable
|
||||
private MessageCryptoAnnotations annotations;
|
||||
|
||||
public LocalMessageExtractorLoader(Context context, Message message, MessageCryptoAnnotations annotations) {
|
||||
public LocalMessageExtractorLoader(
|
||||
Context context, Message message, @Nullable MessageCryptoAnnotations annotations) {
|
||||
super(context);
|
||||
this.message = message;
|
||||
this.annotations = annotations;
|
||||
|
@ -49,4 +53,8 @@ public class LocalMessageExtractorLoader extends AsyncTaskLoader<MessageViewInfo
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCreatedFor(LocalMessage localMessage, MessageCryptoAnnotations messageCryptoAnnotations) {
|
||||
return annotations == messageCryptoAnnotations && message.equals(localMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,4 +57,8 @@ public class LocalMessageLoader extends AsyncTaskLoader<LocalMessage> {
|
|||
private LocalMessage loadMessageFromDatabase() throws MessagingException {
|
||||
return controller.loadMessage(account, messageReference.getFolderName(), messageReference.getUid());
|
||||
}
|
||||
|
||||
public boolean isCreatedFor(MessageReference messageReference) {
|
||||
return this.messageReference.equals(messageReference);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,13 @@ import android.app.DialogFragment;
|
|||
import android.app.DownloadManager;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.LoaderManager;
|
||||
import android.app.LoaderManager.LoaderCallbacks;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.content.Loader;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
@ -36,25 +32,20 @@ import com.fsck.k9.K9;
|
|||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.ChooseFolder;
|
||||
import com.fsck.k9.activity.MessageLoaderHelper;
|
||||
import com.fsck.k9.activity.MessageLoaderHelper.MessageLoaderCallbacks;
|
||||
import com.fsck.k9.activity.MessageReference;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.fragment.ConfirmationDialogFragment;
|
||||
import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
|
||||
import com.fsck.k9.fragment.ProgressDialogFragment;
|
||||
import com.fsck.k9.helper.FileBrowserHelper;
|
||||
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
|
||||
import com.fsck.k9.helper.RetainFragment;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoCallback;
|
||||
import com.fsck.k9.ui.crypto.MessageCryptoHelper;
|
||||
import com.fsck.k9.ui.message.LocalMessageExtractorLoader;
|
||||
import com.fsck.k9.ui.message.LocalMessageLoader;
|
||||
import com.fsck.k9.ui.messageview.CryptoInfoDialog.OnClickShowCryptoKeyListener;
|
||||
import com.fsck.k9.ui.messageview.MessageCryptoPresenter.MessageCryptoMvpView;
|
||||
import com.fsck.k9.view.MessageCryptoDisplayStatus;
|
||||
|
@ -62,7 +53,7 @@ import com.fsck.k9.view.MessageHeader;
|
|||
|
||||
|
||||
public class MessageViewFragment extends Fragment implements ConfirmationDialogFragmentListener,
|
||||
AttachmentViewCallback, MessageCryptoCallback, MessageCryptoMvpView, OnClickShowCryptoKeyListener {
|
||||
AttachmentViewCallback, MessageCryptoMvpView, OnClickShowCryptoKeyListener {
|
||||
|
||||
private static final String ARG_REFERENCE = "reference";
|
||||
|
||||
|
@ -70,12 +61,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
|
||||
private static final int ACTIVITY_CHOOSE_DIRECTORY = 3;
|
||||
|
||||
private static final int LOCAL_MESSAGE_LOADER_ID = 1;
|
||||
private static final int DECODE_MESSAGE_LOADER_ID = 2;
|
||||
|
||||
public static final int REQUEST_MASK_CRYPTO_HELPER = 1 << 8;
|
||||
public static final int REQUEST_MASK_CRYPTO_PRESENTER = 2 << 8;
|
||||
private RetainFragment<MessageCryptoHelper> retainCryptoHelperFragment;
|
||||
public static final int REQUEST_MASK_LOADER_HELPER = (1 << 8);
|
||||
public static final int REQUEST_MASK_CRYPTO_PRESENTER = (1 << 9);
|
||||
|
||||
public static MessageViewFragment newInstance(MessageReference reference) {
|
||||
MessageViewFragment fragment = new MessageViewFragment();
|
||||
|
@ -92,12 +79,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
private Account mAccount;
|
||||
private MessageReference mMessageReference;
|
||||
private LocalMessage mMessage;
|
||||
private MessageCryptoAnnotations messageAnnotations;
|
||||
private MessagingController mController;
|
||||
private DownloadManager downloadManager;
|
||||
private Handler handler = new Handler();
|
||||
private DownloadMessageListener downloadMessageListener = new DownloadMessageListener();
|
||||
private MessageCryptoHelper messageCryptoHelper;
|
||||
private MessageLoaderHelper messageLoaderHelper;
|
||||
private MessageCryptoPresenter messageCryptoPresenter;
|
||||
|
||||
/**
|
||||
|
@ -117,8 +102,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
|
||||
private Context mContext;
|
||||
|
||||
private LoaderCallbacks<LocalMessage> localMessageLoaderCallback = new LocalMessageLoaderCallback();
|
||||
private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new DecodeMessageLoaderCallback();
|
||||
private AttachmentViewInfo currentAttachmentViewInfo;
|
||||
|
||||
@Override
|
||||
|
@ -146,6 +129,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
mController = MessagingController.getInstance(context);
|
||||
downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
messageCryptoPresenter = new MessageCryptoPresenter(this);
|
||||
messageLoaderHelper =
|
||||
new MessageLoaderHelper(context, getLoaderManager(), getFragmentManager(), messageLoaderCallbacks);
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
|
@ -156,25 +141,11 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
Activity activity = getActivity();
|
||||
boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
|
||||
if (isChangingConfigurations) {
|
||||
messageCryptoHelper.detachCallback();
|
||||
messageLoaderHelper.onDestroyChangingConfigurations();
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageCryptoHelper != null) {
|
||||
cancelAndClearMessageCryptoHelper();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void cancelAndClearMessageCryptoHelper() {
|
||||
if (messageCryptoHelper != null) {
|
||||
messageCryptoHelper.cancelIfRunning();
|
||||
messageCryptoHelper = null;
|
||||
}
|
||||
if (retainCryptoHelperFragment != null) {
|
||||
retainCryptoHelperFragment.clearAndRemove(getFragmentManager());
|
||||
retainCryptoHelperFragment = null;
|
||||
}
|
||||
messageLoaderHelper.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -199,7 +170,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
mMessageView.setOnDownloadButtonClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onDownloadRemainder();
|
||||
mMessageView.disableDownloadButton();
|
||||
messageLoaderHelper.downloadCompleteMessage();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -225,19 +197,17 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
}
|
||||
|
||||
mAccount = Preferences.getPreferences(getApplicationContext()).getAccount(mMessageReference.getAccountUuid());
|
||||
|
||||
startLoadingMessageFromDatabase();
|
||||
messageLoaderHelper.asyncStartOrResumeLoadingMessage(messageReference);
|
||||
|
||||
mFragmentListener.updateMenu();
|
||||
}
|
||||
|
||||
|
||||
public void onPendingIntentResult(int requestCode, int resultCode, Intent data) {
|
||||
if ((requestCode & REQUEST_MASK_CRYPTO_HELPER) == REQUEST_MASK_CRYPTO_HELPER) {
|
||||
if ((requestCode & REQUEST_MASK_LOADER_HELPER) == REQUEST_MASK_LOADER_HELPER) {
|
||||
hideKeyboard();
|
||||
|
||||
requestCode ^= REQUEST_MASK_CRYPTO_HELPER;
|
||||
messageCryptoHelper.onActivityResult(requestCode, resultCode, data);
|
||||
requestCode ^= REQUEST_MASK_LOADER_HELPER;
|
||||
messageLoaderHelper.onActivityResult(requestCode, resultCode, data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -259,95 +229,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
}
|
||||
}
|
||||
|
||||
private void startLoadingMessageFromDatabase() {
|
||||
getLoaderManager().initLoader(LOCAL_MESSAGE_LOADER_ID, null, localMessageLoaderCallback);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void onLoadMessageFromDatabaseFinished(LocalMessage message) {
|
||||
displayMessageHeader(message);
|
||||
mMessageView.setToLoadingState();
|
||||
|
||||
if (message.isBodyMissing()) {
|
||||
startDownloadingMessageBody();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAccount.isOpenPgpProviderConfigured()) {
|
||||
startOrResumeProcessingInCryptoHelper(message);
|
||||
return;
|
||||
}
|
||||
|
||||
startExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
private void startOrResumeProcessingInCryptoHelper(LocalMessage message) {
|
||||
retainCryptoHelperFragment =
|
||||
RetainFragment.findOrCreate(getFragmentManager(), "crypto_helper_" + message.hashCode());
|
||||
if (retainCryptoHelperFragment.hasData()) {
|
||||
messageCryptoHelper = retainCryptoHelperFragment.getData();
|
||||
} else {
|
||||
messageCryptoHelper = new MessageCryptoHelper(getActivity(), mAccount.getOpenPgpProvider());
|
||||
retainCryptoHelperFragment.setData(messageCryptoHelper);
|
||||
}
|
||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, this);
|
||||
}
|
||||
|
||||
private void onLoadMessageFromDatabaseFailed() {
|
||||
// mMessageView.showStatusMessage(mContext.getString(R.string.status_invalid_id_error));
|
||||
}
|
||||
|
||||
private void startDownloadingMessageBody() {
|
||||
mController.loadMessagePartialForViewRemote(
|
||||
mAccount, mMessageReference.getFolderName(), mMessageReference.getUid(), downloadMessageListener);
|
||||
}
|
||||
|
||||
private void onMessageDownloadFinished(LocalMessage message) {
|
||||
mMessage = message;
|
||||
|
||||
LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.destroyLoader(LOCAL_MESSAGE_LOADER_ID);
|
||||
loaderManager.destroyLoader(DECODE_MESSAGE_LOADER_ID);
|
||||
cancelAndClearMessageCryptoHelper();
|
||||
|
||||
onLoadMessageFromDatabaseFinished(mMessage);
|
||||
}
|
||||
|
||||
private void onDownloadMessageFailed(Throwable t) {
|
||||
mMessageView.enableDownloadButton();
|
||||
String errorMessage;
|
||||
if (t instanceof IllegalArgumentException) {
|
||||
errorMessage = mContext.getString(R.string.status_invalid_id_error);
|
||||
} else {
|
||||
errorMessage = mContext.getString(R.string.status_network_error);
|
||||
}
|
||||
Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoHelperProgress(int current, int max) {
|
||||
mMessageView.setLoadingProgress(current, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationsFinished(MessageCryptoAnnotations annotations) {
|
||||
this.messageAnnotations = annotations;
|
||||
startExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
private void startExtractingTextAndAttachments() {
|
||||
getLoaderManager().restartLoader(DECODE_MESSAGE_LOADER_ID, null, decodeMessageLoaderCallback);
|
||||
}
|
||||
|
||||
private void onDecodeMessageFinished(MessageViewInfo messageViewInfo) {
|
||||
if (messageViewInfo == null) {
|
||||
showUnableToDecodeError();
|
||||
messageViewInfo = MessageViewInfo.createWithErrorState(mMessage);
|
||||
}
|
||||
|
||||
showMessage(messageViewInfo);
|
||||
}
|
||||
|
||||
private void showUnableToDecodeError() {
|
||||
Context context = getActivity().getApplicationContext();
|
||||
Toast.makeText(context, R.string.message_view_toast_unable_to_display_message, Toast.LENGTH_SHORT).show();
|
||||
|
@ -572,15 +453,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
}
|
||||
}
|
||||
|
||||
private void onDownloadRemainder() {
|
||||
if (mMessage.isSet(Flag.X_DOWNLOADED_FULL)) {
|
||||
return;
|
||||
}
|
||||
mMessageView.disableDownloadButton();
|
||||
mController.loadMessageForViewRemote(mAccount, mMessageReference.getFolderName(), mMessageReference.getUid(),
|
||||
downloadMessageListener);
|
||||
}
|
||||
|
||||
private void setProgress(boolean enable) {
|
||||
if (mFragmentListener != null) {
|
||||
mFragmentListener.setProgress(enable);
|
||||
|
@ -793,21 +665,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
|
||||
@Override
|
||||
public void restartMessageCryptoProcessing() {
|
||||
cancelAndClearMessageCryptoHelper();
|
||||
mMessageView.setToLoadingState();
|
||||
startOrResumeProcessingInCryptoHelper(mMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPendingIntentForCryptoHelper(IntentSender si, int requestCode, Intent fillIntent,
|
||||
int flagsMask, int flagValues, int extraFlags) {
|
||||
requestCode |= REQUEST_MASK_CRYPTO_HELPER;
|
||||
try {
|
||||
getActivity().startIntentSenderForResult(
|
||||
si, requestCode, fillIntent, flagsMask, flagValues, extraFlags);
|
||||
} catch (SendIntentException e) {
|
||||
Log.e(K9.LOG_TAG, "Irrecoverable error calling PendingIntent!", e);
|
||||
}
|
||||
messageLoaderHelper.restartMessageCryptoProcessing();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -831,58 +690,72 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
return mInitialized ;
|
||||
}
|
||||
|
||||
class LocalMessageLoaderCallback implements LoaderCallbacks<LocalMessage> {
|
||||
@Override
|
||||
public Loader<LocalMessage> onCreateLoader(int id, Bundle args) {
|
||||
setProgress(true);
|
||||
return new LocalMessageLoader(mContext, mController, mAccount, mMessageReference);
|
||||
}
|
||||
|
||||
MessageLoaderCallbacks messageLoaderCallbacks = new MessageLoaderCallbacks() {
|
||||
@Override
|
||||
public void onLoadFinished(Loader<LocalMessage> loader, LocalMessage message) {
|
||||
setProgress(false);
|
||||
public void onMessageDataLoadFinished(LocalMessage message) {
|
||||
mMessage = message;
|
||||
if (message == null) {
|
||||
onLoadMessageFromDatabaseFailed();
|
||||
} else {
|
||||
onLoadMessageFromDatabaseFinished(message);
|
||||
}
|
||||
getLoaderManager().destroyLoader(LOCAL_MESSAGE_LOADER_ID);
|
||||
|
||||
displayMessageHeader(message);
|
||||
mMessageView.setToLoadingState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<LocalMessage> loader) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
class DecodeMessageLoaderCallback implements LoaderCallbacks<MessageViewInfo> {
|
||||
@Override
|
||||
public Loader<MessageViewInfo> onCreateLoader(int id, Bundle args) {
|
||||
if (id != DECODE_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message decoder id");
|
||||
}
|
||||
setProgress(true);
|
||||
return new LocalMessageExtractorLoader(mContext, mMessage, messageAnnotations);
|
||||
public void onMessageDataLoadFailed() {
|
||||
Toast.makeText(getActivity(), R.string.status_loading_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<MessageViewInfo> loader, MessageViewInfo messageViewInfo) {
|
||||
if (loader.getId() != DECODE_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message decoder id");
|
||||
}
|
||||
setProgress(false);
|
||||
onDecodeMessageFinished(messageViewInfo);
|
||||
public void onMessageViewInfoLoadFinished(LocalMessage localMessage, MessageViewInfo messageViewInfo) {
|
||||
showMessage(messageViewInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<MessageViewInfo> loader) {
|
||||
if (loader.getId() != DECODE_MESSAGE_LOADER_ID) {
|
||||
throw new IllegalStateException("loader id must be message decoder id");
|
||||
}
|
||||
// Do nothing
|
||||
public void onMessageViewInfoLoadFailed(LocalMessage localMessage) {
|
||||
MessageViewInfo messageViewInfo = MessageViewInfo.createWithErrorState(localMessage);
|
||||
showMessage(messageViewInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadingProgress(int current, int max) {
|
||||
mMessageView.setLoadingProgress(current, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadErrorMessageNotFound() {
|
||||
mMessageView.enableDownloadButton();
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getActivity(), R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadErrorNetworkError() {
|
||||
mMessageView.enableDownloadButton();
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getActivity(), R.string.status_network_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startIntentSenderForMessageLoaderHelper(IntentSender si, int requestCode, Intent fillIntent,
|
||||
int flagsMask, int flagValues, int extraFlags) {
|
||||
try {
|
||||
requestCode |= REQUEST_MASK_LOADER_HELPER;
|
||||
getActivity().startIntentSenderForResult(
|
||||
si, requestCode, fillIntent, flagsMask, flagValues, extraFlags);
|
||||
} catch (SendIntentException e) {
|
||||
Log.e(K9.LOG_TAG, "Irrecoverable error calling PendingIntent!", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewAttachment(AttachmentViewInfo attachment) {
|
||||
|
@ -920,30 +793,4 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||
private AttachmentController getAttachmentController(AttachmentViewInfo attachment) {
|
||||
return new AttachmentController(mController, downloadManager, this, attachment);
|
||||
}
|
||||
|
||||
private class DownloadMessageListener extends MessagingListener {
|
||||
@Override
|
||||
public void loadMessageForViewFinished(Account account, String folder, String uid, final LocalMessage message) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isAdded()) {
|
||||
onMessageDownloadFinished(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageForViewFailed(Account account, String folder, String uid, final Throwable t) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isAdded()) {
|
||||
onDownloadMessageFailed(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,6 +185,7 @@ Please submit bug reports, contribute new features and ask questions at
|
|||
<string name="status_loading_more">Loading messages\u2026</string>
|
||||
<string name="status_network_error">Connection error</string>
|
||||
<string name="status_invalid_id_error">Message not found</string>
|
||||
<string name="status_loading_error">Message loading error</string>
|
||||
|
||||
<string name="status_loading_more_failed">Retry loading more messages</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue