com.aidy.bottomdrawerlayout.AllDrawerLayout.java Source code

Java tutorial

Introduction

Here is the source code for com.aidy.bottomdrawerlayout.AllDrawerLayout.java

Source

/*
 * Copyright (C) 2013 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.aidy.bottomdrawerlayout;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.KeyEventCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewGroupCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;

/**
 * DrawerLayout acts as a top-level container for window content that allows for
 * interactive "drawer" views to be pulled out from the edge of the window.
 * 
 * <p>
 * Drawer positioning and layout is controlled using the
 * <code>android:layout_gravity</code> attribute on child views corresponding to
 * which side of the view you want the drawer to emerge from: left or right. (Or
 * start/end on platform versions that support layout direction.)
 * </p>
 * 
 * <p>
 * To use a DrawerLayout, position your primary content view as the first child
 * with a width and height of <code>match_parent</code>. Add drawers as child
 * views after the main content view and set the <code>layout_gravity</code>
 * appropriately. Drawers commonly use <code>match_parent</code> for height with
 * a fixed width.
 * </p>
 * 
 * <p>
 * {@link DrawerListener} can be used to monitor the state and motion of drawer
 * views. Avoid performing expensive operations such as layout during animation
 * as it can cause stuttering; try to perform expensive operations during the
 * {@link #STATE_IDLE} state. {@link SimpleDrawerListener} offers default/no-op
 * implementations of each callback method.
 * </p>
 * 
 * <p>
 * As per the Android Design guide, any drawers positioned to the left/start
 * should always contain content for navigating around the application, whereas
 * any drawers positioned to the right/end should always contain actions to take
 * on the current content. This preserves the same navigation left, actions
 * right structure present in the Action Bar and elsewhere.
 * </p>
 */
public class AllDrawerLayout extends ViewGroup {
    private static final String TAG = "DrawerLayout.Bottom";

    /**
     * Indicates that any drawers are in an idle, settled state. No animation is
     * in progress.
     */
    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;

    /**
     * Indicates that a drawer is currently being dragged by the user.
     */
    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;

    /**
     * Indicates that a drawer is in the process of settling to a final
     * position.
     */
    public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;

    /**
     * The drawer is unlocked.
     */
    public static final int LOCK_MODE_UNLOCKED = 0;

    /**
     * The drawer is locked closed. The user may not open it, though the app may
     * open it programmatically.
     */
    public static final int LOCK_MODE_LOCKED_CLOSED = 1;

    /**
     * The drawer is locked open. The user may not close it, though the app may
     * close it programmatically.
     */
    public static final int LOCK_MODE_LOCKED_OPEN = 2;

    private static final int MIN_DRAWER_MARGIN = 64; // dp

    private static final int DEFAULT_SCRIM_COLOR = 0x99000000;

    /**
     * Length of time to delay before peeking the drawer.
     */
    private static final int PEEK_DELAY = 160; // ms

    /**
     * Minimum velocity that will be detected as a fling
     */
    private static final int MIN_FLING_VELOCITY = 400; // dips per second

    /**
     * Experimental feature.
     */
    private static final boolean ALLOW_EDGE_LOCK = false;

    private static final boolean CHILDREN_DISALLOW_INTERCEPT = true;

    private static final float TOUCH_SLOP_SENSITIVITY = 1.f;

    private static final int[] LAYOUT_ATTRS = new int[] { android.R.attr.layout_gravity };

    private int mMinDrawerMargin;

    private int mScrimColor = DEFAULT_SCRIM_COLOR;
    private float mScrimOpacity;
    private Paint mScrimPaint = new Paint();

    private final ViewDragHelper mLeftDragger;
    private final ViewDragHelper mRightDragger;
    private final ViewDragHelper mTopDragger;
    private final ViewDragHelper mBottomDragger;
    private final ViewDragCallback mLeftCallback;
    private final ViewDragCallback mRightCallback;
    private final ViewDragCallback mTopCallback;
    private final ViewDragCallback mBottomCallback;
    private int mLockModeLeft;
    private int mLockModeRight;
    private int mLockModeTop;
    private int mLockModeBottom;
    private Drawable mShadowLeft;
    private Drawable mShadowRight;
    private Drawable mShadowTop;
    private Drawable mShadowBottom;

    private int mDrawerState;
    private boolean mInLayout;
    private boolean mFirstLayout = true;

    private boolean mDisallowInterceptRequested;
    private boolean mChildrenCanceledTouch;

    private DrawerListener mListener;

    private float mInitialMotionX;
    private float mInitialMotionY;

    /**
     * Listener for monitoring events about drawers.
     */
    public interface DrawerListener {
        /**
         * Called when a drawer's position changes.
         * 
         * @param drawerView
         *            The child view that was moved
         * @param slideOffset
         *            The new offset of this drawer within its range, from 0-1
         */
        public void onDrawerSlide(View drawerView, float slideOffset);

        /**
         * Called when a drawer has settled in a completely open state. The
         * drawer is interactive at this point.
         * 
         * @param drawerView
         *            Drawer view that is now open
         */
        public void onDrawerOpened(View drawerView);

        /**
         * Called when a drawer has settled in a completely closed state.
         * 
         * @param drawerView
         *            Drawer view that is now closed
         */
        public void onDrawerClosed(View drawerView);

        /**
         * Called when the drawer motion state changes. The new state will be
         * one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or
         * {@link #STATE_SETTLING}.
         * 
         * @param newState
         *            The new drawer motion state
         */
        public void onDrawerStateChanged(int newState);
    }

    /**
     * Stub/no-op implementations of all methods of {@link DrawerListener}.
     * Override this if you only care about a few of the available callback
     * methods.
     */
    public static abstract class SimpleDrawerListener implements DrawerListener {
        @Override
        public void onDrawerSlide(View drawerView, float slideOffset) {
        }

        @Override
        public void onDrawerOpened(View drawerView) {
        }

        @Override
        public void onDrawerClosed(View drawerView) {
        }

        @Override
        public void onDrawerStateChanged(int newState) {
        }
    }

    public AllDrawerLayout(Context context) {
        this(context, null);
    }

    public AllDrawerLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AllDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        final float density = getResources().getDisplayMetrics().density;
        mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);
        final float minVel = MIN_FLING_VELOCITY * density;

        mLeftCallback = new ViewDragCallback(Gravity.LEFT);
        mRightCallback = new ViewDragCallback(Gravity.RIGHT);
        mTopCallback = new ViewDragCallback(Gravity.TOP);
        mBottomCallback = new ViewDragCallback(Gravity.BOTTOM);

        mLeftDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mLeftCallback);
        mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
        mLeftDragger.setMinVelocity(minVel);
        mLeftCallback.setDragger(mLeftDragger);

        mRightDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mRightCallback);
        mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);
        mRightDragger.setMinVelocity(minVel);
        mRightCallback.setDragger(mRightDragger);

        mTopDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mTopCallback);
        mTopDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_TOP);
        mTopDragger.setMinVelocity(minVel);
        mTopCallback.setDragger(mTopDragger);

        mBottomDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mBottomCallback);
        mBottomDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_BOTTOM);
        mBottomDragger.setMinVelocity(minVel);
        mBottomCallback.setDragger(mBottomDragger);

        // So that we can catch the back button
        setFocusableInTouchMode(true);

        ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
        ViewGroupCompat.setMotionEventSplittingEnabled(this, false);
    }

    /**
     * Set a simple drawable used for the left or right shadow. The drawable
     * provided must have a nonzero intrinsic width.
     * 
     * @param shadowDrawable
     *            Shadow drawable to use at the edge of a drawer
     * @param gravity
     *            Which drawer the shadow should apply to
     */
    public void setDrawerShadow(Drawable shadowDrawable, int gravity) {
        /*
         * TODO Someone someday might want to set more complex drawables here.
         * They're probably nuts, but we might want to consider registering
         * callbacks, setting states, etc. properly.
         */

        final int absGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));
        if ((absGravity & Gravity.LEFT) == Gravity.LEFT) {
            mShadowLeft = shadowDrawable;
            invalidate();
        }
        if ((absGravity & Gravity.RIGHT) == Gravity.RIGHT) {
            mShadowRight = shadowDrawable;
            invalidate();
        }
        if ((absGravity & Gravity.TOP) == Gravity.TOP) {
            mShadowTop = shadowDrawable;
            invalidate();
        }
        if ((absGravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
            mShadowBottom = shadowDrawable;
            invalidate();
        }
    }

    /**
     * Set a simple drawable used for the left or right shadow. The drawable
     * provided must have a nonzero intrinsic width.
     * 
     * @param resId
     *            Resource id of a shadow drawable to use at the edge of a
     *            drawer
     * @param gravity
     *            Which drawer the shadow should apply to
     */
    public void setDrawerShadow(int resId, int gravity) {
        setDrawerShadow(getResources().getDrawable(resId), gravity);
    }

    /**
     * Set a color to use for the scrim that obscures primary content while a
     * drawer is open.
     * 
     * @param color
     *            Color to use in 0xAARRGGBB format.
     */
    public void setScrimColor(int color) {
        mScrimColor = color;
        invalidate();
    }

    /**
     * Set a listener to be notified of drawer events.
     * 
     * @param listener
     *            Listener to notify when drawer events occur
     * @see DrawerListener
     */
    public void setDrawerListener(DrawerListener listener) {
        mListener = listener;
    }

    /**
     * Enable or disable interaction with all drawers.
     * 
     * <p>
     * This allows the application to restrict the user's ability to open or
     * close any drawer within this layout. DrawerLayout will still respond to
     * calls to {@link #openDrawer(int)}, {@link #closeDrawer(int)} and friends
     * if a drawer is locked.
     * </p>
     * 
     * <p>
     * Locking drawers open or closed will implicitly open or close any drawers
     * as appropriate.
     * </p>
     * 
     * @param lockMode
     *            The new lock mode for the given drawer. One of
     *            {@link #LOCK_MODE_UNLOCKED}, {@link #LOCK_MODE_LOCKED_CLOSED}
     *            or {@link #LOCK_MODE_LOCKED_OPEN}.
     */
    public void setDrawerLockMode(int lockMode) {
        setDrawerLockMode(lockMode, Gravity.LEFT);
        setDrawerLockMode(lockMode, Gravity.RIGHT);
        setDrawerLockMode(lockMode, Gravity.TOP);
        setDrawerLockMode(lockMode, Gravity.BOTTOM);
    }

    /**
     * Enable or disable interaction with the given drawer.
     * 
     * <p>
     * This allows the application to restrict the user's ability to open or
     * close the given drawer. DrawerLayout will still respond to calls to
     * {@link #openDrawer(int)}, {@link #closeDrawer(int)} and friends if a
     * drawer is locked.
     * </p>
     * 
     * <p>
     * Locking a drawer open or closed will implicitly open or close that drawer
     * as appropriate.
     * </p>
     * 
     * @param lockMode
     *            The new lock mode for the given drawer. One of
     *            {@link #LOCK_MODE_UNLOCKED}, {@link #LOCK_MODE_LOCKED_CLOSED}
     *            or {@link #LOCK_MODE_LOCKED_OPEN}.
     * @param edgeGravity
     *            Gravity.LEFT, RIGHT, START or END. Expresses which drawer to
     *            change the mode for.
     * 
     * @see #LOCK_MODE_UNLOCKED
     * @see #LOCK_MODE_LOCKED_CLOSED
     * @see #LOCK_MODE_LOCKED_OPEN
     */
    public void setDrawerLockMode(int lockMode, int edgeGravity) {
        final int absGravity = GravityCompat.getAbsoluteGravity(edgeGravity, ViewCompat.getLayoutDirection(this));
        final ViewDragHelper helper;
        switch (absGravity) {
        case Gravity.LEFT:
            mLockModeLeft = lockMode;
            helper = mLeftDragger;
            break;
        case Gravity.RIGHT:
            mLockModeRight = lockMode;
            helper = mRightDragger;
            break;
        case Gravity.TOP:
            mLockModeTop = lockMode;
            helper = mTopDragger;
            break;
        case Gravity.BOTTOM:
            mLockModeBottom = lockMode;
            helper = mBottomDragger;
            break;
        default:
            helper = mBottomDragger;
            break;
        }
        if (lockMode != LOCK_MODE_UNLOCKED) {
            // Cancel interaction in progress
            helper.cancel();
        }
        switch (lockMode) {
        case LOCK_MODE_LOCKED_OPEN:
            final View toOpen = findDrawerWithGravity(absGravity);
            if (toOpen != null) {
                openDrawer(toOpen);
            }
            break;
        case LOCK_MODE_LOCKED_CLOSED:
            final View toClose = findDrawerWithGravity(absGravity);
            if (toClose != null) {
                closeDrawer(toClose);
            }
            break;
        // default: do nothing
        }
    }

    /**
     * Enable or disable interaction with the given drawer.
     * 
     * <p>
     * This allows the application to restrict the user's ability to open or
     * close the given drawer. DrawerLayout will still respond to calls to
     * {@link #openDrawer(int)}, {@link #closeDrawer(int)} and friends if a
     * drawer is locked.
     * </p>
     * 
     * <p>
     * Locking a drawer open or closed will implicitly open or close that drawer
     * as appropriate.
     * </p>
     * 
     * @param lockMode
     *            The new lock mode for the given drawer. One of
     *            {@link #LOCK_MODE_UNLOCKED}, {@link #LOCK_MODE_LOCKED_CLOSED}
     *            or {@link #LOCK_MODE_LOCKED_OPEN}.
     * @param drawerView
     *            The drawer view to change the lock mode for
     * 
     * @see #LOCK_MODE_UNLOCKED
     * @see #LOCK_MODE_LOCKED_CLOSED
     * @see #LOCK_MODE_LOCKED_OPEN
     */
    public void setDrawerLockMode(int lockMode, View drawerView) {
        if (!isDrawerView(drawerView)) {
            throw new IllegalArgumentException(
                    "View " + drawerView + " is not a " + "drawer with appropriate layout_gravity");
        }
        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
        setDrawerLockMode(lockMode, gravity);
    }

    /**
     * Check the lock mode of the drawer with the given gravity.
     * 
     * @param edgeGravity
     *            Gravity of the drawer to check
     * @return one of {@link #LOCK_MODE_UNLOCKED},
     *         {@link #LOCK_MODE_LOCKED_CLOSED} or
     *         {@link #LOCK_MODE_LOCKED_OPEN}.
     */
    public int getDrawerLockMode(int edgeGravity) {
        final int absGravity = GravityCompat.getAbsoluteGravity(edgeGravity, ViewCompat.getLayoutDirection(this));
        switch (absGravity) {
        case Gravity.LEFT:
            return mLockModeLeft;
        case Gravity.RIGHT:
            return mLockModeRight;
        case Gravity.TOP:
            return mLockModeTop;
        case Gravity.BOTTOM:
            return mLockModeBottom;
        default:
            return LOCK_MODE_UNLOCKED;
        }
    }

    /**
     * Check the lock mode of the given drawer view.
     * 
     * @param drawerView
     *            Drawer view to check lock mode
     * @return one of {@link #LOCK_MODE_UNLOCKED},
     *         {@link #LOCK_MODE_LOCKED_CLOSED} or
     *         {@link #LOCK_MODE_LOCKED_OPEN}.
     */
    public int getDrawerLockMode(View drawerView) {
        final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
        switch (absGravity) {
        case Gravity.LEFT:
            return mLockModeLeft;
        case Gravity.RIGHT:
            return mLockModeRight;
        case Gravity.TOP:
            return mLockModeTop;
        case Gravity.BOTTOM:
            return mLockModeBottom;
        default:
            return LOCK_MODE_UNLOCKED;
        }
    }

    /**
     * Resolve the shared state of all drawers from the component
     * ViewDragHelpers. Should be called whenever a ViewDragHelper's state
     * changes.
     */
    void updateDrawerState(int forGravity, int activeState, View activeDrawer) {
        final int leftState = mLeftDragger.getViewDragState();
        final int rightState = mRightDragger.getViewDragState();
        final int topState = mTopDragger.getViewDragState();
        final int bottomState = mBottomDragger.getViewDragState();

        final int state;
        if (leftState == STATE_DRAGGING || rightState == STATE_DRAGGING || topState == STATE_DRAGGING
                || bottomState == STATE_DRAGGING) {
            state = STATE_DRAGGING;
        } else if (leftState == STATE_SETTLING || rightState == STATE_SETTLING || topState == STATE_SETTLING
                || bottomState == STATE_SETTLING) {
            state = STATE_SETTLING;
        } else {
            state = STATE_IDLE;
        }

        if (activeDrawer != null && activeState == STATE_IDLE) {
            final LayoutParams lp = (LayoutParams) activeDrawer.getLayoutParams();
            if (lp.onScreen == 0) {
                dispatchOnDrawerClosed(activeDrawer);
            } else if (lp.onScreen == 1) {
                dispatchOnDrawerOpened(activeDrawer);
            }
        }
        if (state != mDrawerState) {
            mDrawerState = state;
            if (mListener != null) {
                mListener.onDrawerStateChanged(state);
            }
        }
    }

    void dispatchOnDrawerClosed(View drawerView) {
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        if (lp.knownOpen) {
            lp.knownOpen = false;
            if (mListener != null) {
                mListener.onDrawerClosed(drawerView);
            }
            sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        }
    }

    void dispatchOnDrawerOpened(View drawerView) {
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        if (!lp.knownOpen) {
            lp.knownOpen = true;
            if (mListener != null) {
                mListener.onDrawerOpened(drawerView);
            }
            drawerView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        }
    }

    void dispatchOnDrawerSlide(View drawerView, float slideOffset) {
        if (mListener != null) {
            mListener.onDrawerSlide(drawerView, slideOffset);
        }
    }

    void setDrawerViewOffset(View drawerView, float slideOffset) {
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        if (slideOffset == lp.onScreen) {
            return;
        }

        lp.onScreen = slideOffset;
        dispatchOnDrawerSlide(drawerView, slideOffset);
    }

    float getDrawerViewOffset(View drawerView) {
        return ((LayoutParams) drawerView.getLayoutParams()).onScreen;
    }

    /**
     * @return the absolute gravity of the child drawerView, resolved according
     *         to the current layout direction
     */
    int getDrawerViewAbsoluteGravity(View drawerView) {
        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
        return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));
    }

    boolean checkDrawerViewAbsoluteGravity(View drawerView, int checkFor) {
        final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
        return (absGravity & checkFor) == checkFor;
    }

    View findOpenDrawer() {
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            if (((LayoutParams) child.getLayoutParams()).knownOpen) {
                return child;
            }
        }
        return null;
    }

    void moveDrawerToOffset(View drawerView, float slideOffset) {
        Log.i(TAG, "BottomDrawerLayout -- moveDrawerToOffset() -- slideOffset = " + slideOffset);
        final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
        final float oldOffset = getDrawerViewOffset(drawerView);
        final int width = drawerView.getWidth();
        final int height = drawerView.getHeight();
        final int xoldPos = (int) (width * oldOffset);
        final int xnewPos = (int) (width * slideOffset);
        final int yoldPos = (int) (height * oldOffset);
        final int ynewPos = (int) (height * slideOffset);
        int dx = xnewPos - xoldPos;
        int dy = ynewPos - yoldPos;

        if (absGravity == Gravity.LEFT || absGravity == Gravity.RIGHT) {
            drawerView.offsetLeftAndRight(checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT) ? dx : -dx);
        }
        if (absGravity == Gravity.TOP || absGravity == Gravity.BOTTOM) {
            drawerView.offsetTopAndBottom(checkDrawerViewAbsoluteGravity(drawerView, Gravity.TOP) ? dy : -dy);
        }
        setDrawerViewOffset(drawerView, slideOffset);
    }

    /**
     * @param gravity
     *            the gravity of the child to return. If specified as a relative
     *            value, it will be resolved according to the current layout
     *            direction.
     * @return the drawer with the specified gravity
     */
    View findDrawerWithGravity(int gravity) {
        final int absHorizGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this))
                & Gravity.HORIZONTAL_GRAVITY_MASK;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final int childAbsGravity = getDrawerViewAbsoluteGravity(child);
            if ((childAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == absHorizGravity) {
                return child;
            }
        }
        return null;
    }

    /**
     * Simple gravity to string - only supports LEFT and RIGHT for debugging
     * output.
     * 
     * @param gravity
     *            Absolute gravity value
     * @return LEFT or RIGHT as appropriate, or a hex string
     */
    static String gravityToString(int gravity) {
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            return "LEFT";
        }
        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            return "RIGHT";
        }
        if ((gravity & Gravity.TOP) == Gravity.TOP) {
            return "TOP";
        }
        if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
            return "BOTTOM";
        }
        return Integer.toHexString(gravity);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mFirstLayout = true;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mFirstLayout = true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i(TAG, "onMeasure -- widthMeasureSpec = " + widthMeasureSpec + " -- heightMeasureSpec = "
                + heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            if (isInEditMode()) {
                if (widthMode == MeasureSpec.AT_MOST) {
                    widthMode = MeasureSpec.EXACTLY;
                } else if (widthMode == MeasureSpec.UNSPECIFIED) {
                    widthMode = MeasureSpec.EXACTLY;
                    widthSize = 300;
                }
                if (heightMode == MeasureSpec.AT_MOST) {
                    heightMode = MeasureSpec.EXACTLY;
                } else if (heightMode == MeasureSpec.UNSPECIFIED) {
                    heightMode = MeasureSpec.EXACTLY;
                    heightSize = 300;
                }
            } else {
                throw new IllegalArgumentException("DrawerLayout must be measured with MeasureSpec.EXACTLY.");
            }
        }

        setMeasuredDimension(widthSize, heightSize);

        // Gravity value for each drawer we've seen. Only one of each permitted.
        int foundDrawers = 0;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            if (child.getVisibility() == GONE) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (isContentView(child)) {
                // Content views get measured at exactly the layout's size.
                final int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - lp.leftMargin - lp.rightMargin,
                        MeasureSpec.EXACTLY);
                final int contentHeightSpec = MeasureSpec
                        .makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
                child.measure(contentWidthSpec, contentHeightSpec);
            } else if (isDrawerView(child)) {
                final int childGravity = getDrawerViewAbsoluteGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
                if ((foundDrawers & childGravity) != 0) {
                    throw new IllegalStateException(
                            "Child drawer has absolute gravity " + gravityToString(childGravity) + " but this "
                                    + TAG + " already has a " + "drawer view along that edge");
                }
                final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                        mMinDrawerMargin + lp.leftMargin + lp.rightMargin, lp.width);
                final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                        mMinDrawerMargin + lp.topMargin + lp.bottomMargin, lp.height);
                child.measure(drawerWidthSpec, drawerHeightSpec);
            } else {
                throw new IllegalStateException("Child " + child + " at index " + i
                        + " does not have a valid layout_gravity - must be Gravity.LEFT, "
                        + "Gravity.RIGHT or Gravity.NO_GRAVITY");
            }
        }
    }

    /**
     * 12-03 22:59:19.686: I/BottomDrawerLayout(12480): onLayout() -- left = 0
     * -- top = 0 -- right = 1080 -- b = 1675 12-03 22:59:19.686:
     * I/BottomDrawerLayout(12480): onLayout() -- childWidth = 750 --
     * childHeight = 1675 -- lp.onScreen = 0.0 12-03 22:59:19.686:
     * I/BottomDrawerLayout(12480): onLayout() -- childLeft = -750 -- newOffset
     * = 0.0 12-03 22:59:19.686: I/BottomDrawerLayout(12480): onLayout() --
     * childWidth = 750 -- childHeight = 1675 -- lp.onScreen = 0.0 12-03
     * 22:59:19.686: I/BottomDrawerLayout(12480): onLayout() -- childLeft = 1080
     * -- newOffset = 0.0
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i(TAG, "onLayout() -- left = " + l + " -- top = " + t + " -- right = " + r + " -- b = " + b);
        mInLayout = true;
        final int width = r - l;// 
        final int height = b - t;// 
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (isContentView(child)) {
                child.layout(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(),
                        lp.topMargin + child.getMeasuredHeight());
            } else {
                // ?view
                final int childWidth = child.getMeasuredWidth();
                final int childHeight = child.getMeasuredHeight();
                // Log.i(TAG, "onLayout() -- childWidth = " + childWidth +
                // " -- childHeight = " + childHeight
                // + " -- lp.onScreen = " + lp.onScreen);
                int childLeft = 0;// 
                int childTop = 0;// 
                float newOffset = 0;// 

                switch (getDrawerViewAbsoluteGravity(child)) {
                case Gravity.LEFT:
                    if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
                        // Log.i(TAG, "onLayout() -- 1");
                        childLeft = -childWidth + (int) (childWidth * lp.onScreen);
                        newOffset = (float) (childWidth + childLeft) / childWidth;// ?
                    }
                    break;
                case Gravity.RIGHT:
                    if (checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
                        // Log.i(TAG, "onLayout() -- 2");
                        childLeft = width - (int) (childWidth * lp.onScreen);
                        newOffset = (float) (width - childLeft) / childWidth;// ?
                    }
                    break;
                case Gravity.TOP:
                    if (checkDrawerViewAbsoluteGravity(child, Gravity.TOP)) {
                        // Log.i(TAG, "onLayout() -- 3");
                        childTop = -childHeight + (int) (childHeight * lp.onScreen);
                        newOffset = (float) (childHeight + childTop) / childHeight;// ?
                    }
                    break;
                case Gravity.BOTTOM:
                    if (checkDrawerViewAbsoluteGravity(child, Gravity.BOTTOM)) {
                        // Log.i(TAG, "onLayout() -- 4");
                        childTop = height - (int) (childHeight * lp.onScreen);
                        newOffset = (float) (height - childTop) / childHeight;// ?
                    }
                    break;
                default:
                    childTop = height - (int) (childHeight * lp.onScreen);
                    newOffset = (float) (height - childTop) / childHeight;// ?
                    break;
                }
                // /////////////////////////////////////////
                // Log.i(TAG, "onLayout() -- childLeft = " + childLeft +
                // " -- newOffset = " + newOffset);
                final boolean changeOffset = newOffset != lp.onScreen;
                final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                switch (vgrav) {
                // case Gravity.TOP: {
                // Log.i(TAG, "onLayout() -- Gravity.TOP");
                // child.layout(childLeft, lp.topMargin, childLeft + childWidth,
                // lp.topMargin + childHeight);
                // break;
                // }
                // case Gravity.BOTTOM: {
                // Log.i(TAG, "onLayout() -- Gravity.BOTTOM");
                // child.layout(childLeft, height - lp.bottomMargin -
                // child.getMeasuredHeight(), childLeft
                // + childWidth, height - lp.bottomMargin);
                // break;
                // }
                case Gravity.CENTER_VERTICAL: {
                    // Log.i(TAG, "onLayout() -- Gravity.CENTER_VERTICAL");
                    int childTop_cv = (height - childHeight) / 2;

                    // Offset for margins. If things don't fit right because of
                    // bad measurement before, oh well.
                    if (childTop_cv < lp.topMargin) {
                        childTop_cv = lp.topMargin;
                    } else if (childTop_cv + childHeight > height - lp.bottomMargin) {
                        childTop_cv = height - lp.bottomMargin - childHeight;
                    }
                    child.layout(childLeft, childTop_cv, childLeft + childWidth, childTop_cv + childHeight);
                    break;
                }
                }

                // /////////////////////////////////////////

                if (changeOffset) {
                    setDrawerViewOffset(child, newOffset);
                }

                final int newVisibility = lp.onScreen > 0 ? VISIBLE : INVISIBLE;
                if (child.getVisibility() != newVisibility) {
                    child.setVisibility(newVisibility);
                }
            }
        }
        mInLayout = false;
        mFirstLayout = false;
    }

    @Override
    public void requestLayout() {
        if (!mInLayout) {
            super.requestLayout();
        }
    }

    @Override
    public void computeScroll() {
        Log.i(TAG, "computeScroll()");
        final int childCount = getChildCount();
        float scrimOpacity = 0;
        for (int i = 0; i < childCount; i++) {
            final float onscreen = ((LayoutParams) getChildAt(i).getLayoutParams()).onScreen;
            Log.i(TAG, "computeScroll() -- 1");
            scrimOpacity = Math.max(scrimOpacity, onscreen);
        }
        mScrimOpacity = scrimOpacity;

        // "|" used on purpose; both need to run.
        if (mLeftDragger.continueSettling(true) | mRightDragger.continueSettling(true)
                | mTopDragger.continueSettling(true) | mBottomDragger.continueSettling(true)) {
            Log.i(TAG, "computeScroll() -- 2");
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    private static boolean hasOpaqueBackground(View v) {
        final Drawable bg = v.getBackground();
        if (bg != null) {
            return bg.getOpacity() == PixelFormat.OPAQUE;
        }
        return false;
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        Log.i(TAG, "drawChild()");
        final int height = getHeight();
        final boolean drawingContent = isContentView(child);
        int clipLeft = 0, clipRight = getWidth();
        int clipTop = 0, clipBottom = getHeight();

        final int restoreCount = canvas.save();
        if (drawingContent) {
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View v = getChildAt(i);
                if (v == child || v.getVisibility() != VISIBLE || !hasOpaqueBackground(v) || !isDrawerView(v)
                        || v.getHeight() < height) {
                    Log.i(TAG, "drawChild() -- 0");
                    continue;
                }
                switch (getDrawerViewAbsoluteGravity(v)) {
                case Gravity.LEFT:
                    Log.i(TAG, "drawChild() -- 1");
                    if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {
                        final int vright = v.getRight();
                        if (vright > clipLeft)
                            clipLeft = vright;
                    }
                    break;
                case Gravity.RIGHT:
                    Log.i(TAG, "drawChild() -- 2");
                    if (checkDrawerViewAbsoluteGravity(v, Gravity.RIGHT)) {
                        final int vleft = v.getLeft();
                        if (vleft < clipRight)
                            clipRight = vleft;
                    }
                    break;
                case Gravity.TOP:
                    Log.i(TAG, "drawChild() -- 3");
                    if (checkDrawerViewAbsoluteGravity(v, Gravity.TOP)) {
                        final int vbottom = v.getBottom();
                        if (vbottom > clipTop) {
                            clipTop = vbottom;
                        }
                    }
                    break;
                case Gravity.BOTTOM:
                    Log.i(TAG, "drawChild() -- 4");
                    if (checkDrawerViewAbsoluteGravity(v, Gravity.BOTTOM)) {
                        final int vtop = v.getTop();
                        if (vtop < clipBottom) {
                            clipBottom = vtop;
                        }
                    }
                    break;
                default:
                    Log.i(TAG, "drawChild() -- 5");
                    final int vtop = v.getTop();
                    if (vtop < clipBottom) {
                        clipBottom = vtop;
                    }
                    break;
                }
            }
            canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
        }
        final boolean result = super.drawChild(canvas, child, drawingTime);
        canvas.restoreToCount(restoreCount);

        if (mScrimOpacity > 0 && drawingContent) {
            Log.i(TAG, "drawChild() -- drawingContent");
            final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;
            final int imag = (int) (baseAlpha * mScrimOpacity);
            final int color = imag << 24 | (mScrimColor & 0xffffff);
            mScrimPaint.setColor(color);
            canvas.drawRect(clipLeft, clipTop, clipRight, clipBottom, mScrimPaint);
        } else if (mShadowLeft != null && checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
            Log.i(TAG, "drawChild() -- LEFT");
            final int shadowWidth = mShadowLeft.getIntrinsicWidth();
            final int childRight = child.getRight();
            final int drawerPeekDistance = mLeftDragger.getEdgeSize();
            final float alpha = Math.max(0, Math.min((float) childRight / drawerPeekDistance, 1.f));
            mShadowLeft.setBounds(childRight, child.getTop(), childRight + shadowWidth, child.getBottom());
            mShadowLeft.setAlpha((int) (0xff * alpha));
            mShadowLeft.draw(canvas);
        } else if (mShadowRight != null && checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
            Log.i(TAG, "drawChild() -- Gravity.RIGHT");
            final int shadowWidth = mShadowRight.getIntrinsicWidth();
            final int childLeft = child.getLeft();
            final int showing = getWidth() - childLeft;
            final int drawerPeekDistance = mRightDragger.getEdgeSize();
            final float alpha = Math.max(0, Math.min((float) showing / drawerPeekDistance, 1.f));
            mShadowRight.setBounds(childLeft - shadowWidth, child.getTop(), childLeft, child.getBottom());
            mShadowRight.setAlpha((int) (0xff * alpha));
            mShadowRight.draw(canvas);
        } else if (mShadowTop != null && checkDrawerViewAbsoluteGravity(child, Gravity.TOP)) {
            Log.i(TAG, "drawChild() -- Gravity.TOP");
            final int shadowHeight = mShadowTop.getIntrinsicHeight();
            final int childBottom = child.getBottom();
            final int drawerPeekDistance = mTopDragger.getEdgeSize();
            final float alpha = Math.max(0, Math.min((float) childBottom / drawerPeekDistance, 1.f));
            mShadowTop.setBounds(child.getLeft(), childBottom, child.getRight(), childBottom + shadowHeight);
            mShadowTop.setAlpha((int) (0xff * alpha));
            mShadowTop.draw(canvas);
        } else if (mShadowBottom != null && checkDrawerViewAbsoluteGravity(child, Gravity.BOTTOM)) {
            Log.i(TAG, "drawChild() -- Gravity.BOTTOM");
            final int shadowHeight = mShadowBottom.getIntrinsicWidth();
            final int childTop = child.getTop();
            final int showing = getHeight() - childTop;
            final int drawerPeekDistance = mBottomDragger.getEdgeSize();
            final float alpha = Math.max(0, Math.min((float) showing / drawerPeekDistance, 1.f));
            mShadowRight.setBounds(child.getLeft(), childTop - shadowHeight, child.getRight(), childTop);
            mShadowRight.setAlpha((int) (0xff * alpha));
            mShadowRight.draw(canvas);
        }
        return result;
    }

    boolean isContentView(View child) {
        return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
    }

    boolean isDrawerView(View child) {
        final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;
        final int absGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(child));
        return (absGravity & (Gravity.LEFT | Gravity.RIGHT | Gravity.TOP | Gravity.BOTTOM)) != 0;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(TAG, "onInterceptTouchEvent()");
        final int action = MotionEventCompat.getActionMasked(ev);

        // "|" used deliberately here; both methods should be invoked.
        final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev)
                | mRightDragger.shouldInterceptTouchEvent(ev) | mTopDragger.shouldInterceptTouchEvent(ev)
                | mBottomDragger.shouldInterceptTouchEvent(ev);
        boolean interceptForTap = false;
        switch (action) {
        case MotionEvent.ACTION_DOWN: {
            Log.i(TAG, "onInterceptTouchEvent() -- ACTION_DOWN");
            final float x = ev.getX();
            final float y = ev.getY();
            mInitialMotionX = x;
            mInitialMotionY = y;
            if (mScrimOpacity > 0 && isContentView(mLeftDragger.findTopChildUnder((int) x, (int) y))) {
                interceptForTap = true;
            }
            mDisallowInterceptRequested = false;
            mChildrenCanceledTouch = false;
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            Log.i(TAG, "onInterceptTouchEvent() -- ACTION_MOVE");
            // If we cross the touch slop, don't perform the delayed peek for an
            // edge touch.
            if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {
                Log.i(TAG, "onInterceptTouchEvent() -- ACTION_MOVE -- 2");
                mLeftCallback.removeCallbacks();
                mRightCallback.removeCallbacks();
                mTopCallback.removeCallbacks();
                mBottomCallback.removeCallbacks();
            }
            break;
        }

        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP: {
            Log.i(TAG, "onInterceptTouchEvent() -- ACTION_CANCEL | ACTION_UP");
            closeDrawers(true);
            mDisallowInterceptRequested = false;
            mChildrenCanceledTouch = false;
        }
        }

        boolean result = interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;
        Log.i(TAG, "onInterceptTouchEvent() -- result = " + result);
        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        Log.i(TAG, "onTouchEvent()");
        final int action = ev.getAction();
        boolean wantTouchEvents = true;
        try {

            mLeftDragger.processTouchEvent(ev);
            mRightDragger.processTouchEvent(ev);
            mTopDragger.processTouchEvent(ev);
            mBottomDragger.processTouchEvent(ev);

            switch (action & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                Log.i(TAG, "onTouchEvent() -- ACTION_DOWN");
                final float x = ev.getX();
                final float y = ev.getY();
                mInitialMotionX = x;
                mInitialMotionY = y;
                mDisallowInterceptRequested = false;
                mChildrenCanceledTouch = false;
                break;
            }

            case MotionEvent.ACTION_UP: {
                Log.i(TAG, "onTouchEvent() -- ACTION_UP");
                final float x = ev.getX();
                final float y = ev.getY();
                boolean peekingOnly = true;
                final View touchedView = mLeftDragger.findTopChildUnder((int) x, (int) y);
                if (touchedView != null && isContentView(touchedView)) {
                    final float dx = x - mInitialMotionX;
                    final float dy = y - mInitialMotionY;
                    final int slop = mLeftDragger.getTouchSlop();
                    if (dx * dx + dy * dy < slop * slop) {
                        // Taps close a dimmed open drawer but only if it isn't
                        // locked open.
                        final View openDrawer = findOpenDrawer();
                        if (openDrawer != null) {
                            peekingOnly = getDrawerLockMode(openDrawer) == LOCK_MODE_LOCKED_OPEN;
                        }
                    }
                }
                closeDrawers(peekingOnly);
                mDisallowInterceptRequested = false;
                break;
            }

            case MotionEvent.ACTION_CANCEL: {
                Log.i(TAG, "onTouchEvent() -- ACTION_CANCEL");
                closeDrawers(true);
                mDisallowInterceptRequested = false;
                mChildrenCanceledTouch = false;
                break;
            }
            }
        } catch (IllegalArgumentException e) {
            // TODO: handle exception
        }
        boolean result = wantTouchEvents;
        Log.i(TAG, "onTouchEvent() -- result = " + result);
        return result;
    }

    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        if (CHILDREN_DISALLOW_INTERCEPT || (!mLeftDragger.isEdgeTouched(ViewDragHelper.EDGE_LEFT)
                && !mRightDragger.isEdgeTouched(ViewDragHelper.EDGE_RIGHT)
                && !mTopDragger.isEdgeTouched(ViewDragHelper.EDGE_TOP)
                && !mBottomDragger.isEdgeTouched(ViewDragHelper.EDGE_BOTTOM))) {
            // If we have an edge touch we want to skip this and track it for
            // later instead.
            super.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
        mDisallowInterceptRequested = disallowIntercept;
        if (disallowIntercept) {
            closeDrawers(true);
        }
    }

    /**
     * Close all currently open drawer views by animating them out of view.
     */
    public void closeDrawers() {
        closeDrawers(false);
    }

    void closeDrawers(boolean peekingOnly) {
        boolean needsInvalidate = false;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (!isDrawerView(child) || (peekingOnly && !lp.isPeeking)) {
                continue;
            }

            final int childWidth = child.getWidth();
            switch (getDrawerViewAbsoluteGravity(child)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT))
                    needsInvalidate |= mLeftDragger.smoothSlideViewTo(child, -childWidth, child.getTop());
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT))
                    needsInvalidate |= mRightDragger.smoothSlideViewTo(child, getWidth(), child.getTop());
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.TOP))
                    needsInvalidate |= mTopDragger.smoothSlideViewTo(child, getWidth(), child.getTop());
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.BOTTOM))
                    needsInvalidate |= mBottomDragger.smoothSlideViewTo(child, getWidth(), child.getTop());
                break;
            default:
                needsInvalidate |= mBottomDragger.smoothSlideViewTo(child, getWidth(), child.getTop());
                break;
            }
            lp.isPeeking = false;
        }

        mLeftCallback.removeCallbacks();
        mRightCallback.removeCallbacks();
        mTopCallback.removeCallbacks();
        mBottomCallback.removeCallbacks();

        if (needsInvalidate) {
            invalidate();
        }
    }

    /**
     * Open the specified drawer view by animating it into view.
     * 
     * @param drawerView
     *            Drawer view to open
     */
    public void openDrawer(View drawerView) {
        if (!isDrawerView(drawerView)) {
            throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
        }

        if (mFirstLayout) {
            final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
            lp.onScreen = 1.f;
            lp.knownOpen = true;
        } else {
            switch (getDrawerViewAbsoluteGravity(drawerView)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT))
                    mLeftDragger.smoothSlideViewTo(drawerView, 0, drawerView.getTop());
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.RIGHT))
                    mRightDragger.smoothSlideViewTo(drawerView, getWidth() - drawerView.getWidth(),
                            drawerView.getTop());
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.TOP))
                    mTopDragger.smoothSlideViewTo(drawerView, drawerView.getLeft(), 0);
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.BOTTOM))
                    mBottomDragger.smoothSlideViewTo(drawerView, getHeight() - drawerView.getHeight(),
                            drawerView.getLeft());
                break;
            default:
                mBottomDragger.smoothSlideViewTo(drawerView, getHeight() - drawerView.getHeight(),
                        drawerView.getLeft());
                break;
            }
        }
        invalidate();
    }

    /**
     * Open the specified drawer by animating it out of view.
     * 
     * @param gravity
     *            Gravity.LEFT to move the left drawer or Gravity.RIGHT for the
     *            right. GravityCompat.START or GravityCompat.END may also be
     *            used.
     */
    public void openDrawer(int gravity) {
        final View drawerView = findDrawerWithGravity(gravity);
        if (drawerView == null) {
            throw new IllegalArgumentException("No drawer view found with gravity " + gravityToString(gravity));
        }
        openDrawer(drawerView);
    }

    /**
     * Close the specified drawer view by animating it into view.
     * 
     * @param drawerView
     *            Drawer view to close
     */
    public void closeDrawer(View drawerView) {
        if (!isDrawerView(drawerView)) {
            throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
        }

        if (mFirstLayout) {
            final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
            lp.onScreen = 0.f;
            lp.knownOpen = false;
        } else {
            switch (getDrawerViewAbsoluteGravity(drawerView)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT))
                    mLeftDragger.smoothSlideViewTo(drawerView, -drawerView.getWidth(), drawerView.getTop());
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.RIGHT))
                    mRightDragger.smoothSlideViewTo(drawerView, getWidth(), drawerView.getTop());
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.TOP))
                    mTopDragger.smoothSlideViewTo(drawerView, drawerView.getLeft(), -drawerView.getHeight());
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.BOTTOM))
                    mBottomDragger.smoothSlideViewTo(drawerView, drawerView.getLeft(), getHeight());
                break;
            default:
                mBottomDragger.smoothSlideViewTo(drawerView, drawerView.getLeft(), getHeight());
                break;
            }
        }
        invalidate();
    }

    /**
     * Close the specified drawer by animating it out of view.
     * 
     * @param gravity
     *            Gravity.LEFT to move the left drawer or Gravity.RIGHT for the
     *            right. GravityCompat.START or GravityCompat.END may also be
     *            used.
     */
    public void closeDrawer(int gravity) {
        final View drawerView = findDrawerWithGravity(gravity);
        if (drawerView == null) {
            throw new IllegalArgumentException("No drawer view found with gravity " + gravityToString(gravity));
        }
        closeDrawer(drawerView);
    }

    /**
     * Check if the given drawer view is currently in an open state. To be
     * considered "open" the drawer must have settled into its fully visible
     * state. To check for partial visibility use
     * {@link #isDrawerVisible(android.view.View)}.
     * 
     * @param drawer
     *            Drawer view to check
     * @return true if the given drawer view is in an open state
     * @see #isDrawerVisible(android.view.View)
     */
    public boolean isDrawerOpen(View drawer) {
        if (!isDrawerView(drawer)) {
            throw new IllegalArgumentException("View " + drawer + " is not a drawer");
        }
        return ((LayoutParams) drawer.getLayoutParams()).knownOpen;
    }

    /**
     * Check if the given drawer view is currently in an open state. To be
     * considered "open" the drawer must have settled into its fully visible
     * state. If there is no drawer with the given gravity this method will
     * return false.
     * 
     * @param drawerGravity
     *            Gravity of the drawer to check
     * @return true if the given drawer view is in an open state
     */
    public boolean isDrawerOpen(int drawerGravity) {
        final View drawerView = findDrawerWithGravity(drawerGravity);
        if (drawerView != null) {
            return isDrawerOpen(drawerView);
        }
        return false;
    }

    /**
     * Check if a given drawer view is currently visible on-screen. The drawer
     * may be only peeking onto the screen, fully extended, or anywhere
     * inbetween.
     * 
     * @param drawer
     *            Drawer view to check
     * @return true if the given drawer is visible on-screen
     * @see #isDrawerOpen(android.view.View)
     */
    public boolean isDrawerVisible(View drawer) {
        if (!isDrawerView(drawer)) {
            throw new IllegalArgumentException("View " + drawer + " is not a drawer");
        }
        return ((LayoutParams) drawer.getLayoutParams()).onScreen > 0;
    }

    /**
     * Check if a given drawer view is currently visible on-screen. The drawer
     * may be only peeking onto the screen, fully extended, or anywhere
     * inbetween. If there is no drawer with the given gravity this method will
     * return false.
     * 
     * @param drawerGravity
     *            Gravity of the drawer to check
     * @return true if the given drawer is visible on-screen
     */
    public boolean isDrawerVisible(int drawerGravity) {
        final View drawerView = findDrawerWithGravity(drawerGravity);
        if (drawerView != null) {
            return isDrawerVisible(drawerView);
        }
        return false;
    }

    private boolean hasPeekingDrawer() {
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
            if (lp.isPeeking) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p)
                : p instanceof ViewGroup.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);
    }

    private boolean hasVisibleDrawer() {
        return findVisibleDrawer() != null;
    }

    private View findVisibleDrawer() {
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            if (isDrawerView(child) && isDrawerVisible(child)) {
                return child;
            }
        }
        return null;
    }

    void cancelChildViewTouch() {
        // Cancel child touches
        if (!mChildrenCanceledTouch) {
            final long now = SystemClock.uptimeMillis();
            final MotionEvent cancelEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                getChildAt(i).dispatchTouchEvent(cancelEvent);
            }
            cancelEvent.recycle();
            mChildrenCanceledTouch = true;
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && hasVisibleDrawer()) {
            KeyEventCompat.startTracking(event);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            final View visibleDrawer = findVisibleDrawer();
            if (visibleDrawer != null && getDrawerLockMode(visibleDrawer) == LOCK_MODE_UNLOCKED) {
                closeDrawers();
            }
            return visibleDrawer != null;
        }
        return super.onKeyUp(keyCode, event);
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        final SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        if (ss.openDrawerGravity != Gravity.NO_GRAVITY) {
            final View toOpen = findDrawerWithGravity(ss.openDrawerGravity);
            if (toOpen != null) {
                openDrawer(toOpen);
            }
        }

        setDrawerLockMode(ss.lockModeLeft, Gravity.LEFT);
        setDrawerLockMode(ss.lockModeRight, Gravity.RIGHT);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();

        final SavedState ss = new SavedState(superState);

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            if (!isDrawerView(child)) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (lp.knownOpen) {
                ss.openDrawerGravity = lp.gravity;
                // Only one drawer can be open at a time.
                break;
            }
        }

        ss.lockModeLeft = mLockModeLeft;
        ss.lockModeRight = mLockModeRight;
        ss.lockModeTop = mLockModeTop;
        ss.lockModeBottom = mLockModeBottom;

        return ss;
    }

    /**
     * State persisted across instances
     */
    protected static class SavedState extends BaseSavedState {
        int openDrawerGravity = Gravity.NO_GRAVITY;
        int lockModeLeft = LOCK_MODE_UNLOCKED;
        int lockModeRight = LOCK_MODE_UNLOCKED;
        int lockModeTop = LOCK_MODE_UNLOCKED;
        int lockModeBottom = LOCK_MODE_UNLOCKED;

        public SavedState(Parcel in) {
            super(in);
            openDrawerGravity = in.readInt();
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(openDrawerGravity);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel source) {
                return new SavedState(source);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    private class ViewDragCallback extends ViewDragHelper.Callback {
        private final int mAbsGravity;
        private ViewDragHelper mDragger;

        private final Runnable mPeekRunnable = new Runnable() {
            @Override
            public void run() {
                peekDrawer();
            }
        };

        public ViewDragCallback(int gravity) {
            mAbsGravity = gravity;
        }

        public void setDragger(ViewDragHelper dragger) {
            mDragger = dragger;
        }

        public void removeCallbacks() {
            AllDrawerLayout.this.removeCallbacks(mPeekRunnable);
        }

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            // Only capture views where the gravity matches what we're looking
            // for.
            // This lets us use two ViewDragHelpers, one for each side drawer.
            return isDrawerView(child) && checkDrawerViewAbsoluteGravity(child, mAbsGravity)
                    && getDrawerLockMode(child) == LOCK_MODE_UNLOCKED;
        }

        @Override
        public void onViewDragStateChanged(int state) {
            updateDrawerState(mAbsGravity, state, mDragger.getCapturedView());
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            Log.i(TAG, "onViewPositionChanged() -- left = " + left + " -- top = " + top + " -- dx = " + dx
                    + " -- dy = " + dy);
            float offset = 0;
            final int childWidth = changedView.getWidth();
            final int childHeight = changedView.getHeight();
            final int width = getWidth();
            final int height = getHeight();

            // This reverses the positioning shown in onLayout.
            switch (getDrawerViewAbsoluteGravity(changedView)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(changedView, Gravity.LEFT))
                    offset = (float) (childWidth + left) / childWidth;
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(changedView, Gravity.RIGHT))
                    offset = (float) (width - left) / childWidth;
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(changedView, Gravity.TOP))
                    offset = (float) (childHeight + top) / childHeight;
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(changedView, Gravity.BOTTOM))
                    offset = (float) (height - top) / childHeight;
                break;
            default:
                offset = (float) (height - top) / childHeight;
                break;
            }
            setDrawerViewOffset(changedView, offset);
            changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);
            invalidate();
        }

        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            final LayoutParams lp = (LayoutParams) capturedChild.getLayoutParams();
            lp.isPeeking = false;
            closeOtherDrawer();
        }

        private void closeOtherDrawer() {
            final int otherGrav;
            switch (mAbsGravity) {
            case Gravity.LEFT:
                otherGrav = Gravity.LEFT;
                break;
            case Gravity.RIGHT:
                otherGrav = Gravity.RIGHT;
                break;
            case Gravity.TOP:
                otherGrav = Gravity.TOP;
                break;
            case Gravity.BOTTOM:
                otherGrav = Gravity.BOTTOM;
                break;
            default:
                otherGrav = Gravity.BOTTOM;
                break;
            }
            final View toClose = findDrawerWithGravity(otherGrav);
            if (toClose != null) {
                closeDrawer(toClose);
            }
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            Log.i(TAG, "onViewReleased() -- xvel = " + xvel + " -- yvel = " + yvel);
            // Offset is how open the drawer is, therefore left/right values
            // are reversed from one another.
            final float offset = getDrawerViewOffset(releasedChild);
            final int childWidth = releasedChild.getWidth();
            final int childHeight = releasedChild.getHeight();

            int left = 0;
            int top = 0;
            switch (getDrawerViewAbsoluteGravity(releasedChild)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.LEFT)) {
                    left = xvel > 0 || xvel == 0 && offset > 0.5f ? 0 : -childWidth;
                    top = releasedChild.getTop();
                }
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.RIGHT)) {
                    final int width = getWidth();
                    left = xvel < 0 || xvel == 0 && offset > 0.5f ? width - childWidth : width;
                    top = releasedChild.getTop();
                }
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.TOP)) {
                    left = releasedChild.getLeft();
                    top = yvel > 0 || yvel == 0 && offset > 0.5f ? 0 : -childHeight;
                }
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.BOTTOM)) {
                    left = releasedChild.getLeft();
                    final int height = getHeight();
                    top = yvel < 0 || yvel == 0 && offset > 0.5f ? height - childHeight : height;
                }
                break;
            }
            mDragger.settleCapturedViewAt(left, top);
            invalidate();
        }

        @Override
        public void onEdgeTouched(int edgeFlags, int pointerId) {
            Log.i(TAG, "onEdgeTouched()");
            postDelayed(mPeekRunnable, PEEK_DELAY);
        }

        private void peekDrawer() {
            View toCapture = null;
            int childLeft = 0;
            int childTop = 0;
            final int peekDistance = mDragger.getEdgeSize();
            switch (mAbsGravity) {
            case Gravity.LEFT:
                toCapture = findDrawerWithGravity(Gravity.LEFT);
                childLeft = (toCapture != null ? -toCapture.getWidth() : 0) + peekDistance;
                childTop = 0;
                break;
            case Gravity.RIGHT:
                toCapture = findDrawerWithGravity(Gravity.RIGHT);
                childLeft = getWidth() - peekDistance;
                childTop = 0;
                break;
            case Gravity.TOP:
                toCapture = findDrawerWithGravity(Gravity.TOP);
                childLeft = 0;
                childTop = (toCapture != null ? -toCapture.getHeight() : 0) + peekDistance;
                break;
            case Gravity.BOTTOM:
                toCapture = findDrawerWithGravity(Gravity.BOTTOM);
                childLeft = 0;
                childTop = getHeight() - peekDistance;
                break;
            default:
                toCapture = findDrawerWithGravity(Gravity.BOTTOM);
                childLeft = 0;
                childTop = getHeight() - peekDistance;
            }
            boolean leftEdge = (mAbsGravity == Gravity.LEFT);
            boolean topEdge = (mAbsGravity == Gravity.TOP);
            if (toCapture != null
                    && ((leftEdge && toCapture.getLeft() < childLeft)
                            || (!leftEdge && toCapture.getLeft() > childLeft))
                    && ((topEdge && toCapture.getTop() < childTop) || (!topEdge && toCapture.getTop() > childTop))
                    && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
                final LayoutParams lp = (LayoutParams) toCapture.getLayoutParams();
                mDragger.smoothSlideViewTo(toCapture, childLeft, toCapture.getTop());
                lp.isPeeking = true;
                invalidate();
                closeOtherDrawer();
                cancelChildViewTouch();
            }
        }

        @Override
        public boolean onEdgeLock(int edgeFlags) {
            if (ALLOW_EDGE_LOCK) {
                final View drawer = findDrawerWithGravity(mAbsGravity);
                if (drawer != null && !isDrawerOpen(drawer)) {
                    closeDrawer(drawer);
                }
                return true;
            }
            return false;
        }

        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            final View toCapture;
            if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT) {
                toCapture = findDrawerWithGravity(Gravity.LEFT);
            } else if ((edgeFlags & ViewDragHelper.EDGE_RIGHT) == ViewDragHelper.EDGE_RIGHT) {
                toCapture = findDrawerWithGravity(Gravity.RIGHT);
            } else if ((edgeFlags & ViewDragHelper.EDGE_TOP) == ViewDragHelper.EDGE_TOP) {
                toCapture = findDrawerWithGravity(Gravity.TOP);
            } else/*
                  * if ((edgeFlags & ViewDragHelper.EDGE_BOTTOM) ==
                  * ViewDragHelper.EDGE_BOTTOM)
                  */ {
                toCapture = findDrawerWithGravity(Gravity.BOTTOM);
            }
            if (toCapture != null && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
                mDragger.captureChildView(toCapture, pointerId);
            }
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return child.getWidth();
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            // TODO Auto-generated method stub
            return child.getHeight();
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            switch (getDrawerViewAbsoluteGravity(child)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
                    return Math.max(-child.getWidth(), Math.min(left, 0));
                }
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
                    final int width = getWidth();
                    return Math.max(width - child.getWidth(), Math.min(left, width));
                }
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.TOP)) {
                    return child.getLeft();
                }
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.BOTTOM)) {
                    return child.getLeft();
                }
                break;
            default:
                final int width = getWidth();
                return Math.max(width - child.getWidth(), Math.min(left, width));
            }
            final int width = getWidth();
            return Math.max(width - child.getWidth(), Math.min(left, width));
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            switch (getDrawerViewAbsoluteGravity(child)) {
            case Gravity.LEFT:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
                    return child.getTop();
                }
                break;
            case Gravity.RIGHT:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
                    return child.getTop();
                }
                break;
            case Gravity.TOP:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.TOP)) {
                    return Math.max(-child.getHeight(), Math.min(top, 0));
                }
                break;
            case Gravity.BOTTOM:
                if (checkDrawerViewAbsoluteGravity(child, Gravity.BOTTOM)) {
                    final int height = getHeight();
                    return Math.max(height - child.getHeight(), Math.min(top, height));
                }
                break;
            default:
                final int height = getHeight();
                return Math.max(height - child.getHeight(), Math.min(top, height));
            }
            final int height = getHeight();
            return Math.max(height - child.getHeight(), Math.min(top, height));
        }
    }

    public static class LayoutParams extends ViewGroup.MarginLayoutParams {

        public int gravity = Gravity.NO_GRAVITY;
        float onScreen;
        boolean isPeeking;
        boolean knownOpen;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);

            final TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
            this.gravity = a.getInt(0, Gravity.NO_GRAVITY);
            a.recycle();
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(int width, int height, int gravity) {
            this(width, height);
            this.gravity = gravity;
        }

        public LayoutParams(LayoutParams source) {
            super(source);
            this.gravity = source.gravity;
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(ViewGroup.MarginLayoutParams source) {
            super(source);
        }
    }

    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);

            info.setSource(host);
            final ViewParent parent = ViewCompat.getParentForAccessibility(host);
            if (parent instanceof View) {
                info.setParent((View) parent);
            }
            copyNodeInfoNoChildren(info, superNode);

            superNode.recycle();

            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                if (!filter(child)) {
                    info.addChild(child);
                }
            }
        }

        @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) {
            final View openDrawer = findOpenDrawer();
            return openDrawer != null && openDrawer != 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());
        }
    }
}