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
This commit is contained in:
parent
8587aa6121
commit
8d5004a6eb
@ -111,6 +111,9 @@
|
||||
<string name="status_loading_more_failed">Retry loading more messages</string>
|
||||
<!-- Notification title in status bar -->
|
||||
<string name="notification_new_title">New email</string>
|
||||
<!-- Dialog text when we are unable to load the message for display -->
|
||||
<string name="error_loading_message_body">Unexpected error while loading message text. Message
|
||||
may be too large to view.</string>
|
||||
|
||||
<!-- Notification message in notifications window when one account has
|
||||
one or more new messages; e.g, "279 unread (someone@google.com)". -->
|
||||
|
@ -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<Void, Void, Cursor> {
|
||||
private class LoadMessageTask extends AsyncTask<Void, Void, Message> {
|
||||
|
||||
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<Void, Void, Cursor> {
|
||||
private class LoadBodyTask extends AsyncTask<Void, Void, String[]> {
|
||||
|
||||
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 <br> and adding a html/body wrapper.
|
||||
* Convert the plain text to HTML
|
||||
*/
|
||||
StringBuffer sb = new StringBuffer("<html><body>");
|
||||
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("</body></html>");
|
||||
text = sb.toString();
|
||||
} else {
|
||||
text = html;
|
||||
mHtmlText = html;
|
||||
text = bodyHtml;
|
||||
mHtmlText = bodyHtml;
|
||||
hasImages = IMG_TAG_START_REGEX.matcher(text).find();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user