From 8d5004a6eb28844c9d86a5e97a947043b693eee6 Mon Sep 17 00:00:00 2001 From: Andrew Stadler Date: Sun, 20 Sep 2009 10:06:08 -0700 Subject: [PATCH] Modernize the code to load message and body from DB. * Don't use unneeded managed cursors * Put more of the DB work in async * Don't try to load HTML and plaintext simultaneously * Explicitly catch & handle exceptions during db ops Bug # 2087051 (see also bug # 2122960 re unexpected db exceptions) Change-Id: I446086bc4f3a85b2e766b12d1f7271e61e3ae8d5 --- res/values/strings.xml | 3 + .../android/email/activity/MessageView.java | 122 +++++++----------- 2 files changed, 53 insertions(+), 72 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 22b644b25..610ede96d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -111,6 +111,9 @@ Retry loading more messages New email + + Unexpected error while loading message text. Message + may be too large to view. diff --git a/src/com/android/email/activity/MessageView.java b/src/com/android/email/activity/MessageView.java index 6a412fa5c..affc695e9 100644 --- a/src/com/android/email/activity/MessageView.java +++ b/src/com/android/email/activity/MessageView.java @@ -43,7 +43,6 @@ import android.database.ContentObserver; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; @@ -58,6 +57,7 @@ import android.provider.ContactsContract.FastTrack; import android.provider.ContactsContract.Presence; import android.text.TextUtils; import android.text.util.Regex; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -160,6 +160,7 @@ public class MessageView extends Activity implements OnClickListener { private static final int MSG_ATTACHMENT_PROGRESS = 2; private static final int MSG_LOAD_CONTENT_URI = 3; private static final int MSG_SET_ATTACHMENTS_ENABLED = 4; + private static final int MSG_LOAD_BODY_ERROR = 5; private static final int MSG_NETWORK_ERROR = 6; private static final int MSG_FETCHING_ATTACHMENT = 10; private static final int MSG_VIEW_ATTACHMENT_ERROR = 12; @@ -196,6 +197,10 @@ public class MessageView extends Activity implements OnClickListener { attachment.downloadButton.setEnabled(msg.arg1 == 1); } break; + case MSG_LOAD_BODY_ERROR: + Toast.makeText(MessageView.this, + R.string.error_loading_message_body, Toast.LENGTH_LONG).show(); + break; case MSG_NETWORK_ERROR: Toast.makeText(MessageView.this, R.string.status_network_error, Toast.LENGTH_LONG).show(); @@ -247,6 +252,10 @@ public class MessageView extends Activity implements OnClickListener { sendMessage(msg); } + public void loadBodyError() { + sendEmptyMessage(MSG_LOAD_BODY_ERROR); + } + public void networkError() { sendEmptyMessage(MSG_NETWORK_ERROR); } @@ -1013,11 +1022,8 @@ public class MessageView extends Activity implements OnClickListener { /** * Async task for loading a single message outside of the UI thread - * - * TODO: start and stop progress indicator - * TODO: set up to listen for additional updates (cursor data changes) */ - private class LoadMessageTask extends AsyncTask { + private class LoadMessageTask extends AsyncTask { private long mId; private boolean mOkToFetch; @@ -1031,37 +1037,25 @@ public class MessageView extends Activity implements OnClickListener { } @Override - protected Cursor doInBackground(Void... params) { - return MessageView.this.managedQuery( - Message.CONTENT_URI, - Message.CONTENT_PROJECTION, - Message.RECORD_ID + "=?", - new String[] { - String.valueOf(mId) - }, - null); + protected Message doInBackground(Void... params) { + return Message.restoreMessageWithId(MessageView.this, mId); } @Override - protected void onPostExecute(Cursor cursor) { - if (cursor.isClosed()) { + protected void onPostExecute(Message message) { + if (message == null) { + finish(); return; } - if (cursor.moveToFirst()) { - reloadUiFromCursor(cursor, mOkToFetch); - } else { - // toast? why would this fail? - } + reloadUiFromMessage(message, mOkToFetch); startPresenceCheck(); } } /** * Async task for loading a single message body outside of the UI thread - * - * TODO: smarter loading of html vs. text */ - private class LoadBodyTask extends AsyncTask { + private class LoadBodyTask extends AsyncTask { private long mId; @@ -1073,30 +1067,29 @@ public class MessageView extends Activity implements OnClickListener { } @Override - protected Cursor doInBackground(Void... params) { - return MessageView.this.managedQuery( - Body.CONTENT_URI, - BODY_CONTENT_PROJECTION, - Body.MESSAGE_KEY + "=?", - new String[] { - String.valueOf(mId) - }, - null); + protected String[] doInBackground(Void... params) { + try { + String text = null; + String html = Body.restoreBodyHtmlWithMessageId(MessageView.this, mId); + if (html == null) { + text = Body.restoreBodyTextWithMessageId(MessageView.this, mId); + } + return new String[] { text, html }; + } catch (RuntimeException re) { + // This catches SQLiteException as well as other RTE's we've seen from the + // database calls, such as IllegalStateException + Log.d(Email.LOG_TAG, "Exception while loading message body: " + re.toString()); + mHandler.loadBodyError(); + return new String[] { null, null }; + } } @Override - protected void onPostExecute(Cursor cursor) { - if (cursor.isClosed()) { + protected void onPostExecute(String[] results) { + if (results == null) { return; } - if (cursor.moveToFirst()) { - reloadBodyFromCursor(cursor); - } else { - // toast? why would this fail? - reloadBodyFromCursor(null); // hack to force text for display - } - - // At this point it's fair to mark the message as "read" + reloadUiFromBody(results[0], results[1]); // text, html onMarkAsRead(true); } } @@ -1143,14 +1136,13 @@ public class MessageView extends Activity implements OnClickListener { /** * Reload the UI from a provider cursor. This must only be called from the UI thread. * - * @param cursor A cursor loaded from EmailStore.Message. + * @param message A copy of the message loaded from the database * @param okToFetch If true, and message is not fully loaded, it's OK to fetch from * the network. Use false to prevent looping here. * * TODO: trigger presence check */ - private void reloadUiFromCursor(Cursor cursor, boolean okToFetch) { - Message message = new Message().restore(cursor); + private void reloadUiFromMessage(Message message, boolean okToFetch) { mMessage = message; mAccountId = message.mAccountKey; if (mMailboxId == -1) { @@ -1193,40 +1185,26 @@ public class MessageView extends Activity implements OnClickListener { /** * Reload the body from the provider cursor. This must only be called from the UI thread. * - * @param cursor + * @param bodyText text part + * @param bodyHtml html part * * TODO deal with html vs text and many other issues */ - private void reloadBodyFromCursor(Cursor cursor) { + private void reloadUiFromBody(String bodyText, String bodyHtml) { + String text = null; mHtmlText = null; boolean hasImages = false; - // TODO Remove this hack that forces some text to test the code - String html = null; - String text = null; - if (cursor != null) { - // First try HTML; we'll show that if it exists... - html = cursor.getString(BODY_CONTENT_COLUMN_HTML_CONTENT); - if (html == null) { - text = cursor.getString(BODY_CONTENT_COLUMN_TEXT_CONTENT); - } - } - - if (html == null) { - // This code is stolen from Listener.loadMessageForViewBodyAvailable - // And also escape special character, such as "<>&", - // to HTML escape sequence. - if (text == null) { - text = ""; - } - text = EmailHtmlUtil.escapeCharacterToDisplay(text); - + if (bodyHtml == null) { + text = bodyText; /* - * Linkify the plain text and convert it to HTML by replacing - * \r?\n with
and adding a html/body wrapper. + * Convert the plain text to HTML */ StringBuffer sb = new StringBuffer(""); if (text != null) { + // Escape any inadvertent HTML in the text message + text = EmailHtmlUtil.escapeCharacterToDisplay(text); + // Find any embedded URL's and linkify Matcher m = Regex.WEB_URL_PATTERN.matcher(text); while (m.find()) { int start = m.start(); @@ -1260,8 +1238,8 @@ public class MessageView extends Activity implements OnClickListener { sb.append(""); text = sb.toString(); } else { - text = html; - mHtmlText = html; + text = bodyHtml; + mHtmlText = bodyHtml; hasImages = IMG_TAG_START_REGEX.matcher(text).find(); }