Implement snappy headers for email

Change-Id: I3bc066f573f217ac1767d46fdf9f68a6b946eb36
diff --git a/res/layout/secure_conversation_view.xml b/res/layout/secure_conversation_view.xml
index f115a99..10be10a 100644
--- a/res/layout/secure_conversation_view.xml
+++ b/res/layout/secure_conversation_view.xml
@@ -104,6 +104,14 @@
 
     </com.android.mail.browse.MessageScrollView>
 
+    <include layout="@layout/conversation_message_header"
+         android:id="@+id/snap_header"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         style="@style/ConversationViewSideMarginStyle"
+         android:layout_gravity="top"
+         android:visibility="gone"/>
+
     <include layout="@layout/conversation_load_spinner"/>
 
 </FrameLayout>
diff --git a/src/com/android/mail/browse/MessageScrollView.java b/src/com/android/mail/browse/MessageScrollView.java
index 250fbd0..11722f6 100644
--- a/src/com/android/mail/browse/MessageScrollView.java
+++ b/src/com/android/mail/browse/MessageScrollView.java
@@ -22,8 +22,12 @@
 import android.view.MotionEvent;
 import android.widget.ScrollView;
 
+import com.android.mail.browse.ScrollNotifier.ScrollListener;
 import com.android.mail.utils.LogUtils;
 
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
 /**
  * A container that tries to play nice with an internally scrollable {@link Touchable} child view.
  * The assumption is that the child view can scroll horizontally, but not vertically, so any
@@ -32,7 +36,7 @@
  * <p>
  * Touch events on any other child of this ScrollView are intercepted in the standard fashion.
  */
-public class MessageScrollView extends ScrollView {
+public class MessageScrollView extends ScrollView implements ScrollNotifier {
 
     /**
      * A View that reports whether onTouchEvent() was recently called.
@@ -58,6 +62,9 @@
      */
     private Touchable mTouchableChild;
 
+    private final Set<ScrollListener> mScrollListeners =
+            new CopyOnWriteArraySet<ScrollListener>();
+
     public static final String LOG_TAG = "MsgScroller";
 
     public MessageScrollView(Context c) {
@@ -119,4 +126,52 @@
         return handled;
     }
 
+    @Override
+    public void addScrollListener(ScrollListener l) {
+        mScrollListeners.add(l);
+    }
+
+    @Override
+    public void removeScrollListener(ScrollListener l) {
+        mScrollListeners.remove(l);
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        for (ScrollListener listener : mScrollListeners) {
+            listener.onNotifierScroll(t);
+        }
+    }
+
+
+    @Override
+    public int computeVerticalScrollRange() {
+        return super.computeVerticalScrollRange();
+    }
+
+    @Override
+    public int computeVerticalScrollOffset() {
+        return super.computeVerticalScrollOffset();
+    }
+
+    @Override
+    public int computeVerticalScrollExtent() {
+        return super.computeVerticalScrollExtent();
+    }
+
+    @Override
+    public int computeHorizontalScrollRange() {
+        return super.computeHorizontalScrollRange();
+    }
+
+    @Override
+    public int computeHorizontalScrollOffset() {
+        return super.computeHorizontalScrollOffset();
+    }
+
+    @Override
+    public int computeHorizontalScrollExtent() {
+        return super.computeHorizontalScrollExtent();
+    }
 }
diff --git a/src/com/android/mail/ui/SecureConversationViewController.java b/src/com/android/mail/ui/SecureConversationViewController.java
index 0ed03f6..8d30f67 100644
--- a/src/com/android/mail/ui/SecureConversationViewController.java
+++ b/src/com/android/mail/ui/SecureConversationViewController.java
@@ -20,12 +20,14 @@
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.webkit.WebSettings;
 
+import com.android.emailcommon.Logging;
 import com.android.mail.FormattedDateBuilder;
 import com.android.mail.R;
 import com.android.mail.browse.BorderView;
@@ -39,21 +41,24 @@
 import com.android.mail.browse.MessageHeaderView;
 import com.android.mail.browse.MessageScrollView;
 import com.android.mail.browse.MessageWebView;
+import com.android.mail.browse.ScrollNotifier.ScrollListener;
 import com.android.mail.browse.WebViewContextMenu;
 import com.android.mail.print.PrintUtils;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Message;
 import com.android.mail.utils.ConversationViewUtils;
+import com.android.mail.utils.LogUtils;
 
 /**
- * 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.
+ * 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 {
+        MessageHeaderView.MessageHeaderViewCallbacks, ScrollListener {
     private static final String BEGIN_HTML =
-            "<body style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">";
+                                           "<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;
@@ -61,6 +66,7 @@
     private MessageWebView mWebView;
     private ConversationViewHeader mConversationHeaderView;
     private MessageHeaderView mMessageHeaderView;
+    private MessageHeaderView mSnapHeaderView;
     private MessageFooterView mMessageFooterView;
     private ConversationMessage mMessage;
     private MessageScrollView mScrollView;
@@ -80,8 +86,11 @@
         mScrollView = (MessageScrollView) rootView.findViewById(R.id.scroll_view);
         mConversationHeaderView = (ConversationViewHeader) rootView.findViewById(R.id.conv_header);
         mMessageHeaderView = (MessageHeaderView) rootView.findViewById(R.id.message_header);
+        mSnapHeaderView = (MessageHeaderView) rootView.findViewById(R.id.snap_header);
         mMessageFooterView = (MessageFooterView) rootView.findViewById(R.id.message_footer);
 
+        mScrollView.addScrollListener(this);
+
         // Add color backgrounds to the header and footer.
         // Otherwise the backgrounds are grey. They can't
         // be set in xml because that would add more overdraw
@@ -89,6 +98,7 @@
         final int color = rootView.getResources().getColor(
                 R.color.message_header_background_color);
         mMessageHeaderView.setBackgroundColor(color);
+        mSnapHeaderView.setBackgroundColor(color);
         mMessageFooterView.setBackgroundColor(color);
 
         ((BorderView) rootView.findViewById(R.id.top_border)).disableCardBottomBorder();
@@ -102,7 +112,7 @@
         mWebView.setWebViewClient(mCallbacks.getWebViewClient());
         final InlineAttachmentViewIntentBuilderCreator creator =
                 InlineAttachmentViewIntentBuilderCreatorHolder.
-                getInlineAttachmentViewIntentCreator();
+                        getInlineAttachmentViewIntentCreator();
         mWebView.setOnCreateContextMenuListener(new WebViewContextMenu(
                 mCallbacks.getFragment().getActivity(),
                 creator.createInlineAttachmentViewIntentBuilder(null, null, -1)));
@@ -136,7 +146,16 @@
         mMessageHeaderView.setExpandable(false);
         mMessageHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
 
+        mSnapHeaderView.setSnappy();
+        mSnapHeaderView.initialize(
+                mCallbacks.getConversationAccountController(), mCallbacks.getAddressCache());
+        mSnapHeaderView.setContactInfoSource(mCallbacks.getContactInfoSource());
+        mSnapHeaderView.setCallbacks(this);
+        mSnapHeaderView.setExpandable(false);
+        mSnapHeaderView.setViewOnlyMode(mCallbacks.isViewOnlyMode());
+
         mCallbacks.setupMessageHeaderVeiledMatcher(mMessageHeaderView);
+        mCallbacks.setupMessageHeaderVeiledMatcher(mSnapHeaderView);
 
         mMessageFooterView.initialize(fragment.getLoaderManager(), fragment.getFragmentManager());
 
@@ -149,6 +168,23 @@
                 R.dimen.conversation_message_content_margin_side) / r.getDisplayMetrics().density);
     }
 
+    @Override
+    public void onNotifierScroll(final int y) {
+        // We need to decide whether or not to display the snap header.
+        // Get the location of the moveable message header inside the scroll view.
+        Rect rect = new Rect();
+        mScrollView.offsetDescendantRectToMyCoords(mMessageHeaderView, rect);
+
+        // If we have scrolled further than the distance from the top of the scrollView to the top
+        // of the message header, then the message header is at least partially ofscreen. As soon
+        // as the message header goes partially offscreen we need to display the snap header.
+        if (y > rect.top) {
+            mSnapHeaderView.setVisibility(View.VISIBLE);
+        } else {
+            mSnapHeaderView.setVisibility(View.GONE);
+        }
+    }
+
     /**
      * Populate the adapter with overlay views (message headers, super-collapsed
      * blocks, a conversation header), and return an HTML document with spacer
@@ -175,6 +211,10 @@
         // Clear out the old info from the header before (re)binding
         mMessageHeaderView.unbind();
         mMessageHeaderView.bind(item, false);
+
+        mSnapHeaderView.unbind();
+        mSnapHeaderView.bind(item, false);
+
         if (mMessage.hasAttachments) {
             mMessageFooterView.setVisibility(View.VISIBLE);
             mMessageFooterView.bind(item, mCallbacks.getAccountUri(), false);