Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e9184f8 authored by Vincent Breitmoser's avatar Vincent Breitmoser
Browse files

compose: use MessageLoaderHelper for quoted message loading (fixes MessageCompose)

parent b72dba67
Loading
Loading
Loading
Loading
+82 −64
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.Loader;
import android.content.pm.ActivityInfo;
@@ -55,6 +56,7 @@ import com.fsck.k9.Identity;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.activity.MessageLoaderHelper.MessageLoaderCallbacks;
import com.fsck.k9.activity.compose.ComposeCryptoStatus;
import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState;
import com.fsck.k9.activity.compose.ComposeCryptoStatus.SendErrorState;
@@ -89,6 +91,7 @@ import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mailstore.LocalBodyPart;
import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.mailstore.MessageViewInfo;
import com.fsck.k9.message.ComposePgpInlineDecider;
import com.fsck.k9.message.IdentityField;
import com.fsck.k9.message.IdentityHeaderParser;
@@ -157,6 +160,9 @@ public class MessageCompose extends K9Activity implements OnClickListener,

    private static final int REQUEST_MASK_RECIPIENT_PRESENTER = (1<<8);
    private static final int REQUEST_MASK_MESSAGE_BUILDER = (2<<8);
    private static final int REQUEST_MASK_LOADER_HELPER = (3<<8);

    private static final int INITIAL_MAX_LOADER_ID = 128;

    /**
     * Regular expression to remove the first localized "Re:" prefix in subjects.
@@ -166,6 +172,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
     */
    private static final Pattern PREFIX = Pattern.compile("^AW[:\\s]\\s*", Pattern.CASE_INSENSITIVE);
    private QuotedMessagePresenter quotedMessagePresenter;
    private MessageLoaderHelper messageLoaderHelper;

    /**
     * The account used for message composition.
@@ -196,7 +203,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
     * have already been added from the restore of the view state.
     */
    private boolean mSourceMessageProcessed = false;
    private int mMaxLoaderId = 0;
    private int mMaxLoaderId = INITIAL_MAX_LOADER_ID;

    private RecipientPresenter recipientPresenter;
    private MessageBuilder currentMessageBuilder;
@@ -249,8 +256,6 @@ public class MessageCompose extends K9Activity implements OnClickListener,
     */
    private Action mAction;

    private Listener mListener = new Listener();

    private boolean mReadReceipt = false;

    private TextView mChooseIdentityButton;
@@ -345,7 +350,6 @@ public class MessageCompose extends K9Activity implements OnClickListener,

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        if (UpgradeDatabases.actionUpgradeDatabases(this, getIntent())) {
@@ -507,18 +511,10 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        if (!mSourceMessageProcessed) {
            if (mAction == Action.REPLY || mAction == Action.REPLY_ALL ||
                    mAction == Action.FORWARD || mAction == Action.EDIT_DRAFT) {
                /*
                 * If we need to load the message we add ourself as a message listener here
                 * so we can kick it off. Normally we add in onResume but we don't
                 * want to reload the message every time the activity is resumed.
                 * There is no harm in adding twice.
                 */
                MessagingController.getInstance(getApplication()).addListener(mListener);

                final Account account = Preferences.getPreferences(this).getAccount(mMessageReference.getAccountUuid());
                final String folderName = mMessageReference.getFolderName();
                final String sourceMessageUid = mMessageReference.getUid();
                MessagingController.getInstance(getApplication()).loadMessageForView(account, folderName, sourceMessageUid, null);
                messageLoaderHelper = new MessageLoaderHelper(this, getLoaderManager(), getFragmentManager(),
                        messageLoaderCallbacks);
                mHandler.sendEmptyMessage(MSG_PROGRESS_ON);
                messageLoaderHelper.asyncStartOrResumeLoadingMessage(mMessageReference);
            }

            if (mAction != Action.EDIT_DRAFT) {
@@ -668,15 +664,15 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    }

    @Override
    public void onResume() {
    protected void onResume() {
        super.onResume();
        MessagingController.getInstance(getApplication()).addListener(mListener);
        MessagingController.getInstance(this).addListener(messagingListener);
    }

    @Override
    public void onPause() {
        super.onPause();
        MessagingController.getInstance(getApplication()).removeListener(mListener);
        MessagingController.getInstance(this).removeListener(messagingListener);

        boolean isPausingOnConfigurationChange = (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION)
                == ActivityInfo.CONFIG_ORIENTATION;
@@ -731,7 +727,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        super.onRestoreInstanceState(savedInstanceState);

        mAttachments.removeAllViews();
        mMaxLoaderId = 0;
        mMaxLoaderId = INITIAL_MAX_LOADER_ID;

        mNumAttachmentsLoading = savedInstanceState.getInt(STATE_KEY_NUM_ATTACHMENTS_LOADING);
        mWaitingForAttachments = WaitingAction.NONE;
@@ -1150,6 +1146,12 @@ public class MessageCompose extends K9Activity implements OnClickListener,
            return;
        }

        if ((requestCode & REQUEST_MASK_LOADER_HELPER) == REQUEST_MASK_LOADER_HELPER) {
            requestCode ^= REQUEST_MASK_LOADER_HELPER;
            messageLoaderHelper.onActivityResult(requestCode, resultCode, data);
            return;
        }

        if (resultCode != RESULT_OK) {
            return;
        }
@@ -1533,12 +1535,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
            throw new IllegalStateException("tried to edit quoted message with no referenced message");
        }

        // TODO - Should we check if mSourceMessageBody is already present and bypass the MessagingController call?
        MessagingController.getInstance(getApplication()).addListener(mListener);
        final Account account = Preferences.getPreferences(this).getAccount(mMessageReference.getAccountUuid());
        final String folderName = mMessageReference.getFolderName();
        final String sourceMessageUid = mMessageReference.getUid();
        MessagingController.getInstance(getApplication()).loadMessageForView(account, folderName, sourceMessageUid, null);
        messageLoaderHelper.asyncStartOrResumeLoadingMessage(mMessageReference);
    }

    /**
@@ -1943,67 +1940,88 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        }
    }

    class Listener extends MessagingListener {
    private MessageLoaderCallbacks messageLoaderCallbacks = new MessageLoaderCallbacks() {
        @Override
        public void loadMessageForViewStarted(Account account, String folder, String uid) {
            if (mMessageReference == null || !mMessageReference.getUid().equals(uid)) {
                return;
        public void onMessageDataLoadFinished(LocalMessage message) {
            // nothing to do here, we don't care about message headers
        }

            mHandler.sendEmptyMessage(MSG_PROGRESS_ON);
        @Override
        public void onMessageDataLoadFailed() {
            mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
            Toast.makeText(MessageCompose.this, R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
        }

        @Override
        public void loadMessageForViewFinished(Account account, String folder, String uid) {
            if (mMessageReference == null || !mMessageReference.getUid().equals(uid)) {
                return;
        public void onMessageViewInfoLoadFinished(LocalMessage localMessage, MessageViewInfo messageViewInfo) {
            mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
            loadLocalMessageForDisplay(localMessage, mAction);
        }

        @Override
        public void onMessageViewInfoLoadFailed(LocalMessage localMessage) {
            mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
            Toast.makeText(MessageCompose.this, R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
        }

            if (mMessageReference == null || !mMessageReference.getUid().equals(uid)) {
                return;
        @Override
        public void setLoadingProgress(int current, int max) {
            // nvm - we don't have a progress bar
        }

            final LocalMessage message = null; // TODO this isn't working at the moment!
        @Override
        public void startIntentSenderForMessageLoaderHelper(IntentSender si, int requestCode, Intent fillIntent,
                int flagsMask, int flagValues, int extraFlags) {
            try {
                requestCode |= REQUEST_MASK_LOADER_HELPER;
                startIntentSenderForResult(si, requestCode, fillIntent, flagsMask, flagValues, extraFlags);
            } catch (SendIntentException e) {
                Log.e(K9.LOG_TAG, "Irrecoverable error calling PendingIntent!", e);
            }
        }

        @Override
        public void onDownloadErrorMessageNotFound() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // We check to see if we've previously processed the source message since this
                    // could be called when switching from HTML to text replies. If that happens, we
                    // only want to update the UI with quoted text (which picks the appropriate
                    // part).
                    loadLocalMessageForDisplay(message, mAction);
                    Toast.makeText(MessageCompose.this, R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
                }
            });
        }

        @Override
        public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t) {
            if (mMessageReference == null || !mMessageReference.getUid().equals(uid)) {
                return;
        public void onDownloadErrorNetworkError() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MessageCompose.this, R.string.status_network_error, Toast.LENGTH_LONG).show();
                }
            mHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
            // TODO show network error
            });
        }
    };

    // TODO We miss callbacks for this listener if they happens while we are paused!
    public MessagingListener messagingListener = new MessagingListener() {

        @Override
        public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {
            // Track UID changes of the source message
            if (mMessageReference != null) {
                final Account sourceAccount = Preferences.getPreferences(MessageCompose.this)
            if (mMessageReference == null) {
                return;
            }

            Account sourceAccount = Preferences.getPreferences(MessageCompose.this)
                    .getAccount(mMessageReference.getAccountUuid());
                final String sourceFolder = mMessageReference.getFolderName();
                final String sourceMessageUid = mMessageReference.getUid();
            String sourceFolder = mMessageReference.getFolderName();
            String sourceMessageUid = mMessageReference.getUid();

                if (account.equals(sourceAccount) && (folder.equals(sourceFolder))) {
                    if (oldUid.equals(sourceMessageUid)) {
            boolean changedMessageIsCurrent =
                    account.equals(sourceAccount) && folder.equals(sourceFolder) && oldUid.equals(sourceMessageUid);
            if (changedMessageIsCurrent) {
                mMessageReference = mMessageReference.withModifiedUid(newUid);
            }
        }
            }
        }
    }

    };

}
+0 −51
Original line number Diff line number Diff line
@@ -2804,57 +2804,6 @@ public class MessagingController implements Runnable {
        }
    }

    public void loadMessageForView(final Account account, final String folder, final String uid,
                                   final MessagingListener listener) {
        for (MessagingListener l : getListeners(listener)) {
            l.loadMessageForViewStarted(account, folder, uid);
        }
        threadPool.execute(new Runnable() {
            @Override
            public void run() {

                try {
                    LocalStore localStore = account.getLocalStore();
                    LocalFolder localFolder = localStore.getFolder(folder);
                    localFolder.open(Folder.OPEN_MODE_RW);

                    LocalMessage message = localFolder.getMessage(uid);
                    if (message == null
                    || message.getId() == 0) {
                        throw new IllegalArgumentException("Message not found: folder=" + folder + ", uid=" + uid);
                    }
                    // IMAP search results will usually need to be downloaded before viewing.
                    // TODO: limit by account.getMaximumAutoDownloadMessageSize().
                    if (!message.isSet(Flag.X_DOWNLOADED_FULL) &&
                            !message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
                        if (loadMessageRemoteSynchronous(account, folder, uid, listener, true)) {
                            markMessageAsReadOnView(account, message);
                        }
                        return;
                    }

                    FetchProfile fp = new FetchProfile();
                    fp.add(FetchProfile.Item.ENVELOPE);
                    fp.add(FetchProfile.Item.BODY);
                    localFolder.fetch(Collections.singletonList(message), fp, null);
                    localFolder.close();

                    for (MessagingListener l : getListeners(listener)) {
                        l.loadMessageForViewFinished(account, folder, uid);
                    }
                    markMessageAsReadOnView(account, message);

                } catch (Exception e) {
                    for (MessagingListener l : getListeners(listener)) {
                        l.loadMessageForViewFailed(account, folder, uid, e);
                    }
                    addErrorMessage(account, null, e);

                }
            }
        });
    }

    public LocalMessage loadMessage(Account account, String folderName, String uid) throws MessagingException {
        LocalStore localStore = account.getLocalStore();
        LocalFolder localFolder = localStore.getFolder(folderName);