Merge "Import translations. DO NOT MERGE"
diff --git a/res/layout/lists_fragment.xml b/res/layout/lists_fragment.xml
index 6ed0f85..d5b2bf8 100644
--- a/res/layout/lists_fragment.xml
+++ b/res/layout/lists_fragment.xml
@@ -14,11 +14,11 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.dialer.widget.OverlappingPaneLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingTop="?android:attr/actionBarSize"
-    android:orientation="vertical"
     android:animateLayoutChanges="true"
     android:id="@+id/lists_frame">
     <ListView
@@ -29,18 +29,23 @@
         android:clipToPadding="false"
         android:fadingEdge="none"
         android:divider="@null" />
-    <com.android.dialer.list.ViewPagerTabs
-        android:id="@+id/lists_pager_header"
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="?android:attr/actionBarSize"
-        android:textAllCaps="true"
-        android:orientation="horizontal"
-        android:layout_gravity="top"
-        style="@style/DialtactsActionBarTabTextStyle" />
-    <android.support.v4.view.ViewPager
-        android:id="@+id/lists_pager"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1">
-    </android.support.v4.view.ViewPager>
-</LinearLayout>
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+        <com.android.dialer.list.ViewPagerTabs
+            android:id="@+id/lists_pager_header"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:textAllCaps="true"
+            android:orientation="horizontal"
+            android:layout_gravity="top"
+            style="@style/DialtactsActionBarTabTextStyle" />
+        <android.support.v4.view.ViewPager
+            android:id="@+id/lists_pager"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1">
+        </android.support.v4.view.ViewPager>
+    </LinearLayout>
+</com.android.dialer.widget.OverlappingPaneLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 53ff534..4dab285 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -52,10 +52,10 @@
     <color name="item_selected">#660099cc</color>
 
     <!-- Background color of new dialer activity -->
-    <color name="background_dialer_light">#ebebeb</color>
+    <color name="background_dialer_light">#ffffff</color>
 
     <!-- Background color of dialer list items (contacts, call log entries) -->
-    <color name="background_dialer_list_items">#ebebeb</color>
+    <color name="background_dialer_list_items">#ffffff</color>
 
     <!-- Background color of action bars. Ensure this stays in sync with packages/Telephony
          actionbar_background_color. -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 766e86c..738a0b6 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -48,12 +48,11 @@
         <item name="list_item_photo_size">@dimen/contact_browser_list_item_photo_size</item>
         <item name="list_item_profile_photo_size">70dip</item>
         <item name="list_item_prefix_highlight_color">@color/people_app_theme_color</item>
+        <item name="list_item_background_color">@color/background_dialer_light</item>
         <item name="list_item_header_text_indent">8dip</item>
         <item name="list_item_header_text_color">@color/people_app_theme_color</item>
         <item name="list_item_header_text_size">14sp</item>
         <item name="list_item_header_height">30dip</item>
-        <item name="list_item_header_underline_height">1dip</item>
-        <item name="list_item_header_underline_color">@color/favorite_contacts_separator_color</item>
         <item name="list_item_data_width_weight">5</item>
         <item name="list_item_label_width_weight">3</item>
         <item name="contact_browser_list_padding_left">8dip</item>
diff --git a/src/com/android/dialer/SpecialCharSequenceMgr.java b/src/com/android/dialer/SpecialCharSequenceMgr.java
index 0b44fac..9d622aa 100644
--- a/src/com/android/dialer/SpecialCharSequenceMgr.java
+++ b/src/com/android/dialer/SpecialCharSequenceMgr.java
@@ -30,6 +30,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -258,10 +259,7 @@
     private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
         if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
             Log.d(TAG, "handleRegulatoryInfoDisplay() sending intent to settings app");
-            ComponentName regInfoDisplayActivity = new ComponentName(
-                    "com.android.settings", "com.android.settings.RegulatoryInfoDisplayActivity");
-            Intent showRegInfoIntent = new Intent("android.settings.SHOW_REGULATORY_INFO");
-            showRegInfoIntent.setComponent(regInfoDisplayActivity);
+            Intent showRegInfoIntent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
             try {
                 context.startActivity(showRegInfoIntent);
             } catch (ActivityNotFoundException e) {
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index a61d8a5..7cabe6f 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -18,7 +18,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 
 import com.android.contacts.common.GeoUtil;
@@ -32,6 +31,8 @@
 import com.android.dialer.calllog.CallLogQuery;
 import com.android.dialer.calllog.CallLogQueryHandler;
 import com.android.dialer.calllog.ContactInfoHelper;
+import com.android.dialer.widget.OverlappingPaneLayout;
+import com.android.dialer.widget.OverlappingPaneLayout.PanelSlideListener;
 import com.android.dialerbind.ObjectFactory;
 
 import java.util.ArrayList;
@@ -226,7 +227,14 @@
                 (ListView) parentView.findViewById(R.id.shortcut_card_list);
         shortcutCardsListView.setAdapter(mMergedAdapter);
 
-        LayoutTransition transition = ((LinearLayout) parentView).getLayoutTransition();
+        final OverlappingPaneLayout paneLayout = (OverlappingPaneLayout) parentView;
+        paneLayout.setSliderFadeColor(android.R.color.transparent);
+        // TODO: Remove the notion of a capturable view. The entire view be slideable, once
+        // the framework better supports nested scrolling.
+        paneLayout.setCapturableView(mViewPagerTabs);
+        paneLayout.openPane();
+
+        LayoutTransition transition = paneLayout.getLayoutTransition();
         // Turns on animations for all types of layout changes so that they occur for
         // height changes.
         transition.enableTransitionType(LayoutTransition.CHANGING);
diff --git a/src/com/android/dialer/widget/OverlappingPaneLayout.java b/src/com/android/dialer/widget/OverlappingPaneLayout.java
new file mode 100644
index 0000000..aa9de63
--- /dev/null
+++ b/src/com/android/dialer/widget/OverlappingPaneLayout.java
@@ -0,0 +1,1256 @@
+/*
+ * 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.dialer.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.ArrayList;
+
+/**
+ * A custom layout that aligns its child views vertically as two panes, and allows for the bottom
+ * pane to be dragged upwards to overlap and hide the top pane. This layout is adapted from
+ * {@link android.support.v4.widget.SlidingPaneLayout}.
+ */
+public class OverlappingPaneLayout extends ViewGroup {
+    private static final String TAG = "SlidingPaneLayout";
+
+    /**
+     * Default size of the overhang for a pane in the open state.
+     * At least this much of a sliding pane will remain visible.
+     * This indicates that there is more content available and provides
+     * a "physical" edge to grab to pull it closed.
+     */
+    private static final int DEFAULT_OVERHANG_SIZE = 32; // dp;
+
+    /**
+     * If no fade color is given by default it will fade to 80% gray.
+     */
+    private static final int DEFAULT_FADE_COLOR = 0xcccccccc;
+
+    /**
+     * The fade color used for the sliding panel. 0 = no fading.
+     */
+    private int mSliderFadeColor = DEFAULT_FADE_COLOR;
+
+    /**
+     * Minimum velocity that will be detected as a fling
+     */
+    private static final int MIN_FLING_VELOCITY = 400; // dips per second
+
+    /**
+     * The fade color used for the panel covered by the slider. 0 = no fading.
+     */
+    private int mCoveredFadeColor;
+
+    /**
+     * The size of the overhang in pixels.
+     * This is the minimum section of the sliding panel that will
+     * be visible in the open state to allow for a closing drag.
+     */
+    private final int mOverhangSize;
+
+    /**
+     * True if a panel can slide with the current measurements
+     */
+    private boolean mCanSlide;
+
+    /**
+     * The child view that can slide, if any.
+     */
+    private View mSlideableView;
+
+    /**
+     * The view that can be used to start the drag with.
+     */
+    private View mCapturableView;
+
+    /**
+     * How far the panel is offset from its closed position.
+     * range [0, 1] where 0 = closed, 1 = open.
+     */
+    private float mSlideOffset;
+
+    /**
+     * How far in pixels the slideable panel may move.
+     */
+    private int mSlideRange;
+
+    /**
+     * A panel view is locked into internal scrolling or another condition that
+     * is preventing a drag.
+     */
+    private boolean mIsUnableToDrag;
+
+    private float mInitialMotionX;
+    private float mInitialMotionY;
+
+    private PanelSlideListener mPanelSlideListener;
+
+    private final ViewDragHelper mDragHelper;
+
+    /**
+     * Stores whether or not the pane was open the last time it was slideable.
+     * If open/close operations are invoked this state is modified. Used by
+     * instance state save/restore.
+     */
+    private boolean mPreservedOpenState;
+    private boolean mFirstLayout = true;
+
+    private final Rect mTmpRect = new Rect();
+
+    private final ArrayList<DisableLayerRunnable> mPostedRunnables =
+            new ArrayList<DisableLayerRunnable>();
+
+    /**
+     * Listener for monitoring events about sliding panes.
+     */
+    public interface PanelSlideListener {
+        /**
+         * Called when a sliding pane's position changes.
+         * @param panel The child view that was moved
+         * @param slideOffset The new offset of this sliding pane within its range, from 0-1
+         */
+        public void onPanelSlide(View panel, float slideOffset);
+        /**
+         * Called when a sliding pane becomes slid completely open. The pane may or may not
+         * be interactive at this point depending on how much of the pane is visible.
+         * @param panel The child view that was slid to an open position, revealing other panes
+         */
+        public void onPanelOpened(View panel);
+
+        /**
+         * Called when a sliding pane becomes slid completely closed. The pane is now guaranteed
+         * to be interactive. It may now obscure other views in the layout.
+         * @param panel The child view that was slid to a closed position
+         */
+        public void onPanelClosed(View panel);
+    }
+
+    public OverlappingPaneLayout(Context context) {
+        this(context, null);
+    }
+
+    public OverlappingPaneLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public OverlappingPaneLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        final float density = context.getResources().getDisplayMetrics().density;
+        mOverhangSize = (int) (DEFAULT_OVERHANG_SIZE * density + 0.5f);
+
+        setWillNotDraw(false);
+
+        ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
+        ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+        mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
+        mDragHelper.setMinVelocity(MIN_FLING_VELOCITY * density);
+    }
+
+    /**
+     * Set the view that can be used to start dragging the sliding pane.
+     */
+    public void setCapturableView(View capturableView) {
+        mCapturableView = capturableView;
+    }
+
+    /**
+     * Set the color used to fade the sliding pane out when it is slid most of the way offscreen.
+     *
+     * @param color An ARGB-packed color value
+     */
+    public void setSliderFadeColor(int color) {
+        mSliderFadeColor = color;
+    }
+
+    /**
+     * @return The ARGB-packed color value used to fade the sliding pane
+     */
+    public int getSliderFadeColor() {
+        return mSliderFadeColor;
+    }
+
+    /**
+     * Set the color used to fade the pane covered by the sliding pane out when the pane
+     * will become fully covered in the closed state.
+     *
+     * @param color An ARGB-packed color value
+     */
+    public void setCoveredFadeColor(int color) {
+        mCoveredFadeColor = color;
+    }
+
+    /**
+     * @return The ARGB-packed color value used to fade the fixed pane
+     */
+    public int getCoveredFadeColor() {
+        return mCoveredFadeColor;
+    }
+
+    public void setPanelSlideListener(PanelSlideListener listener) {
+        mPanelSlideListener = listener;
+    }
+
+    void dispatchOnPanelSlide(View panel) {
+        if (mPanelSlideListener != null) {
+            mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
+        }
+    }
+
+    void dispatchOnPanelOpened(View panel) {
+        if (mPanelSlideListener != null) {
+            mPanelSlideListener.onPanelOpened(panel);
+        }
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+    }
+
+    void dispatchOnPanelClosed(View panel) {
+        if (mPanelSlideListener != null) {
+            mPanelSlideListener.onPanelClosed(panel);
+        }
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+    }
+
+    void updateObscuredViewsVisibility(View panel) {
+        final int startBound = getPaddingTop();
+        final int endBound = getHeight() - getPaddingBottom();
+
+        final int leftBound = getPaddingLeft();
+        final int rightBound = getWidth() - getPaddingRight();
+        final int left;
+        final int right;
+        final int top;
+        final int bottom;
+        if (panel != null && viewIsOpaque(panel)) {
+            left = panel.getLeft();
+            right = panel.getRight();
+            top = panel.getTop();
+            bottom = panel.getBottom();
+        } else {
+            left = right = top = bottom = 0;
+        }
+
+        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
+            final View child = getChildAt(i);
+
+            if (child == panel) {
+                // There are still more children above the panel but they won't be affected.
+                break;
+            }
+
+            final int clampedChildLeft = Math.max(leftBound, child.getLeft());
+            final int clampedChildRight = Math.min(rightBound, child.getRight());
+            final int clampedChildTop = Math.max(startBound, child.getTop());
+            final int clampedChildBottom = Math.min(endBound, child.getBottom());
+
+            final int vis;
+            if (clampedChildLeft >= left && clampedChildTop >= top &&
+                    clampedChildRight <= right && clampedChildBottom <= bottom) {
+                vis = INVISIBLE;
+            } else {
+                vis = VISIBLE;
+            }
+            child.setVisibility(vis);
+        }
+    }
+
+    void setAllChildrenVisible() {
+        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() == INVISIBLE) {
+                child.setVisibility(VISIBLE);
+            }
+        }
+    }
+
+    private static boolean viewIsOpaque(View v) {
+        if (ViewCompat.isOpaque(v)) return true;
+
+        final Drawable bg = v.getBackground();
+        if (bg != null) {
+            return bg.getOpacity() == PixelFormat.OPAQUE;
+        }
+        return false;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mFirstLayout = true;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mFirstLayout = true;
+
+        for (int i = 0, count = mPostedRunnables.size(); i < count; i++) {
+            final DisableLayerRunnable dlr = mPostedRunnables.get(i);
+            dlr.run();
+        }
+        mPostedRunnables.clear();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+        if (widthMode != MeasureSpec.EXACTLY) {
+            if (isInEditMode()) {
+                // Don't crash the layout editor. Consume all of the space if specified
+                // or pick a magic number from thin air otherwise.
+                // TODO Better communication with tools of this bogus state.
+                // It will crash on a real device.
+                if (widthMode == MeasureSpec.AT_MOST) {
+                    widthMode = MeasureSpec.EXACTLY;
+                } else if (widthMode == MeasureSpec.UNSPECIFIED) {
+                    widthMode = MeasureSpec.EXACTLY;
+                    widthSize = 300;
+                }
+            } else {
+                throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
+            }
+        } else if (heightMode == MeasureSpec.UNSPECIFIED) {
+            if (isInEditMode()) {
+                // Don't crash the layout editor. Pick a magic number from thin air instead.
+                // TODO Better communication with tools of this bogus state.
+                // It will crash on a real device.
+                if (heightMode == MeasureSpec.UNSPECIFIED) {
+                    heightMode = MeasureSpec.AT_MOST;
+                    heightSize = 300;
+                }
+            } else {
+                throw new IllegalStateException("Height must not be UNSPECIFIED");
+            }
+        }
+
+        int layoutWidth = 0;
+        int maxLayoutWidth = -1;
+        switch (widthMode) {
+            case MeasureSpec.EXACTLY:
+                layoutWidth = maxLayoutWidth = widthSize - getPaddingLeft() - getPaddingRight();
+                break;
+            case MeasureSpec.AT_MOST:
+                maxLayoutWidth = widthSize - getPaddingLeft() - getPaddingRight();
+                break;
+        }
+
+        float weightSum = 0;
+        boolean canSlide = false;
+        final int heightAvailable = heightSize - getPaddingTop() - getPaddingBottom();
+        int heightRemaining = heightAvailable;
+        final int childCount = getChildCount();
+
+        if (childCount > 2) {
+            Log.e(TAG, "onMeasure: More than two child views are not supported.");
+        }
+
+        // We'll find the current one below.
+        mSlideableView = null;
+
+        // First pass. Measure based on child LayoutParams width/height.
+        // Weight will incur a second pass.
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (child.getVisibility() == GONE) {
+                lp.dimWhenOffset = false;
+                continue;
+            }
+
+            if (lp.weight > 0) {
+                weightSum += lp.weight;
+
+                // If we have no height, weight is the only contributor to the final size.
+                // Measure this view on the weight pass only.
+                if (lp.height == 0) continue;
+            }
+
+            int childHeightSpec;
+            final int verticalMargin = lp.topMargin + lp.bottomMargin;
+            if (lp.height == LayoutParams.WRAP_CONTENT) {
+                childHeightSpec = MeasureSpec.makeMeasureSpec(heightAvailable - verticalMargin,
+                        MeasureSpec.AT_MOST);
+            } else if (lp.height == LayoutParams.MATCH_PARENT) {
+                childHeightSpec = MeasureSpec.makeMeasureSpec(heightAvailable - verticalMargin,
+                        MeasureSpec.EXACTLY);
+            } else {
+                childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
+            }
+
+            int childWidthSpec;
+            if (lp.width == LayoutParams.WRAP_CONTENT) {
+                childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, MeasureSpec.AT_MOST);
+            } else if (lp.width == LayoutParams.MATCH_PARENT) {
+                childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, MeasureSpec.EXACTLY);
+            } else {
+                childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
+            }
+
+            child.measure(childWidthSpec, childHeightSpec);
+            final int childWidth = child.getMeasuredWidth();
+            final int childHeight = child.getMeasuredHeight();
+
+            if (widthMode == MeasureSpec.AT_MOST && childWidth > layoutWidth) {
+                layoutWidth = Math.min(childWidth, maxLayoutWidth);
+            }
+
+            heightRemaining -= childHeight;
+            canSlide |= lp.slideable = heightRemaining < 0;
+            if (lp.slideable) {
+                mSlideableView = child;
+            }
+        }
+
+        // Resolve weight and make sure non-sliding panels are smaller than the full screen.
+        if (canSlide || weightSum > 0) {
+            final int fixedPanelHeightLimit = heightAvailable - mOverhangSize;
+
+            for (int i = 0; i < childCount; i++) {
+                final View child = getChildAt(i);
+
+                if (child.getVisibility() == GONE) {
+                    continue;
+                }
+
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+                if (child.getVisibility() == GONE) {
+                    continue;
+                }
+
+                final boolean skippedFirstPass = lp.height == 0 && lp.weight > 0;
+                final int measuredHeight = skippedFirstPass ? 0 : child.getMeasuredHeight();
+                if (canSlide && child != mSlideableView) {
+                    if (lp.height < 0 && (measuredHeight > fixedPanelHeightLimit || lp.weight > 0)) {
+                        // Fixed panels in a sliding configuration should
+                        // be clamped to the fixed panel limit.
+                        final int childWidthSpec;
+                        if (skippedFirstPass) {
+                            // Do initial width measurement if we skipped measuring this view
+                            // the first time around.
+                            if (lp.width == LayoutParams.WRAP_CONTENT) {
+                                childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth,
+                                        MeasureSpec.AT_MOST);
+                            } else if (lp.height == LayoutParams.MATCH_PARENT) {
+                                childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth,
+                                        MeasureSpec.EXACTLY);
+                            } else {
+                                childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width,
+                                        MeasureSpec.EXACTLY);
+                            }
+                        } else {
+                            childWidthSpec = MeasureSpec.makeMeasureSpec(
+                                    child.getMeasuredWidth(), MeasureSpec.EXACTLY);
+                        }
+                        final int childHeightSpec = MeasureSpec.makeMeasureSpec(
+                                fixedPanelHeightLimit, MeasureSpec.EXACTLY);
+                        child.measure(childWidthSpec, childHeightSpec);
+                    }
+                } else if (lp.weight > 0) {
+                    int childWidthSpec;
+                    if (lp.height == 0) {
+                        // This was skipped the first time; figure out a real width spec.
+                        if (lp.width == LayoutParams.WRAP_CONTENT) {
+                            childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth,
+                                    MeasureSpec.AT_MOST);
+                        } else if (lp.width == LayoutParams.MATCH_PARENT) {
+                            childWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth,
+                                    MeasureSpec.EXACTLY);
+                        } else {
+                            childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width,
+                                    MeasureSpec.EXACTLY);
+                        }
+                    } else {
+                        childWidthSpec = MeasureSpec.makeMeasureSpec(
+                                child.getMeasuredWidth(), MeasureSpec.EXACTLY);
+                    }
+
+                    if (canSlide) {
+                        // Consume available space
+                        final int verticalMargin = lp.topMargin + lp.bottomMargin;
+                        final int newHeight = heightAvailable - verticalMargin;
+                        final int childHeightSpec = MeasureSpec.makeMeasureSpec(
+                                newHeight, MeasureSpec.EXACTLY);
+                        if (measuredHeight != newHeight) {
+                            child.measure(childWidthSpec, childHeightSpec);
+                        }
+                    } else {
+                        // Distribute the extra width proportionally similar to LinearLayout
+                        final int heightToDistribute = Math.max(0, heightRemaining);
+                        final int addedHeight = (int) (lp.weight * heightToDistribute / weightSum);
+                        final int childHeightSpec = MeasureSpec.makeMeasureSpec(
+                                measuredHeight + addedHeight, MeasureSpec.EXACTLY);
+                        child.measure(childWidthSpec, childHeightSpec);
+                    }
+                }
+            }
+        }
+
+        final int measuredHeight = heightSize;
+        final int measuredWidth = layoutWidth + getPaddingLeft() + getPaddingRight();
+
+        setMeasuredDimension(measuredWidth, measuredHeight);
+        mCanSlide = canSlide;
+
+        if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE && !canSlide) {
+            // Cancel scrolling in progress, it's no longer relevant.
+            mDragHelper.abort();
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_TOP);
+
+        final int height = b - t;
+        final int paddingTop = getPaddingTop();
+        final int paddingBottom = getPaddingBottom();
+        final int paddingLeft = getPaddingLeft();
+
+        final int childCount = getChildCount();
+        int yStart = paddingTop;
+        int nextYStart = yStart;
+
+        if (mFirstLayout) {
+            mSlideOffset = mCanSlide && mPreservedOpenState ? 1.f : 0.f;
+        }
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            final int childHeight = child.getMeasuredHeight();
+
+            if (lp.slideable) {
+                final int margin = lp.topMargin + lp.bottomMargin;
+                final int range = Math.min(nextYStart,
+                        height - paddingBottom - mOverhangSize) - yStart - margin;
+                mSlideRange = range;
+                final int lpMargin = lp.topMargin;
+                lp.dimWhenOffset = yStart + lpMargin + range + childHeight / 2 >
+                        height - paddingBottom;
+                final int pos = (int) (range * mSlideOffset);
+                yStart += pos + lpMargin;
+                mSlideOffset = (float) pos / mSlideRange;
+            } else {
+                yStart = nextYStart;
+            }
+
+            final int childTop = yStart;
+            final int childBottom = childTop + childHeight;
+            final int childLeft = paddingLeft;
+            final int childRight = childLeft + child.getMeasuredWidth();
+
+            child.layout(childLeft, childTop, childRight, childBottom);
+
+            nextYStart += child.getHeight();
+        }
+
+        if (mFirstLayout) {
+            if (mCanSlide) {
+                if (((LayoutParams) mSlideableView.getLayoutParams()).dimWhenOffset) {
+                    dimChildView(mSlideableView, mSlideOffset, mSliderFadeColor);
+                }
+            } else {
+                // Reset the dim level of all children; it's irrelevant when nothing moves.
+                for (int i = 0; i < childCount; i++) {
+                    dimChildView(getChildAt(i), 0, mSliderFadeColor);
+                }
+            }
+            updateObscuredViewsVisibility(mSlideableView);
+        }
+
+        mFirstLayout = false;
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        // Recalculate sliding panes and their details
+        if (h != oldh) {
+            mFirstLayout = true;
+        }
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        super.requestChildFocus(child, focused);
+        if (!isInTouchMode() && !mCanSlide) {
+            mPreservedOpenState = child == mSlideableView;
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        final int action = MotionEventCompat.getActionMasked(ev);
+
+        // Preserve the open state based on the last view that was touched.
+        if (!mCanSlide && action == MotionEvent.ACTION_DOWN && getChildCount() > 1) {
+            // After the first things will be slideable.
+            final View secondChild = getChildAt(1);
+            if (secondChild != null) {
+                mPreservedOpenState = !mDragHelper.isViewUnder(secondChild,
+                        (int) ev.getX(), (int) ev.getY());
+            }
+        }
+
+        if (!mCanSlide || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
+            mDragHelper.cancel();
+            return super.onInterceptTouchEvent(ev);
+        }
+
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+            mDragHelper.cancel();
+            return false;
+        }
+
+        boolean interceptTap = false;
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN: {
+                mIsUnableToDrag = false;
+                final float x = ev.getX();
+                final float y = ev.getY();
+                mInitialMotionX = x;
+                mInitialMotionY = y;
+
+                if (mDragHelper.isViewUnder(mSlideableView, (int) x, (int) y) &&
+                        isDimmed(mSlideableView)) {
+                    interceptTap = true;
+                }
+                break;
+            }
+
+            case MotionEvent.ACTION_MOVE: {
+                final float x = ev.getX();
+                final float y = ev.getY();
+                final float adx = Math.abs(x - mInitialMotionX);
+                final float ady = Math.abs(y - mInitialMotionY);
+                final int slop = mDragHelper.getTouchSlop();
+                if (ady > slop && adx > ady || !isCapturableViewUnder((int) x, (int) y)) {
+                    mDragHelper.cancel();
+                    mIsUnableToDrag = true;
+                    return false;
+                }
+            }
+        }
+
+        final boolean interceptForDrag = mDragHelper.shouldInterceptTouchEvent(ev);
+
+        return interceptForDrag || interceptTap;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (!mCanSlide) {
+            return super.onTouchEvent(ev);
+        }
+
+        mDragHelper.processTouchEvent(ev);
+
+        final int action = ev.getAction();
+        boolean wantTouchEvents = true;
+
+        switch (action & MotionEventCompat.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                final float x = ev.getX();
+                final float y = ev.getY();
+                mInitialMotionX = x;
+                mInitialMotionY = y;
+                break;
+            }
+
+            case MotionEvent.ACTION_UP: {
+                if (isDimmed(mSlideableView)) {
+                    final float x = ev.getX();
+                    final float y = ev.getY();
+                    final float dx = x - mInitialMotionX;
+                    final float dy = y - mInitialMotionY;
+                    final int slop = mDragHelper.getTouchSlop();
+                    if (dx * dx + dy * dy < slop * slop &&
+                            mDragHelper.isViewUnder(mSlideableView, (int) x, (int) y)) {
+                        // Taps close a dimmed open pane.
+                        closePane(mSlideableView, 0);
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+
+        return wantTouchEvents;
+    }
+
+    private boolean closePane(View pane, int initialVelocity) {
+        if (mFirstLayout || smoothSlideTo(0.f, initialVelocity)) {
+            mPreservedOpenState = false;
+            return true;
+        }
+        return false;
+    }
+
+    private boolean openPane(View pane, int initialVelocity) {
+        if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) {
+            mPreservedOpenState = true;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Open the sliding pane if it is currently slideable. If first layout
+     * has already completed this will animate.
+     *
+     * @return true if the pane was slideable and is now open/in the process of opening
+     */
+    public boolean openPane() {
+        return openPane(mSlideableView, 0);
+    }
+
+    /**
+     * Close the sliding pane if it is currently slideable. If first layout
+     * has already completed this will animate.
+     *
+     * @return true if the pane was slideable and is now closed/in the process of closing
+     */
+    public boolean closePane() {
+        return closePane(mSlideableView, 0);
+    }
+
+    /**
+     * Check if the layout is completely open. It can be open either because the slider
+     * itself is open revealing the left pane, or if all content fits without sliding.
+     *
+     * @return true if sliding panels are completely open
+     */
+    public boolean isOpen() {
+        return !mCanSlide || mSlideOffset == 1;
+    }
+
+    /**
+     * Check if the content in this layout cannot fully fit side by side and therefore
+     * the content pane can be slid back and forth.
+     *
+     * @return true if content in this layout can be slid open and closed
+     */
+    public boolean isSlideable() {
+        return mCanSlide;
+    }
+
+    private void onPanelDragged(int newTop) {
+        if (mSlideableView == null) {
+            // This can happen if we're aborting motion during layout because everything now fits.
+            mSlideOffset = 0;
+            return;
+        }
+        final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
+
+        final int lpMargin = lp.topMargin;
+        final int topBound = getPaddingTop() + lpMargin;
+
+        mSlideOffset = (float) (newTop - topBound) / mSlideRange;
+
+        if (lp.dimWhenOffset) {
+            dimChildView(mSlideableView, mSlideOffset, mSliderFadeColor);
+        }
+        dispatchOnPanelSlide(mSlideableView);
+    }
+
+    private void dimChildView(View v, float mag, int fadeColor) {
+        final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+
+        if (mag > 0 && fadeColor != 0) {
+            final int baseAlpha = (fadeColor & 0xff000000) >>> 24;
+            int imag = (int) (baseAlpha * mag);
+            int color = imag << 24 | (fadeColor & 0xffffff);
+            if (lp.dimPaint == null) {
+                lp.dimPaint = new Paint();
+            }
+            lp.dimPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_OVER));
+            if (ViewCompat.getLayerType(v) != ViewCompat.LAYER_TYPE_HARDWARE) {
+                ViewCompat.setLayerType(v, ViewCompat.LAYER_TYPE_HARDWARE, lp.dimPaint);
+            }
+            invalidateChildRegion(v);
+        } else if (ViewCompat.getLayerType(v) != ViewCompat.LAYER_TYPE_NONE) {
+            if (lp.dimPaint != null) {
+                lp.dimPaint.setColorFilter(null);
+            }
+            final DisableLayerRunnable dlr = new DisableLayerRunnable(v);
+            mPostedRunnables.add(dlr);
+            ViewCompat.postOnAnimation(this, dlr);
+        }
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        boolean result;
+        final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
+
+        if (mCanSlide && !lp.slideable && mSlideableView != null) {
+            // Clip against the slider; no sense drawing what will immediately be covered.
+            canvas.getClipBounds(mTmpRect);
+
+            mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop());
+            canvas.clipRect(mTmpRect);
+        }
+
+        if (Build.VERSION.SDK_INT >= 11) { // HC
+            result = super.drawChild(canvas, child, drawingTime);
+        } else {
+            if (lp.dimWhenOffset && mSlideOffset > 0) {
+                if (!child.isDrawingCacheEnabled()) {
+                    child.setDrawingCacheEnabled(true);
+                }
+                final Bitmap cache = child.getDrawingCache();
+                if (cache != null) {
+                    canvas.drawBitmap(cache, child.getLeft(), child.getTop(), lp.dimPaint);
+                    result = false;
+                } else {
+                    Log.e(TAG, "drawChild: child view " + child + " returned null drawing cache");
+                    result = super.drawChild(canvas, child, drawingTime);
+                }
+            } else {
+                if (child.isDrawingCacheEnabled()) {
+                    child.setDrawingCacheEnabled(false);
+                }
+                result = super.drawChild(canvas, child, drawingTime);
+            }
+        }
+
+        canvas.restoreToCount(save);
+
+        return result;
+    }
+
+    private void invalidateChildRegion(View v) {
+        ViewCompat.setLayerPaint(v, ((LayoutParams) v.getLayoutParams()).dimPaint);
+    }
+
+    /**
+     * Smoothly animate mDraggingPane to the target X position within its range.
+     *
+     * @param slideOffset position to animate to
+     * @param velocity initial velocity in case of fling, or 0.
+     */
+    boolean smoothSlideTo(float slideOffset, int velocity) {
+        if (!mCanSlide) {
+            // Nothing to do.
+            return false;
+        }
+
+        final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
+
+        int y;
+        int topBound = getPaddingTop() + lp.topMargin;
+        y = (int) (topBound + slideOffset * mSlideRange);
+
+        if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), y)) {
+            setAllChildrenVisible();
+            ViewCompat.postInvalidateOnAnimation(this);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void computeScroll() {
+        if (mDragHelper.continueSettling(true)) {
+            if (!mCanSlide) {
+                mDragHelper.abort();
+                return;
+            }
+
+            ViewCompat.postInvalidateOnAnimation(this);
+        }
+    }
+
+    private boolean isCapturableViewUnder(int x, int y) {
+        View capturableView = mCapturableView != null ? mCapturableView : mSlideableView;
+        if (capturableView == null) {
+            return false;
+        }
+        int[] viewLocation = new int[2];
+        capturableView.getLocationOnScreen(viewLocation);
+        int[] parentLocation = new int[2];
+        this.getLocationOnScreen(parentLocation);
+        int screenX = parentLocation[0] + x;
+        int screenY = parentLocation[1] + y;
+        return screenX >= viewLocation[0]
+                && screenX < viewLocation[0] + capturableView.getWidth()
+                && screenY >= viewLocation[1]
+                && screenY < viewLocation[1] + capturableView.getHeight();
+    }
+
+    boolean isDimmed(View child) {
+        if (child == null) {
+            return false;
+        }
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        return mCanSlide && lp.dimWhenOffset && mSlideOffset > 0;
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams();
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof MarginLayoutParams
+                ? new LayoutParams((MarginLayoutParams) p)
+                : new LayoutParams(p);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams && super.checkLayoutParams(p);
+    }
+
+    @Override
+    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+
+        SavedState ss = new SavedState(superState);
+        ss.isOpen = isSlideable() ? isOpen() : mPreservedOpenState;
+
+        return ss;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+
+        if (ss.isOpen) {
+            openPane();
+        } else {
+            closePane();
+        }
+        mPreservedOpenState = ss.isOpen;
+    }
+
+    private class DragHelperCallback extends ViewDragHelper.Callback {
+
+        @Override
+        public boolean tryCaptureView(View child, int pointerId) {
+            if (mIsUnableToDrag) {
+                return false;
+            }
+
+            return ((LayoutParams) child.getLayoutParams()).slideable;
+        }
+
+        @Override
+        public void onViewDragStateChanged(int state) {
+            if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
+                if (mSlideOffset == 0) {
+                    updateObscuredViewsVisibility(mSlideableView);
+                    dispatchOnPanelClosed(mSlideableView);
+                    mPreservedOpenState = false;
+                } else {
+                    dispatchOnPanelOpened(mSlideableView);
+                    mPreservedOpenState = true;
+                }
+            }
+        }
+
+        @Override
+        public void onViewCaptured(View capturedChild, int activePointerId) {
+            // Make all child views visible in preparation for sliding things around
+            setAllChildrenVisible();
+        }
+
+        @Override
+        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+            onPanelDragged(top);
+            invalidate();
+        }
+
+        @Override
+        public void onViewReleased(View releasedChild, float xvel, float yvel) {
+            final LayoutParams lp = (LayoutParams) releasedChild.getLayoutParams();
+
+            int top = getPaddingTop() + lp.topMargin;
+            if (yvel > 0 || (yvel == 0 && mSlideOffset > 0.5f)) {
+                top += mSlideRange;
+            }
+
+            int left;
+            mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
+            invalidate();
+        }
+
+        @Override
+        public int getViewVerticalDragRange(View child) {
+            return mSlideRange;
+        }
+
+        @Override
+        public int clampViewPositionHorizontal(View child, int left, int dx) {
+            // Make sure we never move views horizontally.
+            return child.getLeft();
+        }
+
+        @Override
+        public int clampViewPositionVertical(View child, int top, int dy) {
+            final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
+
+            final int newTop;
+            int topBound = getPaddingTop() + lp.topMargin;
+            int bottomBound = topBound + mSlideRange;
+            newTop = Math.min(Math.max(top, topBound), bottomBound);
+
+            return newTop;
+        }
+
+        @Override
+        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
+            mDragHelper.captureChildView(mSlideableView, pointerId);
+        }
+    }
+
+    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+        private static final int[] ATTRS = new int[] {
+            android.R.attr.layout_weight
+        };
+
+        /**
+         * The weighted proportion of how much of the leftover space
+         * this child should consume after measurement.
+         */
+        public float weight = 0;
+
+        /**
+         * True if this pane is the slideable pane in the layout.
+         */
+        boolean slideable;
+
+        /**
+         * True if this view should be drawn dimmed
+         * when it's been offset from its default position.
+         */
+        boolean dimWhenOffset;
+
+        Paint dimPaint;
+
+        public LayoutParams() {
+            super(FILL_PARENT, FILL_PARENT);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(LayoutParams source) {
+            super(source);
+            this.weight = source.weight;
+        }
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+
+            final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
+            this.weight = a.getFloat(0, 0);
+            a.recycle();
+        }
+
+    }
+
+    static class SavedState extends BaseSavedState {
+        boolean isOpen;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            isOpen = in.readInt() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(isOpen ? 1 : 0);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    class AccessibilityDelegate extends AccessibilityDelegateCompat {
+        private final Rect mTmpRect = new Rect();
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+            final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
+            super.onInitializeAccessibilityNodeInfo(host, superNode);
+            copyNodeInfoNoChildren(info, superNode);
+            superNode.recycle();
+
+            info.setClassName(OverlappingPaneLayout.class.getName());
+            info.setSource(host);
+
+            final ViewParent parent = ViewCompat.getParentForAccessibility(host);
+            if (parent instanceof View) {
+                info.setParent((View) parent);
+            }
+
+            // This is a best-approximation of addChildrenForAccessibility()
+            // that accounts for filtering.
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                final View child = getChildAt(i);
+                if (!filter(child) && (child.getVisibility() == View.VISIBLE)) {
+                    // Force importance to "yes" since we can't read the value.
+                    ViewCompat.setImportantForAccessibility(
+                            child, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
+                    info.addChild(child);
+                }
+            }
+        }
+
+        @Override
+        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+            super.onInitializeAccessibilityEvent(host, event);
+
+            event.setClassName(OverlappingPaneLayout.class.getName());
+        }
+
+        @Override
+        public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+                AccessibilityEvent event) {
+            if (!filter(child)) {
+                return super.onRequestSendAccessibilityEvent(host, child, event);
+            }
+            return false;
+        }
+
+        public boolean filter(View child) {
+            return isDimmed(child);
+        }
+
+        /**
+         * This should really be in AccessibilityNodeInfoCompat, but there unfortunately
+         * seem to be a few elements that are not easily cloneable using the underlying API.
+         * Leave it private here as it's not general-purpose useful.
+         */
+        private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
+                AccessibilityNodeInfoCompat src) {
+            final Rect rect = mTmpRect;
+
+            src.getBoundsInParent(rect);
+            dest.setBoundsInParent(rect);
+
+            src.getBoundsInScreen(rect);
+            dest.setBoundsInScreen(rect);
+
+            dest.setVisibleToUser(src.isVisibleToUser());
+            dest.setPackageName(src.getPackageName());
+            dest.setClassName(src.getClassName());
+            dest.setContentDescription(src.getContentDescription());
+
+            dest.setEnabled(src.isEnabled());
+            dest.setClickable(src.isClickable());
+            dest.setFocusable(src.isFocusable());
+            dest.setFocused(src.isFocused());
+            dest.setAccessibilityFocused(src.isAccessibilityFocused());
+            dest.setSelected(src.isSelected());
+            dest.setLongClickable(src.isLongClickable());
+
+            dest.addAction(src.getActions());
+
+            dest.setMovementGranularities(src.getMovementGranularities());
+        }
+    }
+
+    private class DisableLayerRunnable implements Runnable {
+        final View mChildView;
+
+        DisableLayerRunnable(View childView) {
+            mChildView = childView;
+        }
+
+        @Override
+        public void run() {
+            if (mChildView.getParent() == OverlappingPaneLayout.this) {
+                ViewCompat.setLayerType(mChildView, ViewCompat.LAYER_TYPE_NONE, null);
+                invalidateChildRegion(mChildView);
+            }
+            mPostedRunnables.remove(this);
+        }
+    }
+}