Working implementation of eml viewer.
Everything works except attachments.
The implementation uses a controller that is used
by both SecureConversationViewFragment and the new
EmlMessageViewFragment to do the rendering work.
Change-Id: I4d8b9b56e6609c3a81b902b102a90f1e50c115cd
diff --git a/res/layout/eml_viewer_activity.xml b/res/layout/eml_viewer_activity.xml
index b7394c3..4999b6f 100644
--- a/res/layout/eml_viewer_activity.xml
+++ b/res/layout/eml_viewer_activity.xml
@@ -16,8 +16,9 @@
limitations under the License.
-->
-<WebView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/webview"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-</WebView>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/eml_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/white" >
+</FrameLayout>
diff --git a/src/com/android/mail/browse/ConversationMessage.java b/src/com/android/mail/browse/ConversationMessage.java
index dbc9b07..e66494f 100644
--- a/src/com/android/mail/browse/ConversationMessage.java
+++ b/src/com/android/mail/browse/ConversationMessage.java
@@ -53,7 +53,6 @@
public ConversationMessage(MimeMessage mimeMessage) throws MessagingException {
super(mimeMessage);
- // TODO - synthesize conversation
}
public void setController(ConversationController controller) {
@@ -61,7 +60,7 @@
}
public Conversation getConversation() {
- return mController.getConversation();
+ return mController != null ? mController.getConversation() : null;
}
/**
diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java
index 97e7642..89ccb09 100644
--- a/src/com/android/mail/browse/ConversationViewAdapter.java
+++ b/src/com/android/mail/browse/ConversationViewAdapter.java
@@ -119,7 +119,10 @@
}
- public class MessageHeaderItem extends ConversationOverlayItem {
+ public static class MessageHeaderItem extends ConversationOverlayItem {
+
+ private final ConversationViewAdapter mAdapter;
+
private ConversationMessage mMessage;
// view state variables
@@ -132,7 +135,9 @@
public CharSequence timestampLong;
public CharSequence recipientSummaryText;
- MessageHeaderItem(ConversationMessage message, boolean expanded, boolean showImages) {
+ MessageHeaderItem(ConversationViewAdapter adapter, ConversationMessage message,
+ boolean expanded, boolean showImages) {
+ mAdapter = adapter;
mMessage = message;
mExpanded = expanded;
mShowImages = showImages;
@@ -153,10 +158,11 @@
public View createView(Context context, LayoutInflater inflater, ViewGroup parent) {
final MessageHeaderView v = (MessageHeaderView) inflater.inflate(
R.layout.conversation_message_header, parent, false);
- v.initialize(mDateBuilder, mAccountController, mAddressCache);
- v.setCallbacks(mMessageCallbacks);
- v.setContactInfoSource(mContactInfoSource);
- v.setVeiledMatcher(mMatcher);
+ v.initialize(mAdapter.mDateBuilder, mAdapter.mAccountController,
+ mAdapter.mAddressCache);
+ v.setCallbacks(mAdapter.mMessageCallbacks);
+ v.setContactInfoSource(mAdapter.mContactInfoSource);
+ v.setVeiledMatcher(mAdapter.mMatcher);
return v;
}
@@ -402,16 +408,16 @@
}
public int addMessageHeader(ConversationMessage msg, boolean expanded, boolean showImages) {
- return addItem(new MessageHeaderItem(msg, expanded, showImages));
+ return addItem(new MessageHeaderItem(this, msg, expanded, showImages));
}
public int addMessageFooter(MessageHeaderItem headerItem) {
return addItem(new MessageFooterItem(headerItem));
}
- public MessageHeaderItem newMessageHeaderItem(ConversationMessage message, boolean expanded,
- boolean showImages) {
- return new MessageHeaderItem(message, expanded, showImages);
+ public static MessageHeaderItem newMessageHeaderItem(ConversationViewAdapter adapter,
+ ConversationMessage message, boolean expanded, boolean showImages) {
+ return new MessageHeaderItem(adapter, message, expanded, showImages);
}
public MessageFooterItem newMessageFooterItem(MessageHeaderItem headerItem) {
diff --git a/src/com/android/mail/browse/EmlMessageLoader.java b/src/com/android/mail/browse/EmlMessageLoader.java
new file mode 100644
index 0000000..09e5b6a
--- /dev/null
+++ b/src/com/android/mail/browse/EmlMessageLoader.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.browse;
+
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.emailcommon.TempDirectory;
+import com.android.emailcommon.internet.MimeMessage;
+import com.android.emailcommon.mail.MessagingException;
+import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Loader that builds a ConversationMessage from an EML file Uri.
+ */
+public class EmlMessageLoader extends AsyncTaskLoader<ConversationMessage> {
+ private static final String LOG_TAG = LogTag.getLogTag();
+
+ private Uri mEmlFileUri;
+ private ConversationMessage mMessage;
+
+ public EmlMessageLoader(Context context, Uri emlFileUri) {
+ super(context);
+ mEmlFileUri = emlFileUri;
+ }
+
+ @Override
+ public ConversationMessage loadInBackground() {
+ final Context context = getContext();
+ TempDirectory.setTempDirectory(context);
+ final ContentResolver resolver = context.getContentResolver();
+ final InputStream stream;
+ try {
+ stream = resolver.openInputStream(mEmlFileUri);
+ } catch (FileNotFoundException e) {
+ LogUtils.e(LOG_TAG, e, "Could not find eml file at uri: %s", mEmlFileUri);
+ return null;
+ }
+
+ final MimeMessage mimeMessage;
+ final ConversationMessage convMessage;
+ try {
+ mimeMessage = new MimeMessage(stream);
+ convMessage = new ConversationMessage(mimeMessage);
+ } catch (IOException e) {
+ LogUtils.e(LOG_TAG, e, "Could not read eml file");
+ return null;
+ } catch (MessagingException e) {
+ LogUtils.e(LOG_TAG, e, "Error in parsing eml file");
+ return null;
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ return convMessage;
+ }
+
+ /**
+ * Called when there is new data to deliver to the client. The
+ * super class will take care of delivering it; the implementation
+ * here just adds a little more logic.
+ */
+ @Override
+ public void deliverResult(ConversationMessage result) {
+ if (isReset()) {
+ // An async query came in while the loader is stopped. We
+ // don't need the result.
+ if (result != null) {
+ onReleaseResources(result);
+ }
+ }
+ ConversationMessage oldMessage = mMessage;
+ mMessage = result;
+
+ if (isStarted()) {
+ // If the Loader is currently started, we can immediately
+ // deliver its results.
+ super.deliverResult(result);
+ }
+
+ // At this point we can release the resources associated with
+ // 'oldMessage' if needed; now that the new result is delivered we
+ // know that it is no longer in use.
+ if (oldMessage != null && oldMessage != mMessage) {
+ onReleaseResources(oldMessage);
+ }
+ }
+
+ /**
+ * Handles a request to start the Loader.
+ */
+ @Override
+ protected void onStartLoading() {
+ if (mMessage != null) {
+ // If we currently have a result available, deliver it immediately.
+ deliverResult(mMessage);
+ }
+
+ if (takeContentChanged() || mMessage == null) {
+ // If the data has changed since the last time it was loaded
+ // or is not currently available, start a load.
+ forceLoad();
+ }
+ }
+
+ /**
+ * Handles a request to stop the Loader.
+ */
+ @Override protected void onStopLoading() {
+ // Attempt to cancel the current load task if possible.
+ cancelLoad();
+ }
+
+ /**
+ * Handles a request to cancel a load.
+ */
+ @Override
+ public void onCanceled(ConversationMessage result) {
+ super.onCanceled(result);
+
+ // At this point we can release the resources associated with
+ // the message, if needed.
+ if (result != null) {
+ onReleaseResources(result);
+ }
+ }
+
+ /**
+ * Handles a request to completely reset the Loader.
+ */
+ @Override
+ protected void onReset() {
+ super.onReset();
+
+ // Ensure the loader is stopped
+ onStopLoading();
+
+ // At this point we can release the resources associated with
+ // the message, if needed.
+ if (mMessage != null) {
+ onReleaseResources(mMessage);
+ mMessage = null;
+ }
+ }
+
+
+ /**
+ * Helper function to take care of releasing resources associated
+ * with an actively loaded data set.
+ */
+ protected void onReleaseResources(ConversationMessage message) {
+ // DO NOTHING
+ }
+}
diff --git a/src/com/android/mail/browse/EmlMessageViewFragment.java b/src/com/android/mail/browse/EmlMessageViewFragment.java
new file mode 100644
index 0000000..724fd13
--- /dev/null
+++ b/src/com/android/mail/browse/EmlMessageViewFragment.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.browse;
+
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.content.Loader;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
+
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Address;
+import com.android.mail.ui.AbstractConversationWebViewClient;
+import com.android.mail.ui.ContactLoaderCallbacks;
+import com.android.mail.ui.SecureConversationViewController;
+import com.android.mail.ui.SecureConversationViewControllerCallbacks;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Fragment that is used to view EML files. It is mostly stubs
+ * that calls {@link SecureConversationViewController} to do most
+ * of the rendering work.
+ */
+public class EmlMessageViewFragment extends Fragment
+ implements SecureConversationViewControllerCallbacks,
+ LoaderManager.LoaderCallbacks<ConversationMessage> {
+ private static final String ARG_EML_FILE_URI = "eml_file_uri";
+ private static final String BASE_URI = "x-thread://message/rfc822/";
+ private static final int MESSAGE_LOADER = 0;
+ private static final int CONTACT_LOADER = 1;
+
+ private final Handler mHandler = new Handler();
+
+ private EmlWebViewClient mWebViewClient;
+ private SecureConversationViewController mViewController;
+ private ContactLoaderCallbacks mContactLoaderCallbacks;
+
+ private Uri mEmlFileUri;
+
+ /**
+ * Cache of email address strings to parsed Address objects.
+ * <p>
+ * Remember to synchronize on the map when reading or writing to this cache, because some
+ * instances use it off the UI thread (e.g. from WebView).
+ */
+ protected final Map<String, Address> mAddressCache = Collections.synchronizedMap(
+ new HashMap<String, Address>());
+
+ private class EmlWebViewClient extends AbstractConversationWebViewClient {
+ public EmlWebViewClient(Account account) {
+ super(account);
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ mViewController.dismissLoadingStatus();
+
+ final Set<String> emailAddresses = Sets.newHashSet();
+ final List<Address> cacheCopy;
+ synchronized (mAddressCache) {
+ cacheCopy = ImmutableList.copyOf(mAddressCache.values());
+ }
+ for (Address addr : cacheCopy) {
+ emailAddresses.add(addr.getAddress());
+ }
+ final ContactLoaderCallbacks callbacks = getContactInfoSource();
+ callbacks.setSenders(emailAddresses);
+ getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks);
+ }
+ };
+
+ /**
+ * Creates a new instance of {@link EmlMessageViewFragment},
+ * initialized to display an eml file from the specified {@link Uri}.
+ */
+ public static EmlMessageViewFragment newInstance(Uri emlFileUri) {
+ EmlMessageViewFragment f = new EmlMessageViewFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_EML_FILE_URI, emlFileUri);
+ f.setArguments(args);
+ return f;
+ }
+
+ /**
+ * Constructor needs to be public to handle orientation changes and activity
+ * lifecycle events.
+ */
+ public EmlMessageViewFragment() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ Bundle args = getArguments();
+ mEmlFileUri = args.getParcelable(ARG_EML_FILE_URI);
+
+ mWebViewClient = new EmlWebViewClient(null);
+ mViewController = new SecureConversationViewController(this);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return mViewController.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mWebViewClient.setContext(getActivity().getApplicationContext());
+ mViewController.onActivityCreated(savedInstanceState);
+ }
+
+ // Start SecureConversationViewControllerCallbacks
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public AbstractConversationWebViewClient getWebViewClient() {
+ return mWebViewClient;
+ }
+
+ @Override
+ public Fragment getFragment() {
+ return this;
+ }
+
+ @Override
+ public void setupConversationHeaderView(ConversationViewHeader headerView) {
+ // DO NOTHING
+ }
+
+ @Override
+ public boolean isViewVisibleToUser() {
+ return true;
+ }
+
+ @Override
+ public ContactLoaderCallbacks getContactInfoSource() {
+ if (mContactLoaderCallbacks == null) {
+ mContactLoaderCallbacks = new ContactLoaderCallbacks(getActivity());
+ }
+ return mContactLoaderCallbacks;
+ }
+
+ @Override
+ public ConversationAccountController getConversationAccountController() {
+ return null;
+ }
+
+ @Override
+ public Map<String, Address> getAddressCache() {
+ return mAddressCache;
+ }
+
+ @Override
+ public void setupMessageHeaderVeiledMatcher(MessageHeaderView messageHeaderView) {
+ // DO NOTHING
+ }
+
+ @Override
+ public void startMessageLoader() {
+ getLoaderManager().initLoader(MESSAGE_LOADER, null, this);
+ }
+
+ @Override
+ public String getBaseUri() {
+ return BASE_URI;
+ }
+
+ @Override
+ public boolean isViewOnlyMode() {
+ return true;
+ }
+
+ // End SecureConversationViewControllerCallbacks
+
+ // Start LoaderCallbacks
+
+ @Override
+ public Loader<ConversationMessage> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case MESSAGE_LOADER:
+ return new EmlMessageLoader(getActivity(), mEmlFileUri);
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<ConversationMessage> loader, ConversationMessage data) {
+ mViewController.setSubject(data.subject);
+ mViewController.renderMessage(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<ConversationMessage> loader) {
+ // Do nothing
+ }
+
+ // End LoaderCallbacks
+}
diff --git a/src/com/android/mail/browse/EmlViewerActivity.java b/src/com/android/mail/browse/EmlViewerActivity.java
index a1c33d0..56cdae6 100644
--- a/src/com/android/mail/browse/EmlViewerActivity.java
+++ b/src/com/android/mail/browse/EmlViewerActivity.java
@@ -19,30 +19,21 @@
import android.app.ActionBar;
import android.app.Activity;
-import android.content.ContentResolver;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.view.MenuItem;
-import android.webkit.WebView;
-import com.android.emailcommon.TempDirectory;
-import com.android.emailcommon.internet.MimeMessage;
-import com.android.emailcommon.mail.MessagingException;
import com.android.mail.R;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.android.mail.utils.MimeType;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
public class EmlViewerActivity extends Activity {
private static final String LOG_TAG = LogTag.getLogTag();
- private WebView mWebView;
-
+ private static final String FRAGMENT_TAG = "eml_message_fragment";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -50,7 +41,6 @@
final ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
- mWebView = (WebView) findViewById(R.id.webview);
final Intent intent = getIntent();
final String action = intent.getAction();
@@ -58,7 +48,14 @@
if (Intent.ACTION_VIEW.equals(action) &&
MimeType.EML_ATTACHMENT_CONTENT_TYPES.contains(type)) {
- openEmlFile(intent.getData());
+ final FragmentManager manager = getFragmentManager();
+
+ if (manager.findFragmentByTag(FRAGMENT_TAG) == null) {
+ final FragmentTransaction transaction = manager.beginTransaction();
+ transaction.add(R.id.eml_root,
+ EmlMessageViewFragment.newInstance(intent.getData()), FRAGMENT_TAG);
+ transaction.commit();
+ }
} else {
LogUtils.wtf(LOG_TAG,
"Entered EmlViewerActivity with wrong intent action or type: %s, %s",
@@ -67,32 +64,7 @@
}
}
- private void openEmlFile(Uri uri) {
- TempDirectory.setTempDirectory(this);
- final ContentResolver resolver = getContentResolver();
- final InputStream stream;
- try {
- stream = resolver.openInputStream(uri);
- } catch (FileNotFoundException e) {
- // TODO handle exception
- return;
- }
- final MimeMessage mimeMessage;
- final ConversationMessage convMessage;
- try {
- mimeMessage = new MimeMessage(stream);
- convMessage = new ConversationMessage(mimeMessage);
- } catch (IOException e) {
- // TODO handle exception
- return;
- } catch (MessagingException e) {
- // TODO handle exception
- return;
- }
-
- mWebView.loadDataWithBaseURL("", convMessage.getBodyAsHtml(), "text/html", "utf-8", null);
- }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
index 1083002..5036818 100644
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -55,6 +55,7 @@
import com.android.mail.preferences.MailPrefs;
import com.android.mail.providers.Account;
import com.android.mail.providers.Address;
+import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
import com.android.mail.providers.Message;
import com.android.mail.providers.UIProvider;
@@ -217,6 +218,8 @@
private VeiledAddressMatcher mVeiledMatcher;
+ private boolean mIsViewOnlyMode = false;
+
public interface MessageHeaderViewCallbacks {
void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight);
@@ -230,6 +233,7 @@
void showExternalResources(String senderRawAddress);
boolean supportsMessageTransforms();
+
String getMessageTransforms(Message msg);
}
@@ -331,7 +335,7 @@
mLeftSpacer.setVisibility(View.VISIBLE);
mRightSpacer.setVisibility(View.VISIBLE);
} else {
- setBackgroundColor(android.R.color.white);
+ setBackgroundColor(getResources().getColor(android.R.color.white));
// scrolling layer does have padding so we don't need spacers
mLeftSpacer.setVisibility(View.GONE);
mRightSpacer.setVisibility(View.GONE);
@@ -368,7 +372,7 @@
}
private Account getAccount() {
- return mAccountController.getAccount();
+ return mAccountController != null ? mAccountController.getAccount() : null;
}
public void bind(MessageHeaderItem headerItem, boolean measureOnly) {
@@ -437,10 +441,14 @@
mStarView.setContentDescription(getResources().getString(
mStarView.isSelected() ? R.string.remove_star : R.string.add_star));
mStarShown = true;
- for (Folder folder : mMessage.getConversation().getRawFolders()) {
- if (folder.isTrash()) {
- mStarShown = false;
- break;
+
+ final Conversation conversation = mMessage.getConversation();
+ if (conversation != null) {
+ for (Folder folder : conversation.getRawFolders()) {
+ if (folder.isTrash()) {
+ mStarShown = false;
+ break;
+ }
}
}
@@ -600,7 +608,18 @@
private void updateChildVisibility() {
// Too bad this can't be done with an XML state list...
- if (isExpanded()) {
+ if (mIsViewOnlyMode) {
+ setMessageDetailsVisibility(VISIBLE);
+
+
+ setChildVisibility(GONE, mReplyButton, mReplyAllButton, mForwardButton,
+ mOverflowButton, mDraftIcon, mEditDraftButton, mStarView,
+ mAttachmentIcon, mUpperDateView);
+ setChildVisibility(VISIBLE, mPhotoView, mPhotoSpacerView,
+ mSenderEmailView);
+
+ setChildMarginRight(mTitleContainerView, 0);
+ } else if (isExpanded()) {
int normalVis, draftVis;
setMessageDetailsVisibility((mIsSnappy) ? GONE : VISIBLE);
@@ -669,8 +688,9 @@
return;
}
- final boolean defaultReplyAll = getAccount().settings.replyBehavior
- == UIProvider.DefaultReplyBehavior.REPLY_ALL;
+ final Account account = getAccount();
+ final boolean defaultReplyAll = (account != null) ? account.settings.replyBehavior
+ == UIProvider.DefaultReplyBehavior.REPLY_ALL : false;
setChildVisibility(defaultReplyAll ? GONE : VISIBLE, mReplyButton);
setChildVisibility(defaultReplyAll ? VISIBLE : GONE, mReplyAllButton);
}
@@ -702,7 +722,8 @@
final String address = email.getAddress();
// Check if the address here is a veiled address. If it is, we need to display an
// alternate layout
- final boolean isVeiledAddress = mVeiledMatcher.isVeiledAddress(address);
+ final boolean isVeiledAddress = mVeiledMatcher != null &&
+ mVeiledMatcher.isVeiledAddress(address);
final String addressShown;
if (isVeiledAddress) {
// Add the warning at the end of the name, and remove the address. The alternate
@@ -797,7 +818,7 @@
final Address email = getAddress(mAddressCache, rawAddrs[i]);
final String emailAddress = email.getAddress();
final String name;
- if (mMatcher.isVeiledAddress(emailAddress)) {
+ if (mMatcher != null && mMatcher.isVeiledAddress(emailAddress)) {
if (TextUtils.isEmpty(email.getName())) {
// Let's write something more readable.
name = mContext.getString(VeiledAddressMatcher.VEILED_SUMMARY_UNKNOWN);
@@ -983,6 +1004,16 @@
return handled;
}
+ /**
+ * Set to true if the user should not be able to perfrom message actions
+ * on the message such as reply/reply all/forward/star/etc.
+ *
+ * Default is false.
+ */
+ public void setViewOnlyMode(boolean isViewOnlyMode) {
+ mIsViewOnlyMode = isViewOnlyMode;
+ }
+
public void setExpandable(boolean expandable) {
mExpandable = expandable;
}
@@ -1194,7 +1225,11 @@
if (mMessageHeaderItem != null) {
mMessageHeaderItem.setShowImages(true);
}
- showImagePromptAlways(false);
+ if (mIsViewOnlyMode) {
+ hideShowImagePrompt();
+ } else {
+ showImagePromptAlways(false);
+ }
break;
case SHOW_IMAGE_PROMPT_ALWAYS:
mMessage.markAlwaysShowImages(getQueryHandler(), 0 /* token */, null /* cookie */);
@@ -1233,8 +1268,10 @@
}
if (!mCollapsedDetailsValid) {
if (mMessageHeaderItem.recipientSummaryText == null) {
+ final Account account = getAccount();
+ final String name = (account != null) ? account.name : "";
mMessageHeaderItem.recipientSummaryText = getRecipientSummaryText(getContext(),
- getAccount().name, mMyName, mTo, mCc, mBcc, mAddressCache, mVeiledMatcher);
+ name, mMyName, mTo, mCc, mBcc, mAddressCache, mVeiledMatcher);
}
((TextView) findViewById(R.id.recipients_summary))
.setText(mMessageHeaderItem.recipientSummaryText);
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index b4bd5fd..bb37c29 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -30,14 +30,12 @@
import android.view.MenuInflater;
import android.view.MenuItem;
-import com.android.mail.FormattedDateBuilder;
import com.android.mail.R;
import com.android.mail.browse.ConversationAccountController;
import com.android.mail.browse.ConversationMessage;
import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks;
import com.android.mail.browse.MessageCursor;
import com.android.mail.browse.MessageCursor.ConversationController;
-import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks;
import com.android.mail.content.ObjectCursor;
import com.android.mail.content.ObjectCursorLoader;
import com.android.mail.providers.Account;
@@ -59,7 +57,7 @@
public abstract class AbstractConversationViewFragment extends Fragment implements
- ConversationController, ConversationAccountController, MessageHeaderViewCallbacks,
+ ConversationController, ConversationAccountController,
ConversationViewHeaderCallbacks {
private static final String ARG_ACCOUNT = "account";
@@ -70,7 +68,6 @@
protected static final int CONTACT_LOADER = 1;
protected ControllableActivity mActivity;
private final MessageLoaderCallbacks mMessageLoaderCallbacks = new MessageLoaderCallbacks();
- protected FormattedDateBuilder mDateBuilder;
private ContactLoaderCallbacks mContactLoaderCallbacks;
private MenuItem mChangeFoldersMenuItem;
protected Conversation mConversation;
@@ -82,6 +79,7 @@
* Must be instantiated in a derived class's onCreate.
*/
protected AbstractConversationWebViewClient mWebViewClient;
+
/**
* Cache of email address strings to parsed Address objects.
* <p>
@@ -241,7 +239,7 @@
}
mActivity = (ControllableActivity) activity;
mContext = activity.getApplicationContext();
- mDateBuilder = new FormattedDateBuilder((Context) mActivity);
+ mWebViewClient.setContext(mContext);
mAccount = mAccountObserver.initialize(mActivity.getAccountController());
mWebViewClient.setAccount(mAccount);
}
@@ -331,6 +329,8 @@
mHasConversationBeenTransformed && !mHasConversationTransformBeenReverted);
}
+ abstract boolean supportsMessageTransforms();
+
// BEGIN conversation header callbacks
@Override
public void onFoldersClicked() {
diff --git a/src/com/android/mail/ui/AbstractConversationWebViewClient.java b/src/com/android/mail/ui/AbstractConversationWebViewClient.java
index 139eaa5..2605c72 100644
--- a/src/com/android/mail/ui/AbstractConversationWebViewClient.java
+++ b/src/com/android/mail/ui/AbstractConversationWebViewClient.java
@@ -45,17 +45,20 @@
private static final String LOG_TAG = LogTag.getLogTag();
private Account mAccount;
- private final Context mContext;
+ private Context mContext;
- public AbstractConversationWebViewClient(Context context, Account account) {
+ public AbstractConversationWebViewClient(Account account) {
mAccount = account;
- mContext = context;
}
public void setAccount(Account account) {
mAccount = account;
}
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (mContext == null) {
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index a110ce2..6de375e 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -17,7 +17,6 @@
package com.android.mail.ui;
-
import android.content.ContentResolver;
import android.content.Context;
import android.content.Loader;
@@ -42,7 +41,6 @@
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
-import android.webkit.WebViewClient;
import android.widget.TextView;
import com.android.mail.FormattedDateBuilder;
@@ -85,13 +83,12 @@
import java.util.Map;
import java.util.Set;
-
/**
* The conversation view UI component.
*/
public final class ConversationViewFragment extends AbstractConversationViewFragment implements
- SuperCollapsedBlock.OnClickListener,
- OnLayoutChangeListener {
+ SuperCollapsedBlock.OnClickListener, OnLayoutChangeListener,
+ MessageHeaderView.MessageHeaderViewCallbacks {
private static final String LOG_TAG = LogTag.getLogTag();
public static final String LAYOUT_TAG = "ConvLayout";
@@ -323,7 +320,7 @@
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
- mWebViewClient = new ConversationWebViewClient(getContext(), mAccount);
+ mWebViewClient = new ConversationWebViewClient(mAccount);
if (savedState != null) {
mWebViewYPercent = savedState.getFloat(BUNDLE_KEY_WEBVIEW_Y_PERCENT);
@@ -738,8 +735,8 @@
for (int i = blockToReplace.getStart(), end = blockToReplace.getEnd(); i <= end; i++) {
cursor.moveToPosition(i);
final ConversationMessage msg = cursor.getMessage();
- final MessageHeaderItem header = mAdapter.newMessageHeaderItem(msg,
- false /* expanded */, mViewState.getShouldShowImages(msg));
+ final MessageHeaderItem header = ConversationViewAdapter.newMessageHeaderItem(
+ mAdapter, msg, false /* expanded */, mViewState.getShouldShowImages(msg));
final MessageFooterItem footer = mAdapter.newMessageFooterItem(header);
final int headerPx = measureOverlayHeight(header);
@@ -979,8 +976,8 @@
}
private class ConversationWebViewClient extends AbstractConversationWebViewClient {
- public ConversationWebViewClient(Context context, Account account) {
- super(context, account);
+ public ConversationWebViewClient(Account account) {
+ super(account);
}
@Override
@@ -1012,8 +1009,8 @@
for (Address addr : cacheCopy) {
emailAddresses.add(addr.getAddress());
}
- ContactLoaderCallbacks callbacks = getContactInfoSource();
- getContactInfoSource().setSenders(emailAddresses);
+ final ContactLoaderCallbacks callbacks = getContactInfoSource();
+ callbacks.setSenders(emailAddresses);
getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks);
}
diff --git a/src/com/android/mail/ui/SecureConversationViewController.java b/src/com/android/mail/ui/SecureConversationViewController.java
new file mode 100644
index 0000000..a3e6ec8
--- /dev/null
+++ b/src/com/android/mail/ui/SecureConversationViewController.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.ui;
+
+import android.app.Fragment;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebSettings;
+
+import com.android.mail.FormattedDateBuilder;
+import com.android.mail.R;
+import com.android.mail.browse.ConversationMessage;
+import com.android.mail.browse.ConversationViewAdapter;
+import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
+import com.android.mail.browse.ConversationViewHeader;
+import com.android.mail.browse.MessageFooterView;
+import com.android.mail.browse.MessageHeaderView;
+import com.android.mail.browse.MessageScrollView;
+import com.android.mail.browse.MessageWebView;
+import com.android.mail.providers.Message;
+import com.android.mail.utils.ConversationViewUtils;
+
+/**
+ * Controller to do most of the heavy lifting for {@link SecureConversationViewFragment}
+ * and {@link com.android.mail.browse.EmlMessageViewFragment}. Currently that work is
+ * pretty much the rendering logic.
+ */
+public class SecureConversationViewController implements
+ MessageHeaderView.MessageHeaderViewCallbacks {
+ private static final String BEGIN_HTML =
+ "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
+ private static final String END_HTML = "</div></body>";
+
+ private final SecureConversationViewControllerCallbacks mCallbacks;
+
+ private MessageWebView mWebView;
+ private ConversationViewHeader mConversationHeaderView;
+ private MessageHeaderView mMessageHeaderView;
+ private MessageFooterView mMessageFooterView;
+ private ConversationMessage mMessage;
+ private MessageScrollView mScrollView;
+
+ private ConversationViewProgressController mProgressController;
+ private FormattedDateBuilder mDateBuilder;
+
+ private int mSideMarginInWebPx;
+
+ public SecureConversationViewController(SecureConversationViewControllerCallbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.secure_conversation_view, container, false);
+ mScrollView = (MessageScrollView) rootView.findViewById(R.id.scroll_view);
+ mConversationHeaderView = (ConversationViewHeader) rootView.findViewById(R.id.conv_header);
+ mMessageHeaderView = (MessageHeaderView) rootView.findViewById(R.id.message_header);
+ mMessageFooterView = (MessageFooterView) rootView.findViewById(R.id.message_footer);
+
+ mProgressController = new ConversationViewProgressController(
+ mCallbacks.getFragment(), mCallbacks.getHandler());
+ mProgressController.instantiateProgressIndicators(rootView);
+ mWebView = (MessageWebView) rootView.findViewById(R.id.webview);
+ mWebView.setWebViewClient(mCallbacks.getWebViewClient());
+ mWebView.setFocusable(false);
+ final WebSettings settings = mWebView.getSettings();
+
+ settings.setJavaScriptEnabled(false);
+ settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+
+ ConversationViewUtils.setTextZoom(mCallbacks.getFragment().getResources(), settings);
+
+ settings.setSupportZoom(true);
+ settings.setBuiltInZoomControls(true);
+ settings.setDisplayZoomControls(false);
+
+ mScrollView.setInnerScrollableView(mWebView);
+
+ return rootView;
+ }
+
+ public void onActivityCreated(Bundle savedInstanceState) {
+ mCallbacks.setupConversationHeaderView(mConversationHeaderView);
+
+ final Fragment fragment = mCallbacks.getFragment();
+
+ mDateBuilder = new FormattedDateBuilder(fragment.getActivity());
+ mMessageHeaderView.initialize(mDateBuilder,
+ mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
+ mMessageHeaderView.setExpandMode(MessageHeaderView.POPUP_MODE);
+ mMessageHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
+ mMessageHeaderView.setCallbacks(this);
+ mMessageHeaderView.setExpandable(false);
+ mMessageHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
+
+ mCallbacks.setupMessageHeaderVeiledMatcher(mMessageHeaderView);
+
+ mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager());
+
+ mCallbacks.startMessageLoader();
+
+ mProgressController.showLoadingStatus(mCallbacks.isViewVisibleToUser());
+
+ final Resources r = mCallbacks.getFragment().getResources();
+ mSideMarginInWebPx = (int) ((r.getDimensionPixelOffset(
+ R.dimen.conversation_view_margin_side) + r.getDimensionPixelOffset(
+ R.dimen.conversation_message_content_margin_side)) / r.getDisplayMetrics().density);
+ }
+
+ /**
+ * Populate the adapter with overlay views (message headers, super-collapsed
+ * blocks, a conversation header), and return an HTML document with spacer
+ * divs inserted for all overlays.
+ */
+ public void renderMessage(ConversationMessage message) {
+ mMessage = message;
+
+ mWebView.getSettings().setBlockNetworkImage(!mMessage.alwaysShowImages);
+
+ // Add formatting to message body
+ // At this point, only adds margins.
+ StringBuilder dataBuilder = new StringBuilder(
+ String.format(BEGIN_HTML, mSideMarginInWebPx));
+ dataBuilder.append(mMessage.getBodyAsHtml());
+ dataBuilder.append(END_HTML);
+
+ mWebView.loadDataWithBaseURL(mCallbacks.getBaseUri(), dataBuilder.toString(),
+ "text/html", "utf-8", null);
+ final MessageHeaderItem item = ConversationViewAdapter.newMessageHeaderItem(
+ null, mMessage, true, mMessage.alwaysShowImages);
+ mMessageHeaderView.bind(item, false);
+ if (mMessage.hasAttachments) {
+ mMessageFooterView.setVisibility(View.VISIBLE);
+ mMessageFooterView.bind(item, false);
+ }
+ }
+
+ public ConversationMessage getMessage() {
+ return mMessage;
+ }
+
+ public ConversationViewHeader getConversationHeaderView() {
+ return mConversationHeaderView;
+ }
+
+ public void dismissLoadingStatus() {
+ mProgressController.dismissLoadingStatus();
+ }
+
+ public void setSubject(String subject) {
+ mConversationHeaderView.setSubject(subject);
+ }
+
+ // Start MessageHeaderViewCallbacks implementations
+
+ @Override
+ public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight) {
+ // Do nothing.
+ }
+
+ @Override
+ public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeight) {
+ // Do nothing.
+ }
+
+ @Override
+ public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, int heightBefore) {
+ // Do nothing.
+ }
+
+ @Override
+ public void showExternalResources(final Message msg) {
+ mWebView.getSettings().setBlockNetworkImage(false);
+ }
+
+ @Override
+ public void showExternalResources(final String rawSenderAddress) {
+ mWebView.getSettings().setBlockNetworkImage(false);
+ }
+
+ @Override
+ public boolean supportsMessageTransforms() {
+ return false;
+ }
+
+ @Override
+ public String getMessageTransforms(final Message msg) {
+ return null;
+ }
+
+ // End MessageHeaderViewCallbacks implementations
+}
diff --git a/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java b/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
new file mode 100644
index 0000000..06857ed
--- /dev/null
+++ b/src/com/android/mail/ui/SecureConversationViewControllerCallbacks.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.ui;
+
+import android.app.Fragment;
+import android.os.Handler;
+
+import com.android.mail.ContactInfoSource;
+import com.android.mail.browse.ConversationAccountController;
+import com.android.mail.browse.ConversationViewHeader;
+import com.android.mail.browse.MessageHeaderView;
+import com.android.mail.providers.Address;
+
+import java.util.Map;
+
+/**
+ * Callbacks for fragments that use the {@link SecureConversationViewController}.
+ */
+public interface SecureConversationViewControllerCallbacks {
+ public Handler getHandler();
+ public AbstractConversationWebViewClient getWebViewClient();
+ public Fragment getFragment();
+ public void setupConversationHeaderView(ConversationViewHeader headerView);
+ public boolean isViewVisibleToUser();
+ public ContactInfoSource getContactInfoSource();
+ public ConversationAccountController getConversationAccountController();
+ public Map<String, Address> getAddressCache();
+ public void setupMessageHeaderVeiledMatcher(MessageHeaderView messageHeaderView);
+ public void startMessageLoader();
+ public String getBaseUri();
+ public boolean isViewOnlyMode();
+}
diff --git a/src/com/android/mail/ui/SecureConversationViewFragment.java b/src/com/android/mail/ui/SecureConversationViewFragment.java
index b94be5d..c3efd59 100644
--- a/src/com/android/mail/ui/SecureConversationViewFragment.java
+++ b/src/com/android/mail/ui/SecureConversationViewFragment.java
@@ -17,60 +17,43 @@
package com.android.mail.ui;
-import android.content.Context;
+import android.app.Fragment;
import android.content.Loader;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.webkit.WebSettings;
-import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import com.android.mail.R;
+import com.android.mail.browse.ConversationAccountController;
import com.android.mail.browse.ConversationMessage;
-import com.android.mail.browse.ConversationViewAdapter;
-import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
import com.android.mail.browse.ConversationViewHeader;
import com.android.mail.browse.MessageCursor;
-import com.android.mail.browse.MessageFooterView;
import com.android.mail.browse.MessageHeaderView;
-import com.android.mail.browse.MessageScrollView;
-import com.android.mail.browse.MessageWebView;
import com.android.mail.content.ObjectCursor;
import com.android.mail.providers.Account;
+import com.android.mail.providers.Address;
import com.android.mail.providers.Conversation;
-import com.android.mail.providers.Message;
-import com.android.mail.utils.ConversationViewUtils;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
-public class SecureConversationViewFragment extends AbstractConversationViewFragment {
+public class SecureConversationViewFragment extends AbstractConversationViewFragment
+ implements SecureConversationViewControllerCallbacks {
private static final String LOG_TAG = LogTag.getLogTag();
- private static final String BEGIN_HTML =
- "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
- private static final String END_HTML = "</div></body>";
-
- private MessageWebView mWebView;
- private ConversationViewHeader mConversationHeaderView;
- private MessageHeaderView mMessageHeaderView;
- private MessageFooterView mMessageFooterView;
- private ConversationMessage mMessage;
- private MessageScrollView mScrollView;
-
- private int mSideMarginInWebPx;
-
- private ConversationViewProgressController mProgressController;
+ private SecureConversationViewController mViewController;
private class SecureConversationWebViewClient extends AbstractConversationWebViewClient {
- public SecureConversationWebViewClient(Context context, Account account) {
- super(context, account);
+ public SecureConversationWebViewClient(Account account) {
+ super(account);
}
@Override
@@ -79,7 +62,19 @@
onConversationSeen();
}
- mProgressController.dismissLoadingStatus();
+ mViewController.dismissLoadingStatus();
+
+ final Set<String> emailAddresses = Sets.newHashSet();
+ final List<Address> cacheCopy;
+ synchronized (mAddressCache) {
+ cacheCopy = ImmutableList.copyOf(mAddressCache.values());
+ }
+ for (Address addr : cacheCopy) {
+ emailAddresses.add(addr.getAddress());
+ }
+ final ContactLoaderCallbacks callbacks = getContactInfoSource();
+ callbacks.setSenders(emailAddresses);
+ getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks);
}
};
@@ -109,76 +104,93 @@
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
- mWebViewClient = new SecureConversationWebViewClient(getContext(), mAccount);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mConversationHeaderView.setCallbacks(this, this);
- mConversationHeaderView.setFolders(mConversation);
- mConversationHeaderView.setSubject(mConversation.subject);
- mMessageHeaderView.initialize(mDateBuilder, this, mAddressCache);
- mMessageHeaderView.setExpandMode(MessageHeaderView.POPUP_MODE);
- mMessageHeaderView.setContactInfoSource(getContactInfoSource());
- mMessageHeaderView.setCallbacks(this);
- mMessageHeaderView.setExpandable(false);
- mMessageHeaderView.setVeiledMatcher(
- ((ControllableActivity) getActivity()).getAccountController()
- .getVeiledAddressMatcher());
- mMessageFooterView.initialize(getLoaderManager(), getFragmentManager());
- getLoaderManager().initLoader(MESSAGE_LOADER, null, getMessageLoaderCallbacks());
- mProgressController.showLoadingStatus(isUserVisible());
-
- final Resources r = getResources();
- mSideMarginInWebPx = (int) ((r.getDimensionPixelOffset(
- R.dimen.conversation_view_margin_side) + r.getDimensionPixelOffset(
- R.dimen.conversation_message_content_margin_side)) / r.getDisplayMetrics().density);
+ mWebViewClient = new SecureConversationWebViewClient(mAccount);
+ mViewController = new SecureConversationViewController(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.secure_conversation_view, container, false);
- mScrollView = (MessageScrollView) rootView.findViewById(R.id.scroll_view);
- mConversationHeaderView = (ConversationViewHeader) rootView.findViewById(R.id.conv_header);
- mMessageHeaderView = (MessageHeaderView) rootView.findViewById(R.id.message_header);
- mMessageFooterView = (MessageFooterView) rootView.findViewById(R.id.message_footer);
-
- mProgressController = new ConversationViewProgressController(this, getHandler());
- mProgressController.instantiateProgressIndicators(rootView);
- mWebView = (MessageWebView) rootView.findViewById(R.id.webview);
- mWebView.setWebViewClient(mWebViewClient);
- mWebView.setFocusable(false);
- final WebSettings settings = mWebView.getSettings();
-
- settings.setJavaScriptEnabled(false);
- settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
-
- ConversationViewUtils.setTextZoom(getResources(), settings);
-
- settings.setSupportZoom(true);
- settings.setBuiltInZoomControls(true);
- settings.setDisplayZoomControls(false);
-
- mScrollView.setInnerScrollableView(mWebView);
-
- return rootView;
+ return mViewController.onCreateView(inflater, container, savedInstanceState);
}
@Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mViewController.onActivityCreated(savedInstanceState);
+ }
+
+ // Start implementations of SecureConversationViewControllerCallbacks
+
+ @Override
+ public Fragment getFragment() {
+ return this;
+ }
+
+ @Override
+ public AbstractConversationWebViewClient getWebViewClient() {
+ return mWebViewClient;
+ }
+
+ @Override
+ public void setupConversationHeaderView(ConversationViewHeader headerView) {
+ headerView.setCallbacks(this, this);
+ headerView.setFolders(mConversation);
+ headerView.setSubject(mConversation.subject);
+ }
+
+ @Override
+ public boolean isViewVisibleToUser() {
+ return isUserVisible();
+ }
+
+ @Override
+ public ConversationAccountController getConversationAccountController() {
+ return this;
+ }
+
+ @Override
+ public Map<String, Address> getAddressCache() {
+ return mAddressCache;
+ }
+
+ @Override
+ public void setupMessageHeaderVeiledMatcher(MessageHeaderView messageHeaderView) {
+ messageHeaderView.setVeiledMatcher(
+ ((ControllableActivity) getActivity()).getAccountController()
+ .getVeiledAddressMatcher());
+ }
+
+ @Override
+ public void startMessageLoader() {
+ getLoaderManager().initLoader(MESSAGE_LOADER, null, getMessageLoaderCallbacks());
+ }
+
+ @Override
+ public String getBaseUri() {
+ return mBaseUri;
+ }
+
+ @Override
+ public boolean isViewOnlyMode() {
+ return false;
+ }
+
+ // End implementations of SecureConversationViewControllerCallbacks
+
+ @Override
protected void markUnread() {
super.markUnread();
// Ignore unsafe calls made after a fragment is detached from an activity
final ControllableActivity activity = (ControllableActivity) getActivity();
- if (activity == null || mConversation == null || mMessage == null) {
+ final ConversationMessage message = mViewController.getMessage();
+ if (activity == null || mConversation == null || message == null) {
LogUtils.w(LOG_TAG, "ignoring markUnread for conv=%s",
mConversation != null ? mConversation.id : 0);
return;
}
final HashSet<Uri> uris = new HashSet<Uri>();
- uris.add(mMessage.uri);
+ uris.add(message.uri);
activity.getConversationUpdater().markConversationMessagesUnread(mConversation, uris,
mViewState.getConversationInfo());
}
@@ -204,41 +216,6 @@
}
@Override
- public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight) {
- // Do nothing.
- }
-
- @Override
- public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeight) {
- // Do nothing.
- }
-
- @Override
- public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, int heightbefore) {
- // Do nothing.
- }
-
- @Override
- public void showExternalResources(final Message msg) {
- mWebView.getSettings().setBlockNetworkImage(false);
- }
-
- @Override
- public void showExternalResources(final String rawSenderAddress) {
- mWebView.getSettings().setBlockNetworkImage(false);
- }
-
- @Override
- public boolean supportsMessageTransforms() {
- return false;
- }
-
- @Override
- public String getMessageTransforms(final Message msg) {
- return null;
- }
-
- @Override
protected void onMessageCursorLoadFinished(Loader<ObjectCursor<ConversationMessage>> loader,
MessageCursor newCursor, MessageCursor oldCursor) {
// ignore cursors that are still loading results
@@ -250,48 +227,25 @@
// Activity is finishing, just bail.
return;
}
- renderMessageBodies(newCursor);
- }
-
- /**
- * Populate the adapter with overlay views (message headers, super-collapsed
- * blocks, a conversation header), and return an HTML document with spacer
- * divs inserted for all overlays.
- */
- private void renderMessageBodies(MessageCursor messageCursor) {
- if (!messageCursor.moveToFirst()) {
+ if (!newCursor.moveToFirst()) {
LogUtils.e(LOG_TAG, "unable to open message cursor");
return;
}
- mMessage = messageCursor.getMessage();
- mWebView.getSettings().setBlockNetworkImage(!mMessage.alwaysShowImages);
- // Add formatting to message body
- // At this point, only adds margins.
- StringBuilder dataBuilder = new StringBuilder(
- String.format(BEGIN_HTML, mSideMarginInWebPx));
- dataBuilder.append(mMessage.getBodyAsHtml());
- dataBuilder.append(END_HTML);
-
- mWebView.loadDataWithBaseURL(mBaseUri, dataBuilder.toString(), "text/html", "utf-8", null);
- final ConversationViewAdapter adapter = new ConversationViewAdapter(mActivity, null, null,
- null, null, null, null, null, null);
- final MessageHeaderItem item = adapter.newMessageHeaderItem(mMessage, true,
- mMessage.alwaysShowImages);
- mMessageHeaderView.bind(item, false);
- if (mMessage.hasAttachments) {
- mMessageFooterView.setVisibility(View.VISIBLE);
- mMessageFooterView.bind(item, false);
- }
+ mViewController.renderMessage(newCursor.getMessage());
}
@Override
public void onConversationUpdated(Conversation conv) {
- final ConversationViewHeader headerView = mConversationHeaderView;
+ final ConversationViewHeader headerView = mViewController.getConversationHeaderView();
if (headerView != null) {
headerView.onConversationUpdated(conv);
headerView.setSubject(conv.subject);
}
}
+ // Need this stub here
+ public boolean supportsMessageTransforms() {
+ return false;
+ }
}