Use a Loader to load the message to view from the database

This commit is contained in:
cketti 2015-01-13 04:17:25 +01:00
parent 787c014265
commit 78ed2a23b1
6 changed files with 179 additions and 151 deletions

View File

@ -3113,27 +3113,34 @@ public class MessagingController implements Runnable {
});
}
/**
* Mark the provided message as read if not disabled by the account setting.
*
* @param account
* The account the message belongs to.
* @param message
* The message to mark as read. This {@link Message} instance will be modify by calling
* {@link Message#setFlag(Flag, boolean)} on it.
*
* @throws MessagingException
*
* @see Account#isMarkMessageAsReadOnView()
*/
private void markMessageAsReadOnView(Account account, Message message)
public LocalMessage loadMessage(Account account, String folderName, String uid) throws MessagingException {
LocalStore localStore = account.getLocalStore();
LocalFolder localFolder = localStore.getFolder(folderName);
localFolder.open(Folder.OPEN_MODE_RW);
LocalMessage message = localFolder.getMessage(uid);
if (message == null || message.getId() == 0) {
throw new IllegalArgumentException("Message not found: folder=" + folderName + ", uid=" + uid);
}
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.BODY);
localFolder.fetch(Collections.singletonList(message), fp, null);
localFolder.close();
markMessageAsReadOnView(account, message);
return message;
}
private void markMessageAsReadOnView(Account account, LocalMessage message)
throws MessagingException {
if (account.isMarkMessageAsReadOnView() && !message.isSet(Flag.SEEN)) {
List<Long> messageIds = Collections.singletonList(message.getId());
setFlag(account, messageIds, Flag.SEEN, true);
((LocalMessage) message).setFlagInternal(Flag.SEEN, true);
message.setFlagInternal(Flag.SEEN, true);
}
}
@ -4013,7 +4020,7 @@ public class MessagingController implements Runnable {
@Override
public void act(final Account account, final Folder folder,
final List<Message> accountMessages) {
final List<Message> accountMessages) {
suppressMessages(account, messages);
putBackground("deleteMessages", null, new Runnable() {

View File

@ -554,4 +554,8 @@ public class LocalMessage extends MimeMessage {
private String getAccountUuid() {
return getAccount().getUuid();
}
public boolean isBodyMissing() {
return getBody() == null;
}
}

View File

@ -0,0 +1,60 @@
package com.fsck.k9.ui.message;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.activity.MessageReference;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mailstore.LocalMessage;
public class LocalMessageLoader extends AsyncTaskLoader<LocalMessage> {
private final MessagingController controller;
private final Account account;
private final MessageReference messageReference;
private LocalMessage message;
public LocalMessageLoader(Context context, MessagingController controller, Account account,
MessageReference messageReference) {
super(context);
this.controller = controller;
this.account = account;
this.messageReference = messageReference;
}
@Override
protected void onStartLoading() {
if (message != null) {
super.deliverResult(message);
}
if (takeContentChanged() || message == null) {
forceLoad();
}
}
@Override
public void deliverResult(LocalMessage message) {
this.message = message;
super.deliverResult(message);
}
@Override
public LocalMessage loadInBackground() {
try {
return loadMessageFromDatabase();
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Error while loading message from database", e);
return null;
}
}
private LocalMessage loadMessageFromDatabase() throws MessagingException {
return controller.loadMessage(account, messageReference.folderName, messageReference.uid);
}
}

View File

@ -6,13 +6,16 @@ import java.util.Locale;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
@ -41,6 +44,7 @@ import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.ui.message.LocalMessageLoader;
import com.fsck.k9.view.AttachmentView;
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
import com.fsck.k9.view.MessageHeader;
@ -60,6 +64,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
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;
public static MessageViewFragment newInstance(MessageReference reference) {
MessageViewFragment fragment = new MessageViewFragment();
@ -105,6 +111,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
private Context mContext;
private LoaderCallbacks<LocalMessage> localMessageLoaderCallback = new LocalMessageLoaderCallback();
class MessageViewHandler extends Handler {
@ -117,15 +125,6 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
});
}
public void addAttachment(final View attachmentView) {
post(new Runnable() {
@Override
public void run() {
mMessageView.addAttachment(attachmentView);
}
});
}
/* A helper for a set of "show a toast" methods */
private void showToast(final String message, final int toastLength) {
post(new Runnable() {
@ -146,16 +145,6 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
showToast(context.getString(R.string.status_network_error), Toast.LENGTH_LONG);
}
public void invalidIdError() {
Context context = getActivity();
if (context == null) {
return;
}
showToast(context.getString(R.string.status_invalid_id_error), Toast.LENGTH_LONG);
}
public void fetchingAttachment() {
Context context = getActivity();
if (context == null) {
@ -230,7 +219,12 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
};
});
mMessageView.initialize(this);
mMessageView.initialize(this, new OnClickListener() {
@Override
public void onClick(View v) {
onToggleFlagged();
}
});
mMessageView.downloadRemainderButton().setOnClickListener(this);
mFragmentListener.messageHeaderViewAvailable(mMessageView.getMessageHeaderView());
@ -261,10 +255,6 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
outState.putSerializable(STATE_PGP_DATA, mPgpData);
}
public void displayMessage(MessageReference ref) {
displayMessage(ref, true);
}
private void displayMessage(MessageReference ref, boolean resetPgpData) {
mMessageReference = ref;
if (K9.DEBUG) {
@ -283,11 +273,50 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
mMessageView.resetView();
mMessageView.resetHeaderView();
mController.loadMessageForView(mAccount, mMessageReference.folderName, mMessageReference.uid, mListener);
startLoadingMessageFromDatabase();
mFragmentListener.updateMenu();
}
private void startLoadingMessageFromDatabase() {
getLoaderManager().initLoader(LOCAL_MESSAGE_LOADER_ID, null, localMessageLoaderCallback);
}
private void onLoadMessageFromDatabaseFinished(LocalMessage message) {
displayMessageHeader(message);
if (message.isBodyMissing()) {
startDownloadingMessageBody(message);
} else {
startExtractingTextAndAttachments(message);
}
}
private void onLoadMessageFromDatabaseFailed() {
mMessageView.showStatusMessage(mContext.getString(R.string.status_invalid_id_error));
}
private void startDownloadingMessageBody(LocalMessage message) {
throw new RuntimeException("Not implemented yet");
}
private void startExtractingTextAndAttachments(LocalMessage message) {
//TODO: extract in background thread
//TODO: handle decryption and signature verification
try {
mMessageView.setMessage(mAccount, message, mPgpData, mController, mListener);
mMessageView.setShowDownloadButton(message);
} catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Error while trying to display message", e);
}
}
private void displayMessageHeader(LocalMessage message) {
mMessageView.setHeaders(message, mAccount);
displayMessageSubject(getSubjectForMessage(message));
mFragmentListener.updateMenu();
}
/**
* Called from UI thread when user select Delete
*/
@ -516,6 +545,15 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
}
}
private String getSubjectForMessage(LocalMessage message) {
String subject = message.getSubject();
if (TextUtils.isEmpty(subject)) {
return mContext.getString(R.string.general_no_subject);
}
return subject;
}
public void moveMessage(MessageReference reference, String destFolderName) {
mController.moveMessage(mAccount, mMessageReference.folderName, mMessage,
destFolderName, null);
@ -530,126 +568,28 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
@Override
public void loadMessageForViewHeadersAvailable(final Account account, String folder, String uid,
final Message message) {
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|| !mMessageReference.accountUuid.equals(account.getUuid())) {
return;
}
/*
* Clone the message object because the original could be modified by
* MessagingController later. This could lead to a ConcurrentModificationException
* when that same object is accessed by the UI thread (below).
*
* See issue 3953
*
* This is just an ugly hack to get rid of the most pressing problem. A proper way to
* fix this is to make Message thread-safe. Or, even better, rewriting the UI code to
* access messages via a ContentProvider.
*
*/
final Message clonedMessage = message.clone();
mHandler.post(new Runnable() {
@Override
public void run() {
if (!clonedMessage.isSet(Flag.X_DOWNLOADED_FULL) &&
!clonedMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
String text = mContext.getString(R.string.message_view_downloading);
mMessageView.showStatusMessage(text);
}
mMessageView.setHeaders(clonedMessage, account);
final String subject = clonedMessage.getSubject();
if (subject == null || subject.equals("")) {
displayMessageSubject(mContext.getString(R.string.general_no_subject));
} else {
displayMessageSubject(clonedMessage.getSubject());
}
mMessageView.setOnFlagListener(new OnClickListener() {
@Override
public void onClick(View v) {
onToggleFlagged();
}
});
}
});
throw new IllegalStateException();
}
@Override
public void loadMessageForViewBodyAvailable(final Account account, String folder,
String uid, final Message message) {
if (!(message instanceof LocalMessage) ||
!mMessageReference.uid.equals(uid) ||
!mMessageReference.folderName.equals(folder) ||
!mMessageReference.accountUuid.equals(account.getUuid())) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
try {
mMessage = (LocalMessage) message;
mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
mController, mListener);
mFragmentListener.updateMenu();
} catch (MessagingException e) {
Log.v(K9.LOG_TAG, "loadMessageForViewBodyAvailable", e);
}
}
});
throw new IllegalStateException();
}
@Override
public void loadMessageForViewFailed(Account account, String folder, String uid, final Throwable t) {
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|| !mMessageReference.accountUuid.equals(account.getUuid())) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
setProgress(false);
if (t instanceof IllegalArgumentException) {
mHandler.invalidIdError();
} else {
mHandler.networkError();
}
if (mMessage == null || mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
mMessageView.showStatusMessage(
mContext.getString(R.string.webview_empty_message));
}
}
});
throw new IllegalStateException();
}
@Override
public void loadMessageForViewFinished(Account account, String folder, String uid, final Message message) {
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|| !mMessageReference.accountUuid.equals(account.getUuid())) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
setProgress(false);
mMessageView.setShowDownloadButton(message);
}
});
throw new IllegalStateException();
}
@Override
public void loadMessageForViewStarted(Account account, String folder, String uid) {
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|| !mMessageReference.accountUuid.equals(account.getUuid())) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
setProgress(true);
}
});
throw new IllegalStateException();
}
@Override
@ -865,4 +805,26 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
public LayoutInflater getFragmentLayoutInflater() {
return mLayoutInflater;
}
class LocalMessageLoaderCallback implements LoaderCallbacks<LocalMessage> {
@Override
public Loader<LocalMessage> onCreateLoader(int id, Bundle args) {
return new LocalMessageLoader(mContext, mController, mAccount, mMessageReference);
}
@Override
public void onLoadFinished(Loader<LocalMessage> loader, LocalMessage message) {
mMessage = message;
if (message == null) {
onLoadMessageFromDatabaseFailed();
} else {
onLoadMessageFromDatabaseFinished(message);
}
}
@Override
public void onLoaderReset(Loader<LocalMessage> loader) {
// Do nothing
}
}
}

View File

@ -115,7 +115,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
private String mText;
public void initialize(Fragment fragment) {
public void initialize(Fragment fragment, OnClickListener flagListener) {
Activity activity = fragment.getActivity();
mMessageContentView = (MessageWebView) findViewById(R.id.message_content);
mMessageContentView.configure();
@ -124,6 +124,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
mHeaderContainer = (MessageHeader) findViewById(R.id.header_container);
mHeaderContainer.setOnLayoutChangedListener(this);
mHeaderContainer.setOnFlagListener(flagListener);
mAttachmentsContainer = findViewById(R.id.attachments_container);
mAttachments = (LinearLayout) findViewById(R.id.attachments);
@ -493,10 +494,6 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
}
}
public void setOnFlagListener(OnClickListener listener) {
mHeaderContainer.setOnFlagListener(listener);
}
public void showAllHeaders() {
mHeaderContainer.onShowAdditionalHeaders();
}

View File

@ -153,8 +153,6 @@ public class MessageHeader extends LinearLayout implements OnClickListener {
}
public void setOnFlagListener(OnClickListener listener) {
if (mFlagged == null)
return;
mFlagged.setOnClickListener(listener);
}