bottom-aligned attachments
Refactor attachment view logic out of MessageHeaderView into
its own view class, paired with MessageFooterItem.
Change-Id: Ic6539f52d53be0e2d23144abec1baffb9368f8cd
diff --git a/res/layout/conversation_message_attachment.xml b/res/layout/conversation_message_attachment.xml
index db9463f..eb15614 100644
--- a/res/layout/conversation_message_attachment.xml
+++ b/res/layout/conversation_message_attachment.xml
@@ -18,7 +18,7 @@
<com.android.mail.browse.MessageHeaderAttachment xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
android:orientation="vertical"
android:divider="?android:attr/dividerHorizontal"
android:showDividers="middle"
diff --git a/res/layout/conversation_message_attachments.xml b/res/layout/conversation_message_footer.xml
similarity index 77%
rename from res/layout/conversation_message_attachments.xml
rename to res/layout/conversation_message_footer.xml
index 6bf6862..ad675d3 100644
--- a/res/layout/conversation_message_attachments.xml
+++ b/res/layout/conversation_message_footer.xml
@@ -15,12 +15,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.mail.browse.MessageFooterView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/attachments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_marginTop="8dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp">
-</LinearLayout>
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp">
+</com.android.mail.browse.MessageFooterView>
diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java
index 75ed84d..f27ca27 100644
--- a/src/com/android/mail/browse/ConversationViewAdapter.java
+++ b/src/com/android/mail/browse/ConversationViewAdapter.java
@@ -164,7 +164,7 @@
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, mAccount, mLoaderManager, false /* defaultReplyAll */);
+ v.initialize(mDateBuilder, mAccount, false /* defaultReplyAll */);
v.setCallbacks(mMessageCallbacks);
return v;
}
@@ -205,20 +205,21 @@
@Override
public View createView(Context context, LayoutInflater inflater, ViewGroup parent) {
- // TODO
- return new View(context);
+ final MessageFooterView v = (MessageFooterView) inflater.inflate(
+ R.layout.conversation_message_footer, parent, false);
+ v.initialize(mLoaderManager);
+ return v;
}
@Override
public void bindView(View v) {
- final Message message = headerItem.message;
- // TODO
+ final MessageFooterView attachmentsView = (MessageFooterView) v;
+ attachmentsView.bind(headerItem.message, headerItem.expanded);
}
@Override
public int measureHeight(View v, ViewGroup parent) {
- // TODO
- return 0;
+ return Utils.measureViewHeight(v, parent);
}
@Override
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
new file mode 100644
index 0000000..fa86e12
--- /dev/null
+++ b/src/com/android/mail/browse/MessageFooterView.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2012 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 com.google.common.collect.Lists;
+
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+
+import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
+import com.android.mail.browse.ConversationContainer.DetachListener;
+import com.android.mail.providers.Attachment;
+import com.android.mail.providers.Message;
+import com.android.mail.utils.LogUtils;
+
+import java.util.List;
+
+public class MessageFooterView extends LinearLayout implements DetachListener,
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ private Message mMessage;
+ private LoaderManager mLoaderManager;
+ private AttachmentCursor mAttachmentsCursor;
+ private boolean mIsExpanded;
+ private LayoutInflater mInflater;
+
+ /**
+ * An easy way for the conversation view to disable immediately kicking off attachment loaders
+ * when measuring overlays during the initial render.
+ */
+ private static boolean sEnableAttachmentLoaders = true;
+
+ private static final String LOG_TAG = new LogUtils().getLogTag();
+
+ public MessageFooterView(Context context) {
+ this(context, null);
+ }
+
+ public MessageFooterView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ /**
+ * Utility method that can be used to temporarily disable attachment loaders in all
+ * {@link MessageFooterView}s.
+ *
+ * @param enabled true to enable loaders
+ */
+ public static void enableAttachmentLoaders(boolean enabled) {
+ sEnableAttachmentLoaders = enabled;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ }
+
+ public void initialize(LoaderManager loaderManager) {
+ mLoaderManager = loaderManager;
+ }
+
+ public void bind(Message msg, boolean expanded) {
+ mMessage = msg;
+ mIsExpanded = expanded;
+
+ removeAllViewsInLayout();
+
+ // kick off load of Attachment objects in background thread
+ final Integer attachmentLoaderId = getAttachmentLoaderId();
+ if (sEnableAttachmentLoaders && attachmentLoaderId != null) {
+ LogUtils.d(LOG_TAG, "binding footer view, calling initLoader for message %d",
+ attachmentLoaderId);
+ mLoaderManager.initLoader(attachmentLoaderId, Bundle.EMPTY, this);
+ }
+
+ if (mIsExpanded) {
+ setVisibility(VISIBLE);
+ // Do an initial render if initLoader didn't already do one
+ if (getChildCount() == 0) {
+ renderAttachments();
+ }
+ } else {
+ setVisibility(GONE);
+ }
+ }
+
+ private void destroyLoader() {
+ final Integer loaderId = getAttachmentLoaderId();
+ if (mLoaderManager != null && loaderId != null) {
+ LogUtils.d(LOG_TAG, "detaching/reusing footer view,"
+ + " calling destroyLoader for message %d", loaderId);
+ mLoaderManager.destroyLoader(loaderId);
+ }
+ }
+
+ private void renderAttachments() {
+ if (!mIsExpanded) {
+ return;
+ }
+
+ List<Attachment> attachments;
+ if (mAttachmentsCursor != null && !mAttachmentsCursor.isClosed()) {
+ int i = -1;
+ attachments = Lists.newArrayList();
+ while (mAttachmentsCursor.moveToPosition(++i)) {
+ attachments.add(mAttachmentsCursor.get());
+ }
+ } else {
+ // before the attachment loader results are in, we can still render immediately using
+ // the basic info in the message's attachmentsJSON
+ attachments = mMessage.getAttachments();
+ }
+ renderAttachments(attachments);
+ }
+
+ private void renderAttachments(List<Attachment> attachments) {
+ for (Attachment attachment : attachments) {
+ MessageHeaderAttachment attachView = (MessageHeaderAttachment) findViewWithTag(
+ attachment.uri);
+
+ if (attachView == null) {
+ attachView = MessageHeaderAttachment.inflate(mInflater, this);
+ attachView.setTag(attachment.uri);
+ addView(attachView);
+ }
+
+ attachView.render(attachment);
+ }
+ }
+
+ private Integer getAttachmentLoaderId() {
+ Integer id = null;
+ if (mMessage != null && mMessage.hasAttachments && mMessage.attachmentListUri != null) {
+ id = mMessage.attachmentListUri.hashCode();
+ }
+ return id;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ destroyLoader();
+ }
+
+ @Override
+ public void onDetachedFromParent() {
+ destroyLoader();
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return new AttachmentLoader(getContext(), mMessage.attachmentListUri);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAttachmentsCursor = (AttachmentCursor) data;
+
+ if (mAttachmentsCursor == null || mAttachmentsCursor.isClosed()) {
+ return;
+ }
+
+ renderAttachments();
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAttachmentsCursor = null;
+ }
+
+}
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
index 30c26d8..0e2cc5d 100644
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -16,14 +16,10 @@
package com.android.mail.browse;
-import android.app.LoaderManager;
import android.content.AsyncQueryHandler;
import android.content.Context;
-import android.content.Loader;
-import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Typeface;
-import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
@@ -47,27 +43,21 @@
import com.android.mail.FormattedDateBuilder;
import com.android.mail.R;
import com.android.mail.SenderInfoLoader.ContactInfo;
-import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
import com.android.mail.compose.ComposeActivity;
import com.android.mail.perf.Timer;
import com.android.mail.providers.Account;
import com.android.mail.providers.Address;
-import com.android.mail.providers.Attachment;
import com.android.mail.providers.Message;
import com.android.mail.providers.UIProvider;
import com.android.mail.utils.LogUtils;
import com.android.mail.utils.Utils;
-
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
import java.io.IOException;
import java.io.StringReader;
-import java.util.List;
public class MessageHeaderView extends LinearLayout implements OnClickListener,
- OnMenuItemClickListener, HeaderBlock, LoaderManager.LoaderCallbacks<Cursor>,
- ConversationContainer.DetachListener {
+ OnMenuItemClickListener, HeaderBlock {
/**
* Cap very long recipient lists during summary construction for efficiency.
@@ -101,7 +91,6 @@
private ViewGroup mCollapsedDetailsView;
private ViewGroup mExpandedDetailsView;
private ViewGroup mImagePromptView;
- private ViewGroup mAttachmentsView;
private View mBottomBorderView;
private ImageView mPresenceView;
@@ -144,11 +133,6 @@
private int mDrawTranslateY;
- /**
- * List of attachments for this message, loaded asynchronously.
- */
- private AttachmentCursor mAttachments;
-
private CharSequence mTimestampShort;
/**
@@ -173,8 +157,6 @@
private boolean mCollapsedDetailsValid;
private boolean mExpandedDetailsValid;
- private LoaderManager mLoaderManager;
-
private final LayoutInflater mInflater;
private AsyncQueryHandler mQueryHandler;
@@ -316,21 +298,12 @@
}
public void initialize(FormattedDateBuilder dateBuilder, Account account,
- LoaderManager loaderManager, boolean defaultReplyAll) {
+ boolean defaultReplyAll) {
mDateBuilder = dateBuilder;
mAccount = account;
- mLoaderManager = loaderManager;
mDefaultReplyAll = defaultReplyAll;
}
- private Integer getAttachmentLoaderId() {
- Integer id = null;
- if (mMessage != null && mMessage.attachmentListUri != null) {
- id = mMessage.attachmentListUri.hashCode();
- }
- return id;
- }
-
public void bind(Message message, boolean expanded, boolean showImagePrompt) {
Timer t = new Timer();
t.start(HEADER_RENDER_TAG);
@@ -352,16 +325,6 @@
mBcc = mMessage.getBccAddresses();
mReplyTo = mMessage.getReplyToAddresses();
- if (mAttachmentsView != null) {
- mAttachmentsView.removeAllViews();
- }
-
- // kick off load of Attachment objects in background thread
- final Integer attachmentLoaderId = getAttachmentLoaderId();
- if (mMessage.hasAttachments && attachmentLoaderId != null) {
- mLoaderManager.initLoader(getAttachmentLoaderId(), Bundle.EMPTY, this);
- }
-
/**
* Turns draft mode on or off. Draft mode hides message operations other
* than "edit", hides contact photo, hides presence, and changes the
@@ -408,49 +371,6 @@
t.pause(HEADER_RENDER_TAG);
}
- // Attachment list loader methods
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- return new AttachmentLoader(getContext(), mMessage.attachmentListUri);
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- mAttachments = (AttachmentCursor) data;
-
- if (mAttachments == null || mAttachments.isClosed()) {
- return;
- }
-
- renderAttachments(mAttachmentsView);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- mAttachments = null;
- }
-
- private void destroyLoader() {
- final Integer loaderId = getAttachmentLoaderId();
- if (mLoaderManager != null && loaderId != null) {
- LogUtils.d(LOG_TAG, "detaching header view, calling destroyLoader for message %d",
- loaderId);
- mLoaderManager.destroyLoader(loaderId);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- destroyLoader();
- }
-
- @Override
- public void onDetachedFromParent() {
- destroyLoader();
- }
-
private boolean isInOutbox() {
// TODO: what should this read? Folder info?
return false;
@@ -901,7 +821,8 @@
hideCollapsedDetails();
hideExpandedDetails();
hideShowImagePrompt();
- hideAttachments();
+ // FIXME: coordinate with matching footer (if exists) to show/hide
+ // hideAttachments();
} else {
setMessageDetailsExpanded(mDetailsExpanded);
if (mShowImagePrompt) {
@@ -909,70 +830,20 @@
} else {
hideShowImagePrompt();
}
+ // FIXME: coordinate with matching footer (if exists) to show/hide
+ /*
if (mMessage.hasAttachments) {
showAttachments();
} else {
hideAttachments();
}
+ */
}
if (mBottomBorderView != null) {
mBottomBorderView.setVisibility(vis);
}
}
- private void showAttachments() {
- if (mAttachmentsView == null) {
- ViewGroup container = (ViewGroup) mInflater.inflate(
- R.layout.conversation_message_attachments, this, false);
-
- addView(container);
- mAttachmentsView = container;
- }
- renderAttachments(mAttachmentsView);
- mAttachmentsView.setVisibility(VISIBLE);
- }
-
- private void renderAttachments(ViewGroup container) {
- if (container == null) {
- return;
- }
-
- List<Attachment> attachments;
- if (mAttachments != null && !mAttachments.isClosed()) {
- int i = -1;
- attachments = Lists.newArrayList();
- while (mAttachments.moveToPosition(++i)) {
- attachments.add(mAttachments.get());
- }
- } else {
- // before the attachment loader results are in, we can still render immediately using
- // the basic info in the message's attachmentsJSON
- attachments = mMessage.getAttachments();
- }
- renderAttachments(attachments, container);
- }
-
- private void renderAttachments(List<Attachment> attachments, ViewGroup container) {
- for (Attachment attachment : attachments) {
- MessageHeaderAttachment attachView = (MessageHeaderAttachment)
- container.findViewWithTag(attachment.uri);
-
- if (attachView == null) {
- attachView = MessageHeaderAttachment.inflate(mInflater, container);
- attachView.setTag(attachment.uri);
- container.addView(attachView);
- }
-
- attachView.render(attachment);
- }
- }
-
- private void hideAttachments() {
- if (mAttachmentsView != null) {
- mAttachmentsView.setVisibility(GONE);
- }
- }
-
public void hideMessageDetails() {
setMessageDetailsVisibility(GONE);
}
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index b669c26..8448023 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -51,6 +51,7 @@
import com.android.mail.browse.ConversationViewHeader;
import com.android.mail.browse.ConversationWebView;
import com.android.mail.browse.MessageCursor;
+import com.android.mail.browse.MessageFooterView;
import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks;
import com.android.mail.providers.Account;
import com.android.mail.providers.Conversation;
@@ -333,6 +334,10 @@
mAdapter.clear();
+ // We don't need to kick off attachment loaders during this first measurement phase,
+ // so disable them temporarily.
+ MessageFooterView.enableAttachmentLoaders(false);
+
// N.B. the units of height for spacers are actually dp and not px because WebView assumes
// a pixel is an mdpi pixel, unless you set device-dpi.
@@ -363,6 +368,9 @@
footerDp);
}
+ // Re-enable attachment loaders
+ MessageFooterView.enableAttachmentLoaders(true);
+
mWebView.getSettings().setBlockNetworkImage(!allowNetworkImages);
return mTemplates.endConversation(mBaseUri, 320);