com.doubleTwist.drawerlib.ADrawerLayout.java Source code

Java tutorial

Introduction

Here is the source code for com.doubleTwist.drawerlib.ADrawerLayout.java

Source

/*
* Copyright (C) 2014 doubleTwist Corporation.
*
* 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.doubleTwist.drawerlib;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.HashMap;

public class ADrawerLayout extends ViewGroup {
    private static final String TAG = "ADrawerLayout";
    private final static boolean DEBUG = false;
    private final static boolean DEBUG_TOUCH = false;

    View mContent;
    View mContentDimmer;
    View mLeft;
    View mRight;
    View mBottom;
    View mTop;

    HashMap<View, Rect> mLayoutBounds = new HashMap<View, Rect>();
    HashMap<View, Rect> mLayoutBoundsWithoutPeek = new HashMap<View, Rect>();

    // If true, the layout will only intercept touches in the peek area
    //
    // NOTE: this overrides mRestrictTouchesToArea
    //
    boolean[] mRestrictTouchesToPeekArea;
    Rect mPeekSize;

    // For each drawer we can set a specific area that can be used
    // to drag the drawer
    //
    // Areas are relative to the initial drawer area and only apply
    // when the drawer is open
    //
    // NOTE: this is overriden by mRestrictTouchesToPeekArea
    //
    Rect[] mRestrictTouchesToArea = new Rect[4];

    // If true, innerMargins apply to all elements, otherwise, only the respective
    // sub-views (i.e. top or bottom only apply to the side views, left or right margins
    // only apply to the top or bottom views)
    //
    // NOTE: this is particularly useful when using the drawer in a action bar
    // 'overlay' themed activity
    //
    boolean[] mGlobalInnerMargins;
    Rect mInnerMargins;

    private int mTouchSlop;
    private VelocityTracker mVelocityTracker;

    public ADrawerLayout(Context ctx) {
        this(ctx, null, 0);
    }

    public ADrawerLayout(Context ctx, AttributeSet attrs) {
        this(ctx, attrs, 0);
    }

    public ADrawerLayout(Context ctx, AttributeSet attrs, int defStyle) {
        super(ctx, attrs, defStyle);

        ViewConfiguration vc = ViewConfiguration.get(ctx);
        mTouchSlop = vc.getScaledTouchSlop();
        mVelocityTracker = VelocityTracker.obtain();

        // Read the attributes passed in the XML view declaration
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.ADrawerLayout);
        try {
            Rect innerMargins = new Rect();
            innerMargins.left = a.getDimensionPixelSize(R.styleable.ADrawerLayout_innerMarginLeft, 0);
            innerMargins.top = a.getDimensionPixelSize(R.styleable.ADrawerLayout_innerMarginTop, 0);
            innerMargins.right = a.getDimensionPixelSize(R.styleable.ADrawerLayout_innerMarginRight, 0);
            innerMargins.bottom = a.getDimensionPixelSize(R.styleable.ADrawerLayout_innerMarginBottom, 0);
            setInnerMargins(innerMargins);

            boolean innerIsGlobalLeft = a.getBoolean(R.styleable.ADrawerLayout_innerMarginIsGlobalLeft, false);
            boolean innerIsGlobalTop = a.getBoolean(R.styleable.ADrawerLayout_innerMarginIsGlobalTop, false);
            boolean innerIsGlobalRight = a.getBoolean(R.styleable.ADrawerLayout_innerMarginIsGlobalRight, false);
            boolean innerIsGlobalBottom = a.getBoolean(R.styleable.ADrawerLayout_innerMarginIsGlobalBottom, false);
            setInnerIsGlobal(innerIsGlobalLeft, innerIsGlobalTop, innerIsGlobalRight, innerIsGlobalBottom);

            Rect peekSize = new Rect();
            peekSize.left = a.getDimensionPixelSize(R.styleable.ADrawerLayout_peekSizeLeft, 0);
            peekSize.top = a.getDimensionPixelSize(R.styleable.ADrawerLayout_peekSizeTop, 0);
            peekSize.right = a.getDimensionPixelSize(R.styleable.ADrawerLayout_peekSizeRight, 0);
            peekSize.bottom = a.getDimensionPixelSize(R.styleable.ADrawerLayout_peekSizeBottom, 0);
            setPeekSize(peekSize);

            boolean restrictToPeekLeft = a.getBoolean(R.styleable.ADrawerLayout_restrictToPeekLeft, true);
            boolean restrictToPeekTop = a.getBoolean(R.styleable.ADrawerLayout_restrictToPeekTop, true);
            boolean restrictToPeekRight = a.getBoolean(R.styleable.ADrawerLayout_restrictToPeekRight, true);
            boolean restrictToPeekBottom = a.getBoolean(R.styleable.ADrawerLayout_restrictToPeekBottom, true);
            setRestrictTouchesToPeekArea(restrictToPeekLeft, restrictToPeekTop, restrictToPeekRight,
                    restrictToPeekBottom);
        } finally {
            a.recycle();
        }
    }

    public interface DrawerLayoutListener {
        public void onBeginScroll(ADrawerLayout dl, DrawerState state);

        public void onOffsetChanged(ADrawerLayout dl, DrawerState state, float offsetXNorm, float offsetYNorm,
                int offsetX, int offsetY);

        public void onPreClose(ADrawerLayout dl, DrawerState state);

        public void onPreOpen(ADrawerLayout dl, DrawerState state);

        public void onClose(ADrawerLayout dl, DrawerState state, int closedDrawerId);

        public void onOpen(ADrawerLayout dl, DrawerState state);
    }

    private DrawerLayoutListener mListener;

    public void setListener(DrawerLayoutListener listener) {
        mListener = listener;
    }

    private boolean isRuntimePostGingerbread() {
        return Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1;
    }

    public void setInnerIsGlobal(boolean flag) {
        setInnerIsGlobal(flag, flag, flag, flag);
    }

    public void setInnerIsGlobal(boolean l, boolean t, boolean r, boolean b) {
        if (mGlobalInnerMargins == null)
            mGlobalInnerMargins = new boolean[4];

        mGlobalInnerMargins[0] = l;
        mGlobalInnerMargins[1] = t;
        mGlobalInnerMargins[2] = r;
        mGlobalInnerMargins[3] = b;
    }

    public void setRestrictTouchesToPeekArea(boolean flag) {
        setRestrictTouchesToPeekArea(flag, flag, flag, flag);
    }

    public void setRestrictTouchesToPeekArea(boolean l, boolean t, boolean r, boolean b) {
        if (mRestrictTouchesToPeekArea == null)
            mRestrictTouchesToPeekArea = new boolean[4];

        mRestrictTouchesToPeekArea[0] = l;
        mRestrictTouchesToPeekArea[1] = t;
        mRestrictTouchesToPeekArea[2] = r;
        mRestrictTouchesToPeekArea[3] = b;
    }

    public void setRestrictTouchesToArea(int drawerId, int dimension) {
        if (dimension < 0)
            throw new IllegalArgumentException("Touch area needs a positive value");

        Rect r = new Rect();
        switch (drawerId) {
        case LEFT_DRAWER:
            r.left = 0;
            r.right = dimension;
            r.top = Integer.MIN_VALUE;
            r.bottom = Integer.MAX_VALUE;
            if (dimension >= 0)
                mRestrictTouchesToArea[0] = r;
            else
                mRestrictTouchesToArea[0] = null;
            return;
        case TOP_DRAWER:
            r.left = Integer.MIN_VALUE;
            r.right = Integer.MAX_VALUE;
            r.top = 0;
            r.bottom = dimension;
            if (dimension >= 0)
                mRestrictTouchesToArea[1] = r;
            else
                mRestrictTouchesToArea[1] = null;
            return;
        case RIGHT_DRAWER:
            r.left = -dimension;
            r.right = 0;
            r.top = Integer.MIN_VALUE;
            r.bottom = Integer.MAX_VALUE;
            if (dimension >= 0)
                mRestrictTouchesToArea[2] = r;
            else
                mRestrictTouchesToArea[2] = null;
            return;
        case BOTTOM_DRAWER:
            r.left = Integer.MIN_VALUE;
            r.right = Integer.MAX_VALUE;
            r.top = -dimension;
            r.bottom = 0;
            if (dimension >= 0)
                mRestrictTouchesToArea[3] = r;
            else
                mRestrictTouchesToArea[3] = null;
            return;
        }
    }

    public void setRestrictTouchesToArea(Rect[] areas) {
        if (areas.length != 4)
            throw new IllegalArgumentException(
                    "setRestrictTouchesToArea(Rect[]) requires an " + "array with 4 elements.");

        mRestrictTouchesToArea = areas;
    }

    public void setInnerMargins(Rect r) {
        mInnerMargins = r;
        requestLayout();
    }

    public void setInnerMargins(int l, int t, int r, int b) {
        mInnerMargins.left = l;
        mInnerMargins.top = t;
        mInnerMargins.right = r;
        mInnerMargins.bottom = b;
        requestLayout();
    }

    public void setPeekSize(Rect r) {
        mPeekSize = r;
        requestLayout();
    }

    public void setPeekSize(int l, int t, int r, int b) {
        mPeekSize.left = l;
        mPeekSize.top = t;
        mPeekSize.right = r;
        mPeekSize.bottom = b;
        requestLayout();
    }

    @Override
    public void setPadding(int left, int top, int right, int bottom) {
        super.setPadding(left, top, right, bottom);
        requestLayout();
    }

    protected boolean mHasMeasured = false;

    protected boolean hasMeasured() {
        return mHasMeasured;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (DEBUG)
            Log.d(TAG, "onMeasure: " + getMeasuredWidth() + "x" + getMeasuredHeight());

        int w = getMeasuredWidth();
        int h = getMeasuredHeight();
        int contentWSpec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.getMode(widthMeasureSpec));
        int contentHSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.getMode(heightMeasureSpec));
        if (mContent != null)
            measureChild(mContent, contentWSpec, contentHSpec);
        if (mContentDimmer != null)
            measureChild(mContentDimmer, contentWSpec, contentHSpec);

        w = w - mInnerMargins.left - mInnerMargins.right;
        h = h - mInnerMargins.top - mInnerMargins.bottom;
        int drawerWInnerMarginSpec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.getMode(widthMeasureSpec));
        int drawerHInnerMarginSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.getMode(heightMeasureSpec));
        if (mLeft != null)
            measureChild(mLeft, !mGlobalInnerMargins[0] ? contentWSpec : drawerWInnerMarginSpec,
                    drawerHInnerMarginSpec);
        if (mRight != null)
            measureChild(mRight, !mGlobalInnerMargins[2] ? contentWSpec : drawerWInnerMarginSpec,
                    drawerHInnerMarginSpec);
        if (mTop != null)
            measureChild(mTop, drawerWInnerMarginSpec,
                    !mGlobalInnerMargins[1] ? contentHSpec : drawerHInnerMarginSpec);
        if (mBottom != null)
            measureChild(mBottom, drawerWInnerMarginSpec,
                    !mGlobalInnerMargins[3] ? contentHSpec : drawerHInnerMarginSpec);

        mHasMeasured = true;
    }

    @Override
    protected void measureChild(View v, int widthSpec, int heightSpec) {
        int wMode, hMode, wSpec, hSpec;
        int maxW = MeasureSpec.getSize(widthSpec);
        int maxH = MeasureSpec.getSize(heightSpec);
        LayoutParams params = (LayoutParams) v.getLayoutParams();

        if (DEBUG) {
            Log.d(TAG, " == VIEW == " + v.toString());
            Log.d(TAG, "params.width: " + params.width);
            Log.d(TAG, "params.height: " + params.height);
            Log.d(TAG, "maxW: " + maxW);
            Log.d(TAG, "maxH: " + maxH);
        }

        if (params.width == LayoutParams.WRAP_CONTENT) {
            wMode = MeasureSpec.AT_MOST;
            wSpec = MeasureSpec.makeMeasureSpec(maxW, wMode);
        } else if (params.width == LayoutParams.MATCH_PARENT) {
            wMode = MeasureSpec.EXACTLY;
            wSpec = MeasureSpec.makeMeasureSpec(maxW, wMode);
        } else {
            wMode = MeasureSpec.EXACTLY;
            wSpec = MeasureSpec.makeMeasureSpec(Math.min(maxW, params.width), wMode);
        }
        if (params.height == LayoutParams.WRAP_CONTENT) {
            hMode = MeasureSpec.AT_MOST;
            hSpec = MeasureSpec.makeMeasureSpec(maxH, hMode);
        } else if (params.height == LayoutParams.MATCH_PARENT) {
            hMode = MeasureSpec.EXACTLY;
            hSpec = MeasureSpec.makeMeasureSpec(maxH, hMode);
        } else {
            hMode = MeasureSpec.EXACTLY;
            hSpec = MeasureSpec.makeMeasureSpec(Math.min(maxH, params.height), hMode);
        }

        v.measure(wSpec, hSpec);

        if (DEBUG) {
            Log.d(TAG, " == VIEW == " + v.toString());
            Log.d(TAG, "mwidth: " + v.getMeasuredWidth());
            Log.d(TAG, "mheight: " + v.getMeasuredHeight());
        }
    }

    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (DEBUG)
            Log.d(TAG, "onLayout: " + l + "," + t + "," + r + "," + b);

        if (mPendingSavedState != null) {
            mPendingSavedState.run();
            mPendingSavedState = null;
        }

        int left, top, right, bottom;

        left = getPaddingLeft();
        right = r - l - getPaddingRight();
        top = getPaddingTop();
        bottom = b - t - getPaddingBottom();

        if (mContent != null)
            layoutView(mContent, left, top, right, bottom);
        if (mContentDimmer != null)
            layoutView(mContentDimmer, left, top, right, bottom);

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            boolean hasGlobalInnerMargins = false;
            int leftChild, topChild, rightChild, bottomChild;
            if (child == mLeft || child == mRight) {
                hasGlobalInnerMargins = child == mLeft ? mGlobalInnerMargins[0] : mGlobalInnerMargins[2];
                leftChild = left + (hasGlobalInnerMargins ? mInnerMargins.left : 0);
                rightChild = right - (hasGlobalInnerMargins ? mInnerMargins.right : 0);
                topChild = top + mInnerMargins.top;
                bottomChild = bottom - mInnerMargins.bottom;
            } else if (child == mTop || child == mBottom) {
                hasGlobalInnerMargins = child == mTop ? mGlobalInnerMargins[1] : mGlobalInnerMargins[3];
                leftChild = left + mInnerMargins.left;
                rightChild = right - mInnerMargins.right;
                topChild = top + (hasGlobalInnerMargins ? mInnerMargins.top : 0);
                bottomChild = bottom - (hasGlobalInnerMargins ? mInnerMargins.bottom : 0);
            } else {
                continue;
            }

            layoutView(child, leftChild, topChild, rightChild, bottomChild);
        }

        adjustScrollStates(mCurrentScrollX, mCurrentScrollY);

        for (Runnable run : mMeasurePendingRunnables) {
            run.run();
        }
        mMeasurePendingRunnables.clear();
    }

    protected void layoutView(View v, int l, int t, int r, int b) {
        LayoutParams params = (LayoutParams) v.getLayoutParams();

        Rect bounds = new Rect();
        Rect boundsWithoutPeek = new Rect();
        int gravity = params.gravity;
        switch (gravity) {
        case Gravity.RIGHT:
            if (DEBUG)
                Log.d(TAG, "gravity: right");
            bounds.left = r - v.getMeasuredWidth() - mPeekSize.right;
            bounds.top = t;
            bounds.right = r - mPeekSize.right;
            bounds.bottom = t + v.getMeasuredHeight();
            v.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
            boundsWithoutPeek = new Rect(bounds);
            boundsWithoutPeek.offset(mPeekSize.right, 0);
            mMinScrollX = -bounds.width();
            break;
        case Gravity.TOP:
            if (DEBUG)
                Log.d(TAG, "gravity: top");
            bounds.left = l;
            bounds.top = t + mPeekSize.top;
            bounds.right = v.getMeasuredWidth();
            bounds.bottom = t + v.getMeasuredHeight() + mPeekSize.top;
            v.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
            boundsWithoutPeek = new Rect(bounds);
            boundsWithoutPeek.offset(0, -mPeekSize.top);
            mMaxScrollY = bounds.height();
            break;
        case Gravity.BOTTOM:
            if (DEBUG)
                Log.d(TAG, "gravity: bottom");
            bounds.left = l;
            bounds.top = b - v.getMeasuredHeight() - mPeekSize.bottom;
            bounds.right = l + v.getMeasuredWidth();
            bounds.bottom = b - mPeekSize.bottom;
            v.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
            boundsWithoutPeek = new Rect(bounds);
            boundsWithoutPeek.offset(0, mPeekSize.bottom);
            mMinScrollY = -bounds.height();
            break;
        case Gravity.LEFT:
            if (DEBUG)
                Log.d(TAG, "gravity: left");
            bounds.left = l + mPeekSize.left;
            bounds.top = t;
            bounds.right = l + v.getMeasuredWidth() + mPeekSize.left;
            bounds.bottom = t + v.getMeasuredHeight();
            v.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
            mMaxScrollX = bounds.width();
            boundsWithoutPeek = new Rect(bounds);
            boundsWithoutPeek.offset(-mPeekSize.left, 0);
            break;
        default:
            if (DEBUG)
                Log.d(TAG, "gravity: default");
            bounds.left = l;
            bounds.top = t;
            bounds.right = l + v.getMeasuredWidth();
            bounds.bottom = t + v.getMeasuredHeight();
            v.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
            boundsWithoutPeek = new Rect(bounds);
            break;
        }

        if (DEBUG) {
            Log.d(TAG, " == VIEW LAYOUT == " + v.toString());
            Log.d(TAG, "bounds: " + bounds.left + "," + bounds.top + "," + bounds.right + "," + bounds.bottom);
        }

        if (mLayoutBounds.containsKey(v))
            mLayoutBounds.remove(v);
        mLayoutBounds.put(v, bounds);
        if (mLayoutBoundsWithoutPeek.containsKey(v))
            mLayoutBoundsWithoutPeek.remove(v);
        mLayoutBoundsWithoutPeek.put(v, boundsWithoutPeek);
    }

    public void setParalaxFactorX(float factor) {
        mContentParalaxFactorX = factor;
    }

    public void setParalaxFactorY(float factor) {
        mContentParalaxFactorY = factor;
    }

    public float getParalaxFactorX() {
        return mContentParalaxFactorX;
    }

    public float getParalaxFactorY() {
        return mContentParalaxFactorY;
    }

    // 1f means that the content moves 1 px for every px the drawer moves horizontally
    float mContentParalaxFactorX = 0.0f;
    // 1f means that the content moves 1 px for every px the drawer moves vertically
    float mContentParalaxFactorY = 0.0f;

    int dx, dy;
    int dxx, dyy;
    float animScale = 1.f;
    float animAlpha = 1.f;
    float animRotX = 0.f;
    float animRotY = 0.f;

    boolean[] mDimContent = new boolean[] { true, true, true, true };
    boolean[] mAnimateScrolling = new boolean[] { true, true, true, true };

    public void setDimContent(boolean flag) {
        setAnimateScrolling(flag, flag, flag, flag);
    }

    public void setDimContent(boolean left, boolean top, boolean right, boolean bottom) {
        setDimContent(LEFT_DRAWER, left);
        setDimContent(TOP_DRAWER, top);
        setDimContent(RIGHT_DRAWER, right);
        setDimContent(BOTTOM_DRAWER, bottom);
    }

    public void setDimContent(int drawer, boolean flag) {
        if (drawer == LEFT_DRAWER)
            mDimContent[0] = flag;
        else if (drawer == TOP_DRAWER)
            mDimContent[1] = flag;
        else if (drawer == RIGHT_DRAWER)
            mDimContent[2] = flag;
        else if (drawer == BOTTOM_DRAWER)
            mDimContent[3] = flag;
    }

    public void setAnimateScrolling(boolean flag) {
        setAnimateScrolling(flag, flag, flag, flag);
    }

    public void setAnimateScrolling(boolean left, boolean top, boolean right, boolean bottom) {
        setAnimateScrolling(LEFT_DRAWER, left);
        setAnimateScrolling(TOP_DRAWER, top);
        setAnimateScrolling(RIGHT_DRAWER, right);
        setAnimateScrolling(BOTTOM_DRAWER, bottom);
    }

    public void setAnimateScrolling(int drawer, boolean flag) {
        if (drawer == LEFT_DRAWER)
            mAnimateScrolling[0] = flag;
        else if (drawer == TOP_DRAWER)
            mAnimateScrolling[1] = flag;
        else if (drawer == RIGHT_DRAWER)
            mAnimateScrolling[2] = flag;
        else if (drawer == BOTTOM_DRAWER)
            mAnimateScrolling[3] = flag;
    }

    boolean dimLeft, dimTop, dimRight, dimBottom;
    boolean animateLeft, animateTop, animateRight, animateBottom;

    protected boolean isClosed() {
        return mDrawerState.mScrollState == IDLE && mDrawerState.mActiveDrawer == NO_DRAWER;
    }

    public static class AnimationParameters {
        public AnimationParameters(float scale, float alpha, float rotX, float rotY) {
            mScale = scale;
            mAlpha = alpha;
            mRotX = rotX;
            mRotY = rotY;
        }

        public float mScale = 0.f;
        public float mAlpha = 0.f;
        public float mRotX = 0.f;
        public float mRotY = 0.f;
    }

    AnimationParameters[] mAnimationParams = new AnimationParameters[] {
            new AnimationParameters(0.f, 0.f, 0.f, 0.f), new AnimationParameters(0.f, 0.f, 0.f, 0.f),
            new AnimationParameters(0.f, 0.f, 0.f, 0.f), new AnimationParameters(0.f, 0.f, 0.f, 0.f) };

    public AnimationParameters getAnimationParameters(int drawerId) {
        if (drawerId == ADrawerLayout.LEFT_DRAWER)
            return mAnimationParams[0];
        else if (drawerId == ADrawerLayout.TOP_DRAWER)
            return mAnimationParams[1];
        else if (drawerId == ADrawerLayout.RIGHT_DRAWER)
            return mAnimationParams[2];
        else if (drawerId == ADrawerLayout.BOTTOM_DRAWER)
            return mAnimationParams[3];
        return null;
    }

    public void setAnimationParameters(AnimationParameters[] params) {
        if (params.length != 4)
            throw new IllegalArgumentException();

        mAnimationParams = params;
    }

    public void setAnimationParameters(int drawerId, AnimationParameters params) {
        if (drawerId == ADrawerLayout.LEFT_DRAWER)
            mAnimationParams[0] = params;
        else if (drawerId == ADrawerLayout.TOP_DRAWER)
            mAnimationParams[1] = params;
        else if (drawerId == ADrawerLayout.RIGHT_DRAWER)
            mAnimationParams[2] = params;
        else if (drawerId == ADrawerLayout.BOTTOM_DRAWER)
            mAnimationParams[3] = params;
    }

    AnimationParameters aparams;
    AnimationParameters mNoAnimationParameters = new AnimationParameters(0f, 0f, 0f, 0f);

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void adjustScrollStates(float x, float y) {
        dx = Math.round(x);
        dy = Math.round(y);

        if (mContent != null) {
            dxx = Math.round(dx * mContentParalaxFactorX);
            dyy = Math.round(dy * mContentParalaxFactorY);
            setDXYCompat(mContent, dxx, dyy);

            animateLeft = mAnimateScrolling[0] && mDrawerState.mActiveDrawer == LEFT_DRAWER;
            animateTop = mAnimateScrolling[1] && mDrawerState.mActiveDrawer == TOP_DRAWER;
            animateRight = mAnimateScrolling[2] && mDrawerState.mActiveDrawer == RIGHT_DRAWER;
            animateBottom = mAnimateScrolling[3] && mDrawerState.mActiveDrawer == BOTTOM_DRAWER;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
                if (animateLeft)
                    aparams = mAnimationParams[0];
                else if (animateTop)
                    aparams = mAnimationParams[1];
                else if (animateRight)
                    aparams = mAnimationParams[2];
                else if (animateBottom)
                    aparams = mAnimationParams[3];
                else
                    aparams = mNoAnimationParameters;

                animScale = 1 - aparams.mScale * getScrollFraction();
                animAlpha = 1 - aparams.mAlpha * getScrollFraction();
                animRotX = -90.f * aparams.mRotX * getScrollFraction();
                animRotY = -90.f * aparams.mRotY * getScrollFraction();
                if (mContent.getScaleX() != animScale)
                    mContent.setScaleX(animScale);
                if (mContent.getScaleY() != animScale)
                    mContent.setScaleY(animScale);
                if (mContent.getAlpha() != animAlpha)
                    mContent.setAlpha(animAlpha);
                if (mContent.getRotationY() != animRotY)
                    mContent.setRotationY(animRotY);
                if (mContent.getRotationX() != animRotX)
                    mContent.setRotationX(animRotX);
            }
        }

        if (mContentDimmer != null) {
            dimLeft = mDimContent[0] && mDrawerState.mActiveDrawer == LEFT_DRAWER;
            dimTop = mDimContent[1] && mDrawerState.mActiveDrawer == TOP_DRAWER;
            dimRight = mDimContent[2] && mDrawerState.mActiveDrawer == RIGHT_DRAWER;
            dimBottom = mDimContent[3] && mDrawerState.mActiveDrawer == BOTTOM_DRAWER;

            // dxx = Math.round(dx);
            // if( mDrawerState.mActiveDrawer == LEFT_DRAWER ) dxx += mPeekSize.left;
            // if( mDrawerState.mActiveDrawer == RIGHT_DRAWER ) dxx -= mPeekSize.right;
            // dyy = Math.round(dy);
            // if( mDrawerState.mActiveDrawer == TOP_DRAWER ) dyy += mPeekSize.top;
            // if( mDrawerState.mActiveDrawer == BOTTOM_DRAWER ) dyy -= mPeekSize.bottom;

            if (isClosed() || dimLeft || dimTop || dimRight || dimBottom) {
                // dxx = Math.round(dx * mContentParalaxFactorX);
                // dyy = Math.round(dy * mContentParalaxFactorY);
                dxx = dx;
                dyy = dy;
                if (mPeekSize != null) {
                    if (dimLeft)
                        dxx += mPeekSize.left;
                    if (dimRight)
                        dxx -= mPeekSize.right;
                    if (dimTop)
                        dyy += mPeekSize.top;
                    if (dimBottom)
                        dyy -= mPeekSize.bottom;
                }
                setDXYCompat(mContentDimmer, dxx, dyy);

                alpha = getScrollFraction();
                alpha = Math.min(1.f, Math.max(0.f, alpha));
                mContentDimmer.setBackgroundColor(Color.argb(Math.round(alpha * Color.alpha(mDimmingColor)),
                        Color.red(mDimmingColor), Color.green(mDimmingColor), Color.blue(mDimmingColor)));
            }
        }

        if (mLeft != null) {
            dxx = -mLeft.getMeasuredWidth() + Math.max(0, dx);
            dyy = dy;
            setDXYCompat(mLeft, dxx, dyy);
        }

        if (mRight != null) {
            dxx = mRight.getMeasuredWidth() + Math.min(0, dx);
            dyy = dy;
            setDXYCompat(mRight, dxx, dyy);
        }

        if (mTop != null) {
            dxx = dx;
            dyy = -mTop.getMeasuredHeight() + Math.max(0, dy);
            setDXYCompat(mTop, dxx, dyy);
        }

        if (mBottom != null) {
            dxx = dx;
            dyy = mBottom.getMeasuredHeight() + Math.min(0, dy);
            setDXYCompat(mBottom, dxx, dyy);
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void setDXYCompat(View v, int dx, int dy) {
        if (isRuntimePostGingerbread()) {
            v.setTranslationX(dx);
            v.setTranslationY(dy);
        } else {
            Rect bounds = mLayoutBounds.get(v);
            v.layout(bounds.left + dx, bounds.top + dy, bounds.right + dx, bounds.bottom + dy);

            // This method does not work so well
            // v.setAnimation(null);
            // TranslateAnimation ta = new TranslateAnimation(dx, dx, dy, dy);
            // ta.setFillAfter(true);
            // ta.setFillEnabled(true);
            // mLeft.startAnimation(ta);
        }
    }

    int mDimmingColor = Color.argb(0xaa, 0x00, 0x00, 0x00);
    float alpha;

    public void setDimmingColor(int color) {
        mDimmingColor = color;
    }

    public int getDimmingColor() {
        return mDimmingColor;
    }

    @Override
    public void dispatchDraw(Canvas c) {
        super.dispatchDraw(c);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        if (DEBUG)
            Log.d(TAG, "onFinishInflate");

        readViews();
        addContentDimmer();
    }

    private int getMainContentIdx() {
        View v;
        for (int i = 0; i < getChildCount(); i++) {
            v = getChildAt(i);
            if (v == mContent)
                return i;
        }
        return -1;
    }

    OnTouchListener mDimmerTouchListener = new OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            return !(mDrawerState.mScrollState == IDLE && mDrawerState.mActiveDrawer == NO_DRAWER);
        }
    };

    private boolean mDimmerInterceptsTouches = true;

    private static int DIMMER_VIEW_ID = 0x78787878;

    private View addContentDimmer() {
        mContentDimmer = this.findViewById(DIMMER_VIEW_ID);
        if (mContentDimmer == null) {
            mContentDimmer = new View(getContext());
            mContentDimmer.setLayoutParams(
                    new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            mContentDimmer.setBackgroundColor(mDimmingColor);
            // int mainContentIdx = getMainContentIdx();
            // if( mainContentIdx >= 0 ) addView(mContentDimmer, mainContentIdx+1);
            // else addView(mContentDimmer);
            addView(mContentDimmer);
        }
        if (mDimmerInterceptsTouches)
            mContentDimmer.setOnTouchListener(mDimmerTouchListener);
        return mContentDimmer;
    }

    private void readViews() {
        mContent = getChildAt(0);
        for (int i = 1, count = getChildCount(); i < count; i++) {
            View v = getChildAt(i);
            if (v.getId() == DIMMER_VIEW_ID)
                continue;
            LayoutParams params = (LayoutParams) v.getLayoutParams();
            switch (params.gravity) {
            case Gravity.LEFT: // left
                mLeft = v;
                mDrawerState.mLeftEnabled = true;
                break;
            case Gravity.RIGHT: // right
                mRight = v;
                mDrawerState.mRightEnabled = true;
                break;
            case Gravity.TOP: // top
                mTop = v;
                mDrawerState.mTopEnabled = true;
                break;
            case Gravity.BOTTOM: // bottom
                mBottom = v;
                mDrawerState.mBottomEnabled = true;
                break;
            default:
                mLeft = v;
                break;
            }
        }
    }

    public void setScrollingEnabled(boolean left, boolean top, boolean right, boolean bottom) {
        setScrollingEnabled(LEFT_DRAWER, left);
        setScrollingEnabled(TOP_DRAWER, top);
        setScrollingEnabled(RIGHT_DRAWER, right);
        setScrollingEnabled(BOTTOM_DRAWER, bottom);
    }

    public boolean setScrollingEnabled(int section, boolean flag) {
        switch (section) {
        case LEFT_DRAWER:
            if (flag && mLeft == null)
                return false;
            mDrawerState.mLeftEnabled = flag;
            return true;
        case RIGHT_DRAWER:
            if (flag && mRight == null)
                return false;
            mDrawerState.mRightEnabled = flag;
            return true;
        case TOP_DRAWER:
            if (flag && mTop == null)
                return false;
            mDrawerState.mTopEnabled = flag;
            return true;
        case BOTTOM_DRAWER:
            if (flag && mBottom == null)
                return false;
            mDrawerState.mBottomEnabled = flag;
            return true;
        }
        return false;
    }

    private boolean isScrollingForSectionEnabled(int section) {
        switch (section) {
        case LEFT_DRAWER:
            return mDrawerState.mLeftEnabled;
        case RIGHT_DRAWER:
            return mDrawerState.mRightEnabled;
        case TOP_DRAWER:
            return mDrawerState.mTopEnabled;
        case BOTTOM_DRAWER:
            return mDrawerState.mBottomEnabled;
        }
        return false;
    }

    private static class HistoryEvent {
        public PointF mCoords = new PointF(0, 0);
        public int mPointerId = 0;
    }

    private HistoryEvent mDownEvent = new HistoryEvent();
    private HistoryEvent mLastMoveEvent = new HistoryEvent();
    private boolean mIsScrolling = false;
    private float mCurrentScrollX = 0;
    private float mCurrentScrollY = 0;
    private float mMaxScrollX = 0;
    private float mMinScrollX = 0;
    private float mMaxScrollY = 0;
    private float mMinScrollY = 0;

    public final static int IDLE = 0;
    public final static int SCROLLING = 1;
    public final static int SNAP_OPEN = 2;
    public final static int SNAP_CLOSE = 3;
    public final static int EXPLICIT_OPEN = 4;
    public final static int EXPLICIT_CLOSE = 5;

    public final static int NO_DRAWER = 0;
    public final static int LEFT_DRAWER = 1;
    public final static int RIGHT_DRAWER = 2;
    public final static int TOP_DRAWER = 3;
    public final static int BOTTOM_DRAWER = 4;

    public static class DrawerState {
        public int mScrollState = IDLE;
        public int mActiveDrawer = NO_DRAWER;

        boolean mLeftEnabled = false;
        boolean mRightEnabled = false;
        boolean mTopEnabled = false;
        boolean mBottomEnabled = false;

        boolean mDraggingEnabled = true;
    }

    private DrawerState mDrawerState = new DrawerState();

    public void setDraggingEnabled(boolean flag) {
        mDrawerState.mDraggingEnabled = flag;
    }

    private float getScrollFractionX() {
        boolean horizScrolling = mDrawerState.mActiveDrawer == LEFT_DRAWER
                || mDrawerState.mActiveDrawer == RIGHT_DRAWER;
        return horizScrolling ? getScrollFraction() : 0f;
    }

    private float getScrollFractionY() {
        boolean vertScrolling = mDrawerState.mActiveDrawer == TOP_DRAWER
                || mDrawerState.mActiveDrawer == BOTTOM_DRAWER;
        return vertScrolling ? getScrollFraction() : 0f;
    }

    private float getScrollFraction() {
        if (mDrawerState.mActiveDrawer == LEFT_DRAWER) {
            return mLeft == null ? 0f : mCurrentScrollX / (float) (mLeft.getMeasuredWidth() - mPeekSize.left);
        } else if (mDrawerState.mActiveDrawer == RIGHT_DRAWER) {
            return mRight == null ? 0f : -mCurrentScrollX / (float) (mRight.getMeasuredWidth() - mPeekSize.right);
        } else if (mDrawerState.mActiveDrawer == TOP_DRAWER) {
            return mTop == null ? 0f : mCurrentScrollY / (float) (mTop.getMeasuredHeight() - mPeekSize.top);
        } else if (mDrawerState.mActiveDrawer == BOTTOM_DRAWER) {
            return mBottom == null ? 0f
                    : -mCurrentScrollY / (float) (mBottom.getMeasuredHeight() - mPeekSize.bottom);
        } else {
            return 0.f;
        }
    }

    private float scrollFractionToSizeOffset(int drawer, float fraction) {
        if (drawer == LEFT_DRAWER)
            return mLeft != null ? fraction * (mLeft.getMeasuredWidth() - mPeekSize.left) : 0.f;
        else if (drawer == RIGHT_DRAWER)
            return mRight != null ? -fraction * (mRight.getMeasuredWidth() - mPeekSize.right) : 0.f;
        else if (drawer == TOP_DRAWER)
            return mTop != null ? fraction * (mTop.getMeasuredHeight() - mPeekSize.top) : 0.f;
        else if (drawer == BOTTOM_DRAWER)
            return mBottom != null ? -fraction * (mBottom.getMeasuredHeight() - mPeekSize.bottom) : 0.f;
        else
            return 0.f;
    }

    public void open(int drawer) {
        open(drawer, true);
    }

    ArrayList<Runnable> mMeasurePendingRunnables = new ArrayList<Runnable>();

    public void open(final int drawer, final boolean animate) {
        // Defer action to a runnable if the views haven't been measured yet
        if (!hasMeasured()) {
            mMeasurePendingRunnables.add(new Runnable() {
                @Override
                public void run() {
                    open(drawer, animate);
                }
            });
            return;
        }
        ;
        if (drawer == LEFT_DRAWER && mLeft != null) {
            mDrawerState.mActiveDrawer = LEFT_DRAWER;
            mTargetOffsetX = mLeft.getMeasuredWidth() - mPeekSize.left;
            mTargetOffsetY = 0;
        } else if (drawer == RIGHT_DRAWER && mRight != null) {
            mDrawerState.mActiveDrawer = RIGHT_DRAWER;
            mDrawerState.mScrollState = EXPLICIT_OPEN;
            mTargetOffsetX = -mRight.getMeasuredWidth() + mPeekSize.right;
            mTargetOffsetY = 0;
        } else if (drawer == TOP_DRAWER && mTop != null) {
            mDrawerState.mActiveDrawer = TOP_DRAWER;
            mDrawerState.mScrollState = EXPLICIT_OPEN;
            mTargetOffsetX = 0;
            mTargetOffsetY = mTop.getMeasuredHeight() - mPeekSize.top;
        } else if (drawer == BOTTOM_DRAWER && mBottom != null) {
            mDrawerState.mActiveDrawer = BOTTOM_DRAWER;
            mDrawerState.mScrollState = EXPLICIT_OPEN;
            mTargetOffsetX = 0;
            mTargetOffsetY = -mBottom.getMeasuredHeight() + mPeekSize.bottom;
        } else {
            return;
        }

        if (animate) {
            mDrawerState.mScrollState = EXPLICIT_OPEN;
        } else {
            mDrawerState.mScrollState = IDLE;
            mCurrentScrollY = mTargetOffsetY;
            mCurrentScrollX = mTargetOffsetX;
        }

        mLastT = System.currentTimeMillis();
        this.post(mStepRunnable);
    }

    public DrawerState getState() {
        return mDrawerState;
    }

    public void open() {
        open(LEFT_DRAWER, true);
    }

    public void close(final int drawer, final boolean animate) {
        boolean valid = false;
        valid = valid || drawer == LEFT_DRAWER && mLeft != null;
        valid = valid || drawer == RIGHT_DRAWER && mRight != null;
        valid = valid || drawer == TOP_DRAWER && mTop != null;
        valid = valid || drawer == BOTTOM_DRAWER && mBottom != null;
        if (!valid)
            return;

        // Defer action to a runnable if the views haven't been measured yet
        if (!hasMeasured()) {
            mMeasurePendingRunnables.add(new Runnable() {
                @Override
                public void run() {
                    close(drawer, animate);
                }
            });
            return;
        }
        ;

        mTargetOffsetX = 0;
        mTargetOffsetY = 0;

        if (animate) {
            mDrawerState.mActiveDrawer = drawer;
            mDrawerState.mScrollState = EXPLICIT_CLOSE;
        } else {
            mDrawerState.mActiveDrawer = NO_DRAWER;
            mDrawerState.mScrollState = IDLE;
            mCurrentScrollY = mTargetOffsetY;
            mCurrentScrollX = mTargetOffsetX;
        }

        mLastT = System.currentTimeMillis();
        this.post(mStepRunnable);
    }

    public void close(int drawer) {
        close(drawer, true);
    }

    public void close() {
        close(mDrawerState.mActiveDrawer, true);
    }

    private void snap() {
        mVelocityTracker.computeCurrentVelocity(1, 10);
        if (mDrawerState.mActiveDrawer == LEFT_DRAWER) {
            if (mVelocityTracker.getXVelocity() > 0) {
                mDrawerState.mScrollState = SNAP_OPEN;
                mTargetOffsetX = mLeft.getMeasuredWidth() - mPeekSize.left;
                if (mListener != null)
                    mListener.onPreOpen(this, mDrawerState);
            } else {
                mDrawerState.mScrollState = SNAP_CLOSE;
                mTargetOffsetX = 0;
                if (mListener != null)
                    mListener.onPreClose(this, mDrawerState);
            }
            mTargetOffsetY = 0;
        } else if (mDrawerState.mActiveDrawer == RIGHT_DRAWER) {
            if (mVelocityTracker.getXVelocity() < 0) {
                mDrawerState.mScrollState = SNAP_OPEN;
                mTargetOffsetX = -mRight.getMeasuredWidth() + mPeekSize.right;
                if (mListener != null)
                    mListener.onPreOpen(this, mDrawerState);
            } else {
                mDrawerState.mScrollState = SNAP_CLOSE;
                mTargetOffsetX = 0;
                if (mListener != null)
                    mListener.onPreClose(this, mDrawerState);
            }
            mTargetOffsetY = 0;
        } else if (mDrawerState.mActiveDrawer == TOP_DRAWER) {
            if (mVelocityTracker.getYVelocity() > 0) {
                mDrawerState.mScrollState = SNAP_OPEN;
                mTargetOffsetY = mTop.getMeasuredHeight() - mPeekSize.top;
                if (mListener != null)
                    mListener.onPreOpen(this, mDrawerState);
            } else {
                mDrawerState.mScrollState = SNAP_CLOSE;
                mTargetOffsetY = 0;
                if (mListener != null)
                    mListener.onPreClose(this, mDrawerState);
            }
            mTargetOffsetX = 0;
        } else if (mDrawerState.mActiveDrawer == BOTTOM_DRAWER) {
            if (mVelocityTracker.getYVelocity() < 0) {
                mDrawerState.mScrollState = SNAP_OPEN;
                mTargetOffsetY = -mBottom.getMeasuredHeight() + mPeekSize.bottom;
                if (mListener != null)
                    mListener.onPreOpen(this, mDrawerState);
            } else {
                mDrawerState.mScrollState = SNAP_CLOSE;
                mTargetOffsetY = 0;
                if (mListener != null)
                    mListener.onPreClose(this, mDrawerState);
            }
            mTargetOffsetX = 0;
        }
        mLastT = System.currentTimeMillis();
        this.post(mStepRunnable);
    }

    private Runnable mStepRunnable = new Runnable() {
        @Override
        public void run() {
            stepAnimation();
            adjustScrollStates(mCurrentScrollX, mCurrentScrollY);
        }
    };

    private float targetDiffX;
    private float targetDiffY;
    private float a = 13.33f;
    private float dt;
    private float adt;
    private double mLastT;
    private float mTargetOffsetX = 0;
    private float mTargetOffsetY = 0;

    private void stepAnimation() {
        dt = (float) (System.currentTimeMillis() - mLastT);
        if (dt < 1) {
            this.post(mStepRunnable);
            return;
        }
        dt = Math.min(dt, 50); /* If we get less than 20fps force the animation to go slower */
        dt *= .001f;
        adt = Math.min(1f, a * dt);
        mLastT = System.currentTimeMillis();

        targetDiffX = (mTargetOffsetX - mCurrentScrollX);
        targetDiffY = (mTargetOffsetY - mCurrentScrollY);
        if (Math.abs(targetDiffX) < 1)
            mCurrentScrollX = mTargetOffsetX;
        else
            mCurrentScrollX += targetDiffX > 0 ? Math.max(1, targetDiffX * adt) : Math.min(-1, targetDiffX * adt);
        if (Math.abs(targetDiffY) < 1)
            mCurrentScrollY = mTargetOffsetY;
        else
            mCurrentScrollY += targetDiffY > 0 ? Math.max(1, targetDiffY * adt) : Math.min(-1, targetDiffY * adt);

        if (mTargetOffsetX != mCurrentScrollX || mTargetOffsetY != mCurrentScrollY) {
            this.post(mStepRunnable);
            notifyOffset();
        } else {
            mDrawerState.mScrollState = IDLE;
            if (mCurrentScrollX == 0f && mCurrentScrollY == 0f) {
                notifyOffset();
                int closedDrawerId = mDrawerState.mActiveDrawer;
                mDrawerState.mActiveDrawer = NO_DRAWER;
                if (mListener != null)
                    mListener.onClose(this, mDrawerState, closedDrawerId);
            } else {
                notifyOffset();
                if (mListener != null)
                    mListener.onOpen(this, mDrawerState);
            }
        }

    }

    public boolean isOpen(int drawer) {
        boolean expandedScrollFraction = false;
        switch (drawer) {
        case BOTTOM_DRAWER:
            expandedScrollFraction = getScrollFractionY() == 1.f;
            break;
        case TOP_DRAWER:
            expandedScrollFraction = getScrollFractionY() == 1.f;
            break;
        case LEFT_DRAWER:
            expandedScrollFraction = getScrollFractionX() == 1.f;
            break;
        case RIGHT_DRAWER:
            expandedScrollFraction = getScrollFractionX() == 1.f;
            break;
        }
        boolean open = mDrawerState.mScrollState == IDLE && mDrawerState.mActiveDrawer == drawer;
        return open && expandedScrollFraction;
    }

    public boolean isClosed(int drawer) {
        return !isOpen(drawer);
    }

    public boolean isOpening(int drawer) {
        return isOpening(drawer, .66f);
    }

    public boolean isOpening(int drawer, float threshold) {
        if (mDrawerState.mActiveDrawer != drawer)
            return false;

        return isOpening(threshold);
    }

    private boolean isOpening(float threshold) {
        return mDrawerState.mScrollState == EXPLICIT_OPEN || mDrawerState.mScrollState == SNAP_OPEN
                || (mDrawerState.mScrollState == SCROLLING && getScrollFraction() > threshold);
    }

    public boolean isClosing(int drawer) {
        return isClosing(drawer, .33f);
    }

    public boolean isClosing(int drawer, float threshold) {
        if (mDrawerState.mActiveDrawer != drawer)
            return false;

        return isClosing(threshold);
    }

    public boolean isClosing(float threshold) {
        return mDrawerState.mScrollState == EXPLICIT_CLOSE || mDrawerState.mScrollState == SNAP_CLOSE
                || (mDrawerState.mScrollState == SCROLLING && getScrollFraction() < threshold);
    }

    private void notifyOffset() {
        notifyOffset(mListener);
    }

    private void notifyOffset(DrawerLayoutListener listener) {
        if (listener != null) {
            listener.onOffsetChanged(this, mDrawerState, getScrollFractionX(), getScrollFractionY(),
                    Math.round(mCurrentScrollX), Math.round(mCurrentScrollY));
        }
    }

    boolean mStartedTracking = false;

    private View getDrawerView(int drawerId) {
        switch (drawerId) {
        case NO_DRAWER:
            return mContent;
        case LEFT_DRAWER:
            return mLeft;
        case RIGHT_DRAWER:
            return mRight;
        case TOP_DRAWER:
            return mTop;
        case BOTTOM_DRAWER:
            return mBottom;
        }
        return null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // Here we actually handle the touch event (e.g. if the action is ACTION_MOVE,
        // scroll this container).
        // This method will only be called if the touch event was intercepted in
        // onInterceptTouchEvent

        if (DEBUG_TOUCH)
            Log.d(TAG, "onTouchEvent");

        if (!mDrawerState.mDraggingEnabled)
            return false;

        if (mVelocityTracker != null)
            mVelocityTracker.addMovement(ev);

        final int action = MotionEventCompat.getActionMasked(ev);

        if (!mIsScrolling) {
            if (DEBUG_TOUCH)
                Log.d(TAG, "NOT SCROLLING IS NOW SCROLLING");
            int drawer = preProcessScrollMotionEvent(ev);
            postProcessScrollMotionEvent(drawer, ev);
            if (action == MotionEvent.ACTION_UP) {
                if (mDrawerState.mScrollState == IDLE)
                    processIdleUp(ev);
            }
            return true; // needs to be true so we keep getting these values
        }

        if (DEBUG_TOUCH)
            Log.d(TAG, "DOING THE TOUCH STUFF");

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            mIsScrolling = false;
            mStartedTracking = false;
            snap();
            mVelocityTracker.clear();
        }

        if (action == MotionEvent.ACTION_MOVE) {
            int ptrIdx = ev.findPointerIndex(mDownEvent.mPointerId);
            if (DEBUG_TOUCH)
                Log.d(TAG, "ptrIdx: " + ptrIdx);
            if (ptrIdx >= ev.getPointerCount() || ptrIdx < 0)
                return true;

            if (mDrawerState.mActiveDrawer == LEFT_DRAWER || mDrawerState.mActiveDrawer == RIGHT_DRAWER) {
                mCurrentScrollX += mStartedTracking ? ev.getX(ptrIdx) - mLastMoveEvent.mCoords.x
                        : ev.getX(ptrIdx) - mDownEvent.mCoords.x;
                mCurrentScrollX = Math.max(mMinScrollX + mPeekSize.right, mCurrentScrollX);
                mCurrentScrollX = Math.min(mMaxScrollX - mPeekSize.left, mCurrentScrollX);
            } else if (mDrawerState.mActiveDrawer == TOP_DRAWER || mDrawerState.mActiveDrawer == BOTTOM_DRAWER) {
                mCurrentScrollY += mStartedTracking ? ev.getY(ptrIdx) - mLastMoveEvent.mCoords.y
                        : ev.getY(ptrIdx) - mDownEvent.mCoords.y;
                mCurrentScrollY = Math.max(mMinScrollY + mPeekSize.bottom, mCurrentScrollY);
                mCurrentScrollY = Math.min(mMaxScrollY - mPeekSize.top, mCurrentScrollY);
            }

            if (!mStartedTracking)
                mStartedTracking = true;
            mLastMoveEvent.mCoords.x = ev.getX(ptrIdx);
            mLastMoveEvent.mCoords.y = ev.getY(ptrIdx);

            // Log.d(TAG, "=======================");
            // Log.d(TAG, "mCurrentScrollX: "+mCurrentScrollX);
            // Log.d(TAG, "mCurrentScrollY: "+mCurrentScrollY);
            // Log.d(TAG, "mMinScrollY: "+mMinScrollY);
            // Log.d(TAG, "mMaxScrollY: "+mMaxScrollY);
            // Log.d(TAG, "mPeekSize.top: "+mPeekSize.top);
            // Log.d(TAG, "mPeekSize.bottom: "+mPeekSize.bottom);

            adjustScrollStates(mCurrentScrollX, mCurrentScrollY);
            notifyOffset();
        }

        return true;
    }

    private boolean mTouchOutsideToClose = true;

    private boolean processIdleUp(MotionEvent ev) {
        if (!mTouchOutsideToClose)
            return false;

        if (mDrawerState.mActiveDrawer == NO_DRAWER)
            return false;
        if (mDownEvent == null)
            return false;

        boolean inside = touchIsInDrawerArea(mDrawerState.mActiveDrawer, ev);
        inside &= inside
                && touchIsInDrawerArea(mDrawerState.mActiveDrawer, mDownEvent.mCoords.x, mDownEvent.mCoords.y);

        if (!inside)
            close(mDrawerState.mActiveDrawer);
        return !inside;
    }

    private boolean mRequestDisallowIntercept = false;

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        Log.d("HOBO", "requestedDisallowIntercept");

        mRequestDisallowIntercept = disallowIntercept;
        super.requestDisallowInterceptTouchEvent(disallowIntercept);

        // Do we need this?
        // super.requestDisallowInterceptTouchEvent(disallowIntercept);

        //        944         if (!mLeftDragger.isEdgeTouched(ViewDragHelper.EDGE_LEFT) &&
        //                945                 !mRightDragger.isEdgeTouched(ViewDragHelper.EDGE_RIGHT)) {
        //            946             // If we have an edge touch we want to skip this and track it for later instead.
        //            947             super.requestDisallowInterceptTouchEvent(disallowIntercept);
        //            948         }
        //        949         mDisallowInterceptRequested = disallowIntercept;
        //        950         if (disallowIntercept) {
        //            951             closeDrawers(true);
        //            952         }
        //        953     }
    }

    /*
     * This method JUST determines whether we want to intercept the motion.
     * If we return true, onTouchEvent will be called and we do the actual
     * scrolling there.
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (!mDrawerState.mDraggingEnabled)
            return false;

        final int action = MotionEventCompat.getActionMasked(ev);

        if (action == MotionEvent.ACTION_DOWN)
            mRequestDisallowIntercept = false;

        if (mRequestDisallowIntercept)
            return false;

        if (mVelocityTracker != null)
            mVelocityTracker.addMovement(ev);

        if (mDrawerState.mScrollState != IDLE)
            return true;

        // Always handle the case of the touch gesture being complete.
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            if (DEBUG_TOUCH)
                Log.d(TAG, "UP OR CANCEL: RELEASING SCROLL");
            mIsScrolling = false;
            mVelocityTracker.clear();
            if (action == MotionEvent.ACTION_UP) {
                processIdleUp(ev);
            }
            mRequestDisallowIntercept = false;
            return false; // Do not intercept touch event, let the child handle it
        }

        int drawer = preProcessScrollMotionEvent(ev);
        boolean intercept = drawer >= 0;
        boolean aDrawerIsOpen = mDrawerState.mScrollState == IDLE && mDrawerState.mActiveDrawer != NO_DRAWER;

        // Let touches go through if they are in the area of an open drawer
        if (!intercept && aDrawerIsOpen) {
            if (touchIsInDrawerArea(mDrawerState.mActiveDrawer, ev)) {
                if (DEBUG_TOUCH)
                    Log.d(TAG, "NOT INTERCEPTING BECAUSE A DRAWER IS OPEN");
                return false;
            }
        }

        postProcessScrollMotionEvent(drawer, ev);

        if (DEBUG_TOUCH)
            Log.d(TAG, "intercept/aDrawerIsOpen: " + intercept + "/" + aDrawerIsOpen);

        // Intercept events unless we are in the initial idle position
        return intercept || aDrawerIsOpen;
    }

    private boolean touchIsInDrawerArea(int drawerId, MotionEvent ev) {
        return touchIsInDrawerArea(drawerId, ev.getX(), ev.getY());
    }

    private boolean touchIsInDrawerArea(int drawerId, float x, float y) {
        Rect drawerBounds = null;
        switch (drawerId) {
        case LEFT_DRAWER:
            drawerBounds = mLayoutBoundsWithoutPeek.get(mLeft);
            break;
        case RIGHT_DRAWER:
            drawerBounds = mLayoutBoundsWithoutPeek.get(mRight);
            break;
        case TOP_DRAWER:
            drawerBounds = mLayoutBoundsWithoutPeek.get(mTop);
            break;
        case BOTTOM_DRAWER:
            drawerBounds = mLayoutBoundsWithoutPeek.get(mBottom);
            break;
        }

        return drawerBounds == null ? false : drawerBounds.contains((int) x, (int) y);
    }

    private boolean canScroll(int axis, float diff) {
        if (axis == MotionEvent.AXIS_X) {
            if (mDrawerState.mActiveDrawer == BOTTOM_DRAWER || mDrawerState.mActiveDrawer == TOP_DRAWER)
                return false;
            else if (diff < 0)
                return !(mDrawerState.mActiveDrawer == NO_DRAWER && !isScrollingForSectionEnabled(RIGHT_DRAWER));
            else
                return !(mDrawerState.mActiveDrawer == NO_DRAWER && !isScrollingForSectionEnabled(LEFT_DRAWER));
        } else if (axis == MotionEvent.AXIS_Y) {
            if (mDrawerState.mActiveDrawer == LEFT_DRAWER || mDrawerState.mActiveDrawer == RIGHT_DRAWER)
                return false;
            else if (diff < 0)
                return !(mDrawerState.mActiveDrawer == NO_DRAWER && !isScrollingForSectionEnabled(BOTTOM_DRAWER));
            else
                return !(mDrawerState.mActiveDrawer == NO_DRAWER && !isScrollingForSectionEnabled(TOP_DRAWER));
        } else {
            throw new IllegalArgumentException();
        }
    }

    private void postProcessScrollMotionEvent(int drawer, MotionEvent ev) {
        if (drawer >= 0 && mDrawerState.mScrollState == IDLE) {
            mIsScrolling = true;
            mDrawerState.mScrollState = SCROLLING;
            if (mDrawerState.mActiveDrawer == NO_DRAWER)
                mDrawerState.mActiveDrawer = drawer;
            if (mListener != null)
                mListener.onBeginScroll(this, mDrawerState);
        }
    }

    private int preProcessScrollMotionEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            if (DEBUG_TOUCH)
                Log.d(TAG, "DOWN: " + ev.getX(0) + "," + ev.getY(0));
            if (!mIsScrolling) {
                mVelocityTracker.clear();
                mDownEvent.mCoords.x = ev.getX(0);
                mDownEvent.mCoords.y = ev.getY(0);
                mDownEvent.mPointerId = ev.getPointerId(0);
                if (DEBUG_TOUCH)
                    Log.d(TAG, "SAVED: " + ev.getX(0) + "," + ev.getY(0));
            }
            break;
        case MotionEvent.ACTION_MOVE: {
            final int ptrIdx = ev.findPointerIndex(mDownEvent.mPointerId);
            if (ptrIdx < 0 || ptrIdx >= ev.getPointerCount())
                return -1;

            if (DEBUG_TOUCH)
                Log.d(TAG, "MOVE: " + ev.getX(ptrIdx) + "," + ev.getY(ptrIdx));
            if (mIsScrolling) {
                return mDrawerState.mActiveDrawer;
            }

            // If the user has dragged her finger horizontally more than
            // the touch slop, start the scroll

            final float xDiff = calculateDistanceX(ev, mDownEvent);
            final float yDiff = calculateDistanceY(ev, mDownEvent);
            final float dxAbs = Math.abs(xDiff);
            final float dyAbs = Math.abs(yDiff);

            if (DEBUG_TOUCH) {
                Log.d(TAG, "xDiff: " + xDiff + " -- " + ev.getX(ptrIdx) + " -- " + mDownEvent.mCoords.x);
                Log.d(TAG, "xDiff/yDiff/dxAbs/dyAbs: " + xDiff + "/" + yDiff + "/" + dxAbs + "/" + dyAbs);
                Log.d(TAG, "canScrollX/canScrollY: " + canScroll(MotionEvent.AXIS_X, xDiff) + "/"
                        + canScroll(MotionEvent.AXIS_Y, yDiff));
                Log.d(TAG, "activeDrawer/scrollState: " + mDrawerState.mActiveDrawer + "/"
                        + mDrawerState.mScrollState);
            }

            if (dxAbs > dyAbs && dxAbs > mTouchSlop && canScroll(MotionEvent.AXIS_X, xDiff)) {
                if (DEBUG_TOUCH)
                    Log.d(TAG, "X axis branch");
                int drawer;
                if (mDrawerState.mActiveDrawer == NO_DRAWER)
                    drawer = xDiff > 0 ? LEFT_DRAWER : RIGHT_DRAWER;
                else
                    drawer = mDrawerState.mActiveDrawer;
                if (!isInitialPositionValidForDrawer(mDownEvent, drawer))
                    drawer = -1;
                return drawer;
            }

            if (dyAbs > dxAbs && dyAbs > mTouchSlop && canScroll(MotionEvent.AXIS_Y, yDiff)) {
                if (DEBUG_TOUCH)
                    Log.d(TAG, "Y axis branch");
                int drawer;
                if (mDrawerState.mActiveDrawer == NO_DRAWER)
                    drawer = yDiff > 0 ? TOP_DRAWER : BOTTOM_DRAWER;
                else
                    drawer = mDrawerState.mActiveDrawer;

                if (!isInitialPositionValidForDrawer(mDownEvent, drawer))
                    drawer = -1;
                return drawer;
            }

            break;
        }
        }
        return -1;
    }

    private boolean isInitialPositionValidForDrawer(HistoryEvent ev, int drawer) {
        boolean restrictToPeekArea = false;
        int peekSize = 0;
        Rect restrictToArea = null;

        if (DEBUG_TOUCH)
            Log.d(TAG, "drawer/peekSize/evX/evY: " + drawer + "/" + peekSize + "/" + ev.mCoords.x + "/"
                    + ev.mCoords.y);

        switch (drawer) {
        case LEFT_DRAWER:
            restrictToPeekArea = mRestrictTouchesToPeekArea[0];
            peekSize = mPeekSize.left;
            restrictToArea = mRestrictTouchesToArea[0];
            break;
        case TOP_DRAWER:
            restrictToPeekArea = mRestrictTouchesToPeekArea[1];
            peekSize = mPeekSize.top;
            restrictToArea = mRestrictTouchesToArea[1];
            break;
        case RIGHT_DRAWER:
            restrictToPeekArea = mRestrictTouchesToPeekArea[2];
            peekSize = mPeekSize.right;
            restrictToArea = mRestrictTouchesToArea[2];
            break;
        case BOTTOM_DRAWER:
            restrictToPeekArea = mRestrictTouchesToPeekArea[3];
            peekSize = mPeekSize.bottom;
            restrictToArea = mRestrictTouchesToArea[3];
            break;
        case NO_DRAWER:
            return true;
        default:
            return false;
        }

        if (!restrictToPeekArea || peekSize == 0) {
            if (restrictToArea == null)
                return true;
            // restricted non-peek touch areas only apply when
            // all drawers are closed
            else if (mDrawerState.mActiveDrawer != NO_DRAWER) {
                return true;
            }
        }

        if (DEBUG_TOUCH)
            Log.d(TAG, "drawer/peekSize/evX/evY: " + drawer + "/" + peekSize + "/" + ev.mCoords.x + "/"
                    + ev.mCoords.y);
        if (DEBUG_TOUCH && restrictToArea != null) {
            Log.d(TAG, "restrictArea: " + restrictToArea.left + "/" + restrictToArea.top + "/"
                    + restrictToArea.right + "/" + restrictToArea.bottom);
            Log.d(TAG, "left: " + (ev.mCoords.x > restrictToArea.left));
            Log.d(TAG, "right: " + (ev.mCoords.x < restrictToArea.right));
            Log.d(TAG, "top: " + (ev.mCoords.y > restrictToArea.top));
            Log.d(TAG, "bottom: " + (ev.mCoords.y < restrictToArea.bottom));
            Log.d(TAG, "restrictToPeek: " + restrictToPeekArea);
            Log.d(TAG, "peekSize: " + peekSize);
        }

        switch (drawer) {
        case LEFT_DRAWER:
            if (restrictToPeekArea && peekSize != 0) {
                return ev.mCoords.x <= peekSize + mCurrentScrollX && ev.mCoords.x >= mCurrentScrollX;
            } else {
                return ev.mCoords.x >= restrictToArea.left && ev.mCoords.x <= restrictToArea.right
                        && ev.mCoords.y >= restrictToArea.top && ev.mCoords.y <= restrictToArea.bottom;
            }
        case TOP_DRAWER:
            if (restrictToPeekArea && peekSize != 0) {
                return ev.mCoords.y <= peekSize + mCurrentScrollY && ev.mCoords.y >= mCurrentScrollY;
            } else {
                return ev.mCoords.x >= restrictToArea.left && ev.mCoords.x <= restrictToArea.right
                        && ev.mCoords.y >= restrictToArea.top && ev.mCoords.y <= restrictToArea.bottom;
            }
        case RIGHT_DRAWER:
            if (restrictToPeekArea && peekSize != 0) {
                return ev.mCoords.x >= getWidth() - peekSize + mCurrentScrollX
                        && ev.mCoords.x <= getWidth() + mCurrentScrollX;
            } else {
                return ev.mCoords.x >= getWidth() + restrictToArea.left
                        && ev.mCoords.x <= getWidth() + restrictToArea.right && ev.mCoords.y >= restrictToArea.top
                        && ev.mCoords.y <= restrictToArea.bottom;
            }
        case BOTTOM_DRAWER:
            if (restrictToPeekArea && peekSize != 0) {
                return ev.mCoords.y >= getHeight() - peekSize + mCurrentScrollY
                        && ev.mCoords.y <= getHeight() + mCurrentScrollY;
            } else {
                return ev.mCoords.x >= restrictToArea.left && ev.mCoords.x <= restrictToArea.right
                        && ev.mCoords.y >= getHeight() + restrictToArea.top
                        && ev.mCoords.y <= getHeight() + restrictToArea.bottom;
            }
        }

        return false;
    }

    private float calculateDistanceX(MotionEvent ev, HistoryEvent down) {
        float x = ev.getX() - down.mCoords.x;
        return x;
    }

    private float calculateDistanceY(MotionEvent ev, HistoryEvent down) {
        float y = ev.getY() - down.mCoords.y;
        return y;
    }

    public static class LayoutParams extends LinearLayout.LayoutParams {

        public LayoutParams(int w, int h) {
            super(w, h);
        }

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

    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

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

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p.width, p.height);
    }

    protected Parcelable onSaveInstanceState() {
        Parcelable p = super.onSaveInstanceState();
        SavedState ss = new SavedState(p);
        ss.currentScrollFractionX = getScrollFractionX();
        ss.currentScrollFractionY = getScrollFractionY();
        ss.activeDrawer = mDrawerState.mActiveDrawer;
        ss.scrollState = mDrawerState.mScrollState;
        ss.draggingEnabled = mDrawerState.mDraggingEnabled;
        return ss;
    }

    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        // Avoid restoring state if the drawer was still being dragged
        // i.e. round the scroll states and set the right active drawer
        if (ss.scrollState != IDLE) {
            if (ss.currentScrollFractionX != Float.MIN_VALUE)
                ss.currentScrollFractionX = Math.round(ss.currentScrollFractionX);
            if (ss.currentScrollFractionY != Float.MIN_VALUE)
                ss.currentScrollFractionY = Math.round(ss.currentScrollFractionY);
            ss.scrollState = IDLE;
            // Need to set the right activeDrawer
            if (ss.currentScrollFractionX == 0 && ss.currentScrollFractionY == 0)
                ss.activeDrawer = NO_DRAWER;
        }

        if (ss.scrollState >= 0)
            mDrawerState.mScrollState = ss.scrollState;
        if (ss.activeDrawer >= 0)
            mDrawerState.mActiveDrawer = ss.activeDrawer;
        // Do not restore this, use setDraggingEnabled explicitly from activity/fragment code
        // mDrawerState.mDraggingEnabled = ss.draggingEnabled;

        boolean doNotRestore = (mDrawerState.mActiveDrawer == LEFT_DRAWER && mLeft == null)
                || (mDrawerState.mActiveDrawer == RIGHT_DRAWER && mRight == null)
                || (mDrawerState.mActiveDrawer == TOP_DRAWER && mTop == null)
                || (mDrawerState.mActiveDrawer == BOTTOM_DRAWER && mBottom == null);
        if (doNotRestore) {
            mDrawerState.mActiveDrawer = NO_DRAWER;
            mDrawerState.mScrollState = IDLE;
        }

        // Restoring the drawer offset will only occur in the next layout pass.
        // We need the actual drawer dimensions in order to properly set the
        // the offset.
        mPendingSavedState = new RestoreStateRunnable(ss);
    }

    private RestoreStateRunnable mPendingSavedState;

    private class RestoreStateRunnable implements Runnable {
        SavedState mSavedState;

        RestoreStateRunnable(SavedState ss) {
            mSavedState = ss;
        }

        @Override
        public void run() {
            if (DEBUG)
                Log.d(TAG, "running Pending Saved State");

            SavedState ss = mSavedState;
            if (ss.currentScrollFractionX != Float.MIN_VALUE) {
                mCurrentScrollX = scrollFractionToSizeOffset(ss.activeDrawer, ss.currentScrollFractionX);
            }
            if (ss.currentScrollFractionY != Float.MIN_VALUE) {
                mCurrentScrollY = scrollFractionToSizeOffset(ss.activeDrawer, ss.currentScrollFractionY);
            }

            notifyOffset();
        }
    }

    static class SavedState extends BaseSavedState {
        float currentScrollFractionX = Float.MIN_VALUE;
        float currentScrollFractionY = Float.MIN_VALUE;
        int scrollState = -1;
        int activeDrawer = -1;
        boolean draggingEnabled = true;

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

        private SavedState(Parcel in) {
            super(in);
            this.currentScrollFractionX = in.readFloat();
            this.currentScrollFractionY = in.readFloat();
            this.scrollState = in.readInt();
            this.activeDrawer = in.readInt();
            boolean[] bArray = new boolean[1];
            in.readBooleanArray(bArray);
            this.draggingEnabled = bArray[0];
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeFloat(this.currentScrollFractionX);
            out.writeFloat(this.currentScrollFractionY);
            out.writeInt(this.scrollState);
            out.writeInt(this.activeDrawer);
            out.writeBooleanArray(new boolean[] { this.draggingEnabled });
        }

        //required field that makes Parcelables from a Parcel
        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

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