Merge "Fix broken touch-interception in ContactDetailFragmentCarousel."
diff --git a/res/layout-sw580dp/contact_detail_updates_fragment.xml b/res/layout-sw580dp/contact_detail_updates_fragment.xml
index bf63d27..d231dbc 100644
--- a/res/layout-sw580dp/contact_detail_updates_fragment.xml
+++ b/res/layout-sw580dp/contact_detail_updates_fragment.xml
@@ -26,19 +26,4 @@
         android:fadingEdge="none"
         android:divider="@null"/>
 
-    <View
-        android:id="@+id/alpha_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/black"
-        android:alpha=".50"
-        android:visibility="gone"/>
-
-    <View
-        android:id="@+id/touch_intercept_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/transparent"
-        android:visibility="gone"/>
-
 </FrameLayout>
diff --git a/res/layout-w470dp/contact_detail_fragment.xml b/res/layout-w470dp/contact_detail_fragment.xml
index d7d1756..b5c4c94 100644
--- a/res/layout-w470dp/contact_detail_fragment.xml
+++ b/res/layout-w470dp/contact_detail_fragment.xml
@@ -71,20 +71,5 @@
             android:lineSpacingMultiplier="0.92"/>
     </ScrollView>
 
-    <View
-        android:id="@+id/alpha_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"/>
-
-    <View
-        android:id="@+id/touch_intercept_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:background="?android:attr/selectableItemBackground"
-        android:visibility="gone"/>
 </RelativeLayout>
 
diff --git a/res/layout-w470dp/contact_detail_updates_fragment.xml b/res/layout-w470dp/contact_detail_updates_fragment.xml
index 4eeaebb..338a986 100644
--- a/res/layout-w470dp/contact_detail_updates_fragment.xml
+++ b/res/layout-w470dp/contact_detail_updates_fragment.xml
@@ -25,20 +25,4 @@
         android:fadingEdge="none"
         android:divider="@null"/>
 
-    <View
-        android:id="@+id/alpha_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"/>
-
-    <View
-        android:id="@+id/touch_intercept_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:background="?android:attr/selectableItemBackground"
-        android:visibility="gone"/>
-
 </FrameLayout>
diff --git a/res/layout/carousel_about_tab.xml b/res/layout/carousel_about_tab.xml
index f2e504b..e033a6f 100644
--- a/res/layout/carousel_about_tab.xml
+++ b/res/layout/carousel_about_tab.xml
@@ -21,41 +21,47 @@
     android:layout_height="match_parent"
     android:layout_weight="1">
 
-    <ImageView android:id="@+id/photo"
-        android:scaleType="centerCrop"
+    <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true"/>
+        android:layout_height="match_parent" >
 
-    <!-- Transparent view to overlay on the contact's photo
-    (to allow white text to appear over a white photo). -->
-    <View android:id="@+id/label_background"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentBottom="true"
-        android:background="#7F000000" />
+        <ImageView android:id="@+id/photo"
+            android:scaleType="centerCrop"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"/>
 
-    <View
-        android:id="@+id/alpha_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"/>
+        <!-- Transparent view to overlay on the contact's photo
+        (to allow white text to appear over a white photo). -->
+        <View android:id="@+id/label_background"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:background="#7F000000" />
 
-    <TextView
-        android:id="@+id/label"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentBottom="true"
-        android:paddingLeft="@dimen/detail_tab_carousel_tab_label_indent"
-        android:singleLine="true"
-        android:gravity="left|center_vertical"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="@color/detail_tab_carousel_tab_label_color"
-        style="@android:style/Widget.Holo.ActionBar.TabView" />
+        <View
+            android:id="@+id/alpha_overlay"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"/>
+
+        <TextView
+            android:id="@+id/label"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:paddingLeft="@dimen/detail_tab_carousel_tab_label_indent"
+            android:singleLine="true"
+            android:gravity="left|center_vertical"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/detail_tab_carousel_tab_label_color"
+            style="@android:style/Widget.Holo.ActionBar.TabView" />
+
+    </RelativeLayout>
 
 </view>
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index 93e6e8f..67ea582 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -26,59 +26,65 @@
     android:layout_weight="1"
     android:background="@drawable/bg_people_updates_holo">
 
-    <ImageView android:id="@+id/status_photo"
-        android:scaleType="centerCrop"
+    <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true"
-        android:visibility="gone" />
+        android:layout_height="match_parent" >
 
-    <!-- Transparent view to overlay on the update photo
-    (to allow white text to appear over a white photo). -->
-    <View android:id="@+id/label_background"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentBottom="true"
-        android:layout_above="@id/status_photo"
-        android:background="#7F000000" />
+        <ImageView android:id="@+id/status_photo"
+            android:scaleType="centerCrop"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:visibility="gone" />
 
-    <TextView android:id="@+id/status"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true"
-        android:layout_above="@id/label"
-        android:gravity="center_vertical"
-        android:paddingLeft="@dimen/detail_update_tab_side_padding"
-        android:paddingRight="@dimen/detail_update_tab_side_padding"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textColor="@color/detail_update_tab_text_color"
-        android:textStyle="bold"
-        android:maxLines="@integer/updates_tab_snippet_max_lines"
-        android:ellipsize="end" />
+        <!-- Transparent view to overlay on the update photo
+        (to allow white text to appear over a white photo). -->
+        <View android:id="@+id/label_background"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:layout_above="@id/status_photo"
+            android:background="#7F000000" />
 
-    <View
-        android:id="@+id/alpha_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"/>
+        <TextView android:id="@+id/status"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:layout_above="@id/label"
+            android:gravity="center_vertical"
+            android:paddingLeft="@dimen/detail_update_tab_side_padding"
+            android:paddingRight="@dimen/detail_update_tab_side_padding"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textColor="@color/detail_update_tab_text_color"
+            android:textStyle="bold"
+            android:maxLines="@integer/updates_tab_snippet_max_lines"
+            android:ellipsize="end" />
 
-    <TextView
-        android:id="@+id/label"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentBottom="true"
-        android:layout_above="@id/status_photo"
-        android:paddingLeft="@dimen/detail_tab_carousel_tab_label_indent"
-        android:singleLine="true"
-        android:gravity="left|center_vertical"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="@color/detail_tab_carousel_tab_label_color"
-        style="@android:style/Widget.Holo.ActionBar.TabView" />
+        <View
+            android:id="@+id/alpha_overlay"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"/>
+
+        <TextView
+            android:id="@+id/label"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:layout_above="@id/status_photo"
+            android:paddingLeft="@dimen/detail_tab_carousel_tab_label_indent"
+            android:singleLine="true"
+            android:gravity="left|center_vertical"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/detail_tab_carousel_tab_label_color"
+            style="@android:style/Widget.Holo.ActionBar.TabView" />
+
+    </RelativeLayout>
 
 </view>
diff --git a/res/layout/contact_detail_fragment_carousel.xml b/res/layout/contact_detail_fragment_carousel.xml
index d01a18e..0695511 100644
--- a/res/layout/contact_detail_fragment_carousel.xml
+++ b/res/layout/contact_detail_fragment_carousel.xml
@@ -27,7 +27,8 @@
       with social updates. This view ID must match with a view ID in the layout
       that is used after an orientation change.
     -->
-    <FrameLayout
+    <view
+        class="com.android.contacts.widget.FrameLayoutWithOverlay"
         android:id="@+id/about_fragment_container"
         android:layout_width="0dip"
         android:layout_height="match_parent"
@@ -40,13 +41,14 @@
       with social updates. This view ID must match with a view ID in the layout
       that is used after an orientation change.
     -->
-    <FrameLayout
+    <view
+        class="com.android.contacts.widget.FrameLayoutWithOverlay"
         android:id="@+id/updates_fragment_container"
         android:layout_width="0dip"
         android:layout_height="match_parent"
         android:layout_weight="1"
-        android:visibility="gone"
         android:focusable="true"
-        android:focusableInTouchMode="true" />
+        android:focusableInTouchMode="true"
+        android:visibility="gone" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/src/com/android/contacts/detail/CarouselTab.java b/src/com/android/contacts/detail/CarouselTab.java
index 9bdf98b..0b430b6 100644
--- a/src/com/android/contacts/detail/CarouselTab.java
+++ b/src/com/android/contacts/detail/CarouselTab.java
@@ -17,57 +17,33 @@
 package com.android.contacts.detail;
 
 import com.android.contacts.R;
-import com.android.contacts.util.ThemeUtils;
+import com.android.contacts.widget.FrameLayoutWithOverlay;
 
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
-import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 /**
  * This is a tab in the {@link ContactDetailTabCarousel}.
  */
-public class CarouselTab extends RelativeLayout implements ViewOverlay {
+public class CarouselTab extends FrameLayoutWithOverlay {
 
     private static final String TAG = CarouselTab.class.getSimpleName();
 
-    private static final boolean DEBUG = false;
-
     private static final long FADE_TRANSITION_TIME = 150;
 
     private TextView mLabelView;
     private View mLabelBackgroundView;
 
     /**
-     * This view adds an alpha layer over the entire tab.
+     * This view adds an alpha layer over the entire tab (except for the label).
      */
     private View mAlphaLayer;
 
-    /**
-     * This view adds a layer over the entire tab so that when visible, it intercepts all touch
-     * events on the tab.
-     */
-    private View mTouchInterceptLayer;
-
     public CarouselTab(Context context, AttributeSet attrs) {
         super(context, attrs);
-
-        // Programmatically create and initialize touch-interceptor View.
-        mTouchInterceptLayer = new View(context);
-
-        LayoutParams layoutParams =
-                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-        layoutParams.addRule(ALIGN_PARENT_LEFT, TRUE);
-        layoutParams.addRule(ALIGN_PARENT_TOP, TRUE);
-        int background = ThemeUtils.getSelectableItemBackground(context.getTheme());
-
-        mTouchInterceptLayer.setVisibility(GONE);
-        mTouchInterceptLayer.setBackgroundResource(background);
-        mTouchInterceptLayer.setLayoutParams(layoutParams);
-
-        addView(mTouchInterceptLayer);
     }
 
     @Override
@@ -77,9 +53,7 @@
         mLabelView = (TextView) findViewById(R.id.label);
         mLabelBackgroundView = findViewById(R.id.label_background);
         mAlphaLayer = findViewById(R.id.alpha_overlay);
-
-        mTouchInterceptLayer.bringToFront();
-        if (DEBUG) mTouchInterceptLayer.setBackgroundColor(0x4400FF00);
+        setAlphaLayer(mAlphaLayer);
     }
 
     public void setLabel(String label) {
@@ -94,26 +68,6 @@
         mLabelView.setSelected(false);
     }
 
-    @Override
-    public void setTouchInterceptorListener(OnClickListener listener) {
-        mTouchInterceptLayer.setOnClickListener(listener);
-    }
-
-    @Override
-    public void disableTouchInterceptor() {
-        mTouchInterceptLayer.setVisibility(View.GONE);
-    }
-
-    @Override
-    public void enableTouchInterceptor() {
-        mTouchInterceptLayer.setVisibility(View.VISIBLE);
-    }
-
-    @Override
-    public void setAlphaLayerValue(float alpha) {
-        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
-    }
-
     public void fadeInLabelViewAnimator(int startDelay, boolean fadeBackground) {
         final ViewPropertyAnimator labelAnimator = mLabelView.animate();
         mLabelView.setAlpha(0.0f);
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 34250d5..603d2fa 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -118,7 +118,7 @@
 import java.util.List;
 import java.util.Map;
 
-public class ContactDetailFragment extends Fragment implements FragmentKeyListener, ViewOverlay,
+public class ContactDetailFragment extends Fragment implements FragmentKeyListener,
         SelectAccountDialogFragment.Listener, OnItemClickListener {
 
     private static final String TAG = "ContactDetailFragment";
@@ -182,22 +182,6 @@
     private View mEmptyView;
 
     /**
-     * Initial alpha value to set on the alpha layer.
-     */
-    private float mInitialAlphaValue;
-
-    /**
-     * This optional view adds an alpha layer over the entire fragment.
-     */
-    private View mAlphaLayer;
-
-    /**
-     * This optional view adds a layer over the entire fragment so that when visible, it intercepts
-     * all touch events on the fragment.
-     */
-    private View mTouchInterceptLayer;
-
-    /**
      * Saved state of the {@link ListView}. This must be saved and applied to the {@ListView} only
      * when the adapter has been populated again.
      */
@@ -285,10 +269,6 @@
         // Don't set it to mListView yet.  We do so later when we bind the adapter.
         mEmptyView = mView.findViewById(android.R.id.empty);
 
-        mTouchInterceptLayer = mView.findViewById(R.id.touch_intercept_overlay);
-        mAlphaLayer = mView.findViewById(R.id.alpha_overlay);
-        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, mInitialAlphaValue);
-
         mQuickFixButton = (Button) mView.findViewById(R.id.contact_quick_fix);
         mQuickFixButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -316,39 +296,6 @@
         mListener = value;
     }
 
-    @Override
-    public void setAlphaLayerValue(float alpha) {
-        // If the alpha layer is not ready yet, store it for later when the view
-        // is initialized
-        if (mAlphaLayer == null) {
-            mInitialAlphaValue = alpha;
-        } else {
-            // Otherwise set the value immediately
-            ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
-        }
-    }
-
-    @Override
-    public void setTouchInterceptorListener(OnClickListener listener) {
-        if (mTouchInterceptLayer != null) {
-            mTouchInterceptLayer.setOnClickListener(listener);
-        }
-    }
-
-    @Override
-    public void enableTouchInterceptor() {
-        if (mTouchInterceptLayer != null) {
-            mTouchInterceptLayer.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void disableTouchInterceptor() {
-        if (mTouchInterceptLayer != null) {
-            mTouchInterceptLayer.setVisibility(View.GONE);
-        }
-    }
-
     protected Context getContext() {
         return mContext;
     }
@@ -1370,7 +1317,7 @@
     /**
      * Cache of the children views for a view that displays a header view entry.
      */
-    private static class HeaderViewCache implements ViewOverlay {
+    private static class HeaderViewCache {
         public final TextView displayNameView;
         public final TextView companyView;
         public final ImageView photoView;
@@ -1387,31 +1334,12 @@
             layoutResourceId = layoutResourceInflated;
         }
 
-        @Override
-        public void setTouchInterceptorListener(OnClickListener listener) {
+        public void enablePhotoOverlay(OnClickListener listener) {
             if (photoOverlayView != null) {
                 photoOverlayView.setOnClickListener(listener);
-            }
-        }
-
-        @Override
-        public void setAlphaLayerValue(float alpha) {
-            // Nothing to do.
-        }
-
-        @Override
-        public void enableTouchInterceptor() {
-            if (photoOverlayView != null) {
                 photoOverlayView.setVisibility(View.VISIBLE);
             }
         }
-
-        @Override
-        public void disableTouchInterceptor() {
-            if (photoOverlayView != null) {
-                photoOverlayView.setVisibility(View.GONE);
-            }
-        }
     }
 
     /**
@@ -1528,8 +1456,7 @@
                         mContext, mContactData, viewCache.photoView, expandOnClick);
 
                 if (expandOnClick || mContactData.isWritableContact(mContext)) {
-                    viewCache.setTouchInterceptorListener(listener);
-                    viewCache.enableTouchInterceptor();
+                    viewCache.enablePhotoOverlay(listener);
                 }
             }
 
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index a83409f..7153b8d 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -17,6 +17,7 @@
 package com.android.contacts.detail;
 
 import com.android.contacts.R;
+import com.android.contacts.widget.FrameLayoutWithOverlay;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -82,11 +83,8 @@
     private int mCurrentPage = ABOUT_PAGE;
     private int mLastScrollPosition;
 
-    private ViewOverlay mAboutFragment;
-    private ViewOverlay mUpdatesFragment;
-
-    private View mAboutFragmentView;
-    private View mUpdatesFragmentView;
+    private FrameLayoutWithOverlay mAboutFragment;
+    private FrameLayoutWithOverlay mUpdatesFragment;
 
     public ContactDetailFragmentCarousel(Context context) {
         this(context, null);
@@ -153,25 +151,19 @@
             mAboutFragment.setAlphaLayerValue(mCurrentPage == ABOUT_PAGE ? 0 : MAX_ALPHA);
             mUpdatesFragment.setAlphaLayerValue(mCurrentPage == UPDATES_PAGE ? 0 : MAX_ALPHA);
         }
+
+        updateTouchInterceptors();
     }
 
     /**
      * Set the view containers for the detail and updates fragment.
      */
-    public void setFragmentViews(View aboutFragmentView, View updatesFragmentView) {
-        mAboutFragmentView = aboutFragmentView;
-        mUpdatesFragmentView = updatesFragmentView;
-    }
+    public void setFragmentViews(FrameLayoutWithOverlay about, FrameLayoutWithOverlay updates) {
+        mAboutFragment = about;
+        mUpdatesFragment = updates;
 
-    /**
-     * Set the detail and updates fragment.
-     */
-    public void setFragments(ViewOverlay aboutFragment, ViewOverlay updatesFragment) {
-        mAboutFragment = aboutFragment;
-        mUpdatesFragment = updatesFragment;
-
-        mAboutFragment.setTouchInterceptorListener(mAboutFragTouchInterceptListener);
-        mUpdatesFragment.setTouchInterceptorListener(mUpdatesFragTouchInterceptListener);
+        mAboutFragment.setOverlayOnClickListener(mAboutFragTouchInterceptListener);
+        mUpdatesFragment.setOverlayOnClickListener(mUpdatesFragTouchInterceptListener);
     }
 
     /**
@@ -181,12 +173,12 @@
     public void enableSwipe(boolean enable) {
         if (mEnableSwipe != enable) {
             mEnableSwipe = enable;
-            if (mUpdatesFragmentView != null) {
-                mUpdatesFragmentView.setVisibility(enable ? View.VISIBLE : View.GONE);
+            if (mUpdatesFragment != null) {
+                mUpdatesFragment.setVisibility(enable ? View.VISIBLE : View.GONE);
                 if (mCurrentPage == ABOUT_PAGE) {
-                    mAboutFragmentView.requestFocus();
+                    mAboutFragment.requestFocus();
                 } else {
-                    mUpdatesFragmentView.requestFocus();
+                    mUpdatesFragment.requestFocus();
                 }
                 updateTouchInterceptors();
             }
@@ -214,17 +206,12 @@
     };
 
     private void updateTouchInterceptors() {
-        switch (mCurrentPage) {
-            case ABOUT_PAGE:
-                // The "about this contact" page has been selected, so disable the touch interceptor
-                // on this page and enable it for the "updates" page.
-                mAboutFragment.disableTouchInterceptor();
-                mUpdatesFragment.enableTouchInterceptor();
-                break;
-            case UPDATES_PAGE:
-                mUpdatesFragment.disableTouchInterceptor();
-                mAboutFragment.enableTouchInterceptor();
-                break;
+        // Disable the touch-interceptor on the selected page, and enable it on the other.
+        if (mAboutFragment != null) {
+            mAboutFragment.setOverlayClickable(mCurrentPage != ABOUT_PAGE);
+        }
+        if (mUpdatesFragment != null) {
+            mUpdatesFragment.setOverlayClickable(mCurrentPage != UPDATES_PAGE);
         }
     }
 
@@ -245,14 +232,8 @@
     }
 
     private void snapToEdge() {
-        switch (mCurrentPage) {
-            case ABOUT_PAGE:
-                smoothScrollTo(0, 0);
-                break;
-            case UPDATES_PAGE:
-                smoothScrollTo(mAllowedHorizontalScrollLength, 0);
-                break;
-        }
+        final int x = mCurrentPage == ABOUT_PAGE ? 0 : mAllowedHorizontalScrollLength;
+        smoothScrollTo(x,0);
         updateTouchInterceptors();
     }
 
@@ -292,8 +273,8 @@
      */
     public void animateAppear() {
         final int x = Math.round((1.0f - FRAGMENT_WIDTH_SCREEN_WIDTH_FRACTION) * getWidth());
-        mUpdatesFragmentView.setTranslationX(x);
-        final ViewPropertyAnimator animator = mUpdatesFragmentView.animate();
+        mUpdatesFragment.setTranslationX(x);
+        final ViewPropertyAnimator animator = mUpdatesFragment.animate();
         animator.translationX(0.0f);
     }
 }
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index e5ce961..12facef 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -23,6 +23,7 @@
 import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
 import com.android.contacts.util.PhoneCapabilityTester;
 import com.android.contacts.util.UriUtils;
+import com.android.contacts.widget.FrameLayoutWithOverlay;
 import com.android.contacts.widget.TransitionAnimationView;
 
 import android.animation.Animator;
@@ -43,6 +44,8 @@
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
 
+import android.util.Log;
+
 /**
  * Determines the layout of the contact card.
  */
@@ -232,9 +235,11 @@
                     mFragmentManager.executePendingTransactions();
                 }
 
-                mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView);
-                mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment);
+                mFragmentCarousel.setFragmentViews(
+                        (FrameLayoutWithOverlay) mDetailFragmentView,
+                        (FrameLayoutWithOverlay) mUpdatesFragmentView);
                 mFragmentCarousel.setCurrentPage(currentPageIndex);
+
                 break;
             }
         }
@@ -618,7 +623,7 @@
         }
     };
 
-    private final ContactDetailTabCarousel.Listener mTabCarouselListener 
+    private final ContactDetailTabCarousel.Listener mTabCarouselListener
             = new ContactDetailTabCarousel.Listener() {
 
         @Override
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index 657a774..b0b3dc4 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -123,13 +123,13 @@
         mTabAndShadowContainer = findViewById(R.id.tab_and_shadow_container);
         mAboutTab = (CarouselTab) findViewById(R.id.tab_about);
         mAboutTab.setLabel(mContext.getString(R.string.contactDetailAbout));
-        mAboutTab.setTouchInterceptorListener(mAboutTabTouchInterceptListener);
+        mAboutTab.setOverlayOnClickListener(mAboutTabTouchInterceptListener);
 
         mTabDivider = findViewById(R.id.tab_divider);
 
         mUpdatesTab = (CarouselTab) findViewById(R.id.tab_update);
         mUpdatesTab.setLabel(mContext.getString(R.string.contactDetailUpdates));
-        mUpdatesTab.setTouchInterceptorListener(mUpdatesTabTouchInterceptListener);
+        mUpdatesTab.setOverlayOnClickListener(mUpdatesTabTouchInterceptListener);
 
         mShadow = findViewById(R.id.shadow);
 
@@ -446,9 +446,9 @@
                 throw new IllegalStateException("Invalid tab position " + position);
         }
         selected.showSelectedState();
-        selected.disableTouchInterceptor();
+        selected.setOverlayClickable(false);
         deselected.showDeselectedState();
-        deselected.enableTouchInterceptor();
+        deselected.setOverlayClickable(true);
         mCurrentTab = position;
     }
 
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index 1f3ce55..6c35682 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -37,8 +37,7 @@
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.ListView;
 
-public class ContactDetailUpdatesFragment extends ListFragment
-        implements FragmentKeyListener, ViewOverlay {
+public class ContactDetailUpdatesFragment extends ListFragment implements FragmentKeyListener {
 
     private static final String TAG = "ContactDetailUpdatesFragment";
 
@@ -48,19 +47,6 @@
     private LayoutInflater mInflater;
     private StreamItemAdapter mStreamItemAdapter;
 
-    private float mInitialAlphaValue;
-
-    /**
-     * This optional view adds an alpha layer over the entire fragment.
-     */
-    private View mAlphaLayer;
-
-    /**
-     * This optional view adds a layer over the entire fragment so that when visible, it intercepts
-     * all touch events on the fragment.
-     */
-    private View mTouchInterceptLayer;
-
     private OnScrollListener mVerticalScrollListener;
 
     /**
@@ -116,14 +102,7 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
         mInflater = inflater;
-        View rootView = mInflater.inflate(R.layout.contact_detail_updates_fragment, container,
-                false);
-
-        mTouchInterceptLayer = rootView.findViewById(R.id.touch_intercept_overlay);
-        mAlphaLayer = rootView.findViewById(R.id.alpha_overlay);
-        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, mInitialAlphaValue);
-
-        return rootView;
+        return mInflater.inflate(R.layout.contact_detail_updates_fragment, container, false);
     }
 
     @Override
@@ -167,39 +146,6 @@
     }
 
     @Override
-    public void setAlphaLayerValue(float alpha) {
-        // If the alpha layer is not ready yet, store it for later when the view is initialized
-        if (mAlphaLayer == null) {
-            mInitialAlphaValue = alpha;
-        } else {
-            // Otherwise set the value immediately
-            ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
-        }
-    }
-
-
-    @Override
-    public void setTouchInterceptorListener(OnClickListener clickListener) {
-        if (mTouchInterceptLayer != null) {
-            mTouchInterceptLayer.setOnClickListener(clickListener);
-        }
-    }
-
-    @Override
-    public void enableTouchInterceptor() {
-        if (mTouchInterceptLayer != null) {
-            mTouchInterceptLayer.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void disableTouchInterceptor() {
-        if (mTouchInterceptLayer != null) {
-            mTouchInterceptLayer.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
     public boolean handleKeyDown(int keyCode) {
         return false;
     }
diff --git a/src/com/android/contacts/detail/ViewOverlay.java b/src/com/android/contacts/detail/ViewOverlay.java
deleted file mode 100644
index 6b5b02f..0000000
--- a/src/com/android/contacts/detail/ViewOverlay.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2011 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.contacts.detail;
-
-import android.view.View.OnClickListener;
-
-/**
- * This is implemented by {@link View}s that contain an alpha layer and touch interceptor layer.
- * The alpha layer covers the entire fragment and has an alpha value which makes the fragment
- * contents appear "dimmed" out. The touch interceptor layer covers the entire fragment so that
- * when visible, it intercepts all touch events on the {@link View}.
- */
-public interface ViewOverlay {
-
-    /**
-     * Sets the callback that will be invoked when the touch-interceptor tapped
-     * while enabled.
-     */
-    public void setTouchInterceptorListener(OnClickListener listener);
-
-    /**
-     * Sets the alpha value on the alpha layer (if there is one).
-     */
-    public void setAlphaLayerValue(float alpha);
-
-    /**
-     * Enables the touch intercept layer on this fragment, so that it intercepts
-     * and handles touch events.
-     */
-    public void enableTouchInterceptor();
-
-    /**
-     * Disables the touch intercept layer on this fragment; touch events are
-     * handled normally by the view hierarchy under the overlay.
-     */
-    public void disableTouchInterceptor();
-}
diff --git a/src/com/android/contacts/widget/AlphaTouchInterceptorOverlay.java b/src/com/android/contacts/widget/AlphaTouchInterceptorOverlay.java
new file mode 100644
index 0000000..5418435
--- /dev/null
+++ b/src/com/android/contacts/widget/AlphaTouchInterceptorOverlay.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 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.contacts.widget;
+
+import com.android.contacts.detail.ContactDetailDisplayUtils;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * A View that other Views can use to create a touch-interceptor layer above
+ * their other sub-views. This layer can be enabled and disabled; when enabled,
+ * clicks are intercepted and passed to a listener.
+ *
+ * Also supports an alpha layer to dim the content underneath.  By default, the
+ * alpha layer is the same View as the touch-interceptor layer.  However, for
+ * some use-cases, you want a few Views to not be dimmed, but still have touches
+ * intercepted (for example, {@link CarouselTab}'s label appears above the alpha
+ * layer).  In this case, you can specify the View to use as the alpha layer via
+ * setAlphaLayer(); in this case you are responsible for managing the z-order of
+ * the alpha-layer with respect to your other sub-views.
+ *
+ * Typically, you would not use this class directly, but rather use another class
+ * that uses it, for example {@link FrameLayoutWithOverlay}.
+ */
+public class AlphaTouchInterceptorOverlay extends View {
+
+    private View mAlphaLayer = this;
+    private float mAlpha = 1.0f;
+
+    public AlphaTouchInterceptorOverlay(Context context) {
+        super(context);
+        setAlphaLayer(this);
+        setVisibility(VISIBLE);
+        ContactDetailDisplayUtils.setAlphaOnViewBackground(this, 1.0f);
+    }
+
+    /**
+     * Set the View that the overlay will use as its alpha-layer.  If
+     * none is set it will use itself.  Only necessary to set this if
+     * some child views need to appear above the alpha-layer but below
+     * the touch-interceptor.
+     */
+    public void setAlphaLayer(View alphaLayer) {
+        if (mAlphaLayer == alphaLayer) return;
+
+        // We're no longer the alpha-layer, so make ourself invisible.
+        if (mAlphaLayer == this) ContactDetailDisplayUtils.setAlphaOnViewBackground(this, 0.0f);
+        mAlphaLayer = (alphaLayer == null) ? this : alphaLayer;
+        setAlphaLayerValue(mAlpha);
+    }
+
+    /** Sets the alpha value on the alpha layer. */
+    public void setAlphaLayerValue(float alpha) {
+        mAlpha = alpha;
+        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, mAlpha);
+    }
+}
diff --git a/src/com/android/contacts/widget/FrameLayoutWithOverlay.java b/src/com/android/contacts/widget/FrameLayoutWithOverlay.java
new file mode 100644
index 0000000..b4cd810
--- /dev/null
+++ b/src/com/android/contacts/widget/FrameLayoutWithOverlay.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 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.contacts.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+
+/**
+ * A FrameLayout whose contents are kept beneath an {@link AlphaTouchInterceptorOverlay}.
+ * If necessary, you can specify your own alpha-layer and manually manage its z-order.
+ */
+public class FrameLayoutWithOverlay extends FrameLayout {
+    private final AlphaTouchInterceptorOverlay mOverlay;
+
+    public FrameLayoutWithOverlay(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        // Programmatically create touch-interceptor View.
+        mOverlay = new AlphaTouchInterceptorOverlay(context);
+
+        addView(mOverlay);
+    }
+
+    /** After adding the View, bring the overlay to the front to ensure it's always on top. */
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        super.addView(child, index, params);
+        mOverlay.bringToFront();
+    }
+
+    /**
+     * Delegate to overlay:  set the View that it will use as it's alpha-layer.
+     * If none is set, the overlay will use itself as the alpha layer.  Only
+     * necessary to set this if some child views need to appear above the
+     * alpha-layer.
+     */
+    protected void setAlphaLayer(View layer) {
+        mOverlay.setAlphaLayer(layer);
+    }
+
+    /** Delegate to overlay: set the alpha value on the alpha layer. */
+    public void setAlphaLayerValue(float alpha) {
+        mOverlay.setAlphaLayerValue(alpha);
+    }
+
+    /** Delegate to overlay. */
+    public void setOverlayOnClickListener(OnClickListener listener) {
+        mOverlay.setOnClickListener(listener);
+    }
+
+    /** Delegate to overlay. */
+    public void setOverlayClickable(boolean clickable) {
+        mOverlay.setClickable(clickable);
+    }
+}