First commit for FoldingLayout.

Change-Id: I3eaca79e3c7a0c6d2e8f5c6de5a3d4c3db4bf5a5
(cherry picked from commit 425e318026b116d94b4dcae1f1a72342bf396486)
diff --git a/samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml b/samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml
new file mode 100644
index 0000000..13758d7
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.android.foldinglayout"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-sdk android:minSdkVersion="17"
+              android:targetSdkVersion="18"/>
+    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+        <activity android:name=".FoldingLayoutActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/image.jpg b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/image.jpg
new file mode 100644
index 0000000..60ce9f2
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/image.jpg
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/layout/activity_fold.xml b/samples/devbytes/graphics/FoldingLayout/res/layout/activity_fold.xml
new file mode 100644
index 0000000..9ed3bc5
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/layout/activity_fold.xml
@@ -0,0 +1,41 @@
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <com.example.android.foldinglayout.FoldingLayout
+        android:layout_weight="1"
+        android:id="@+id/fold_view"
+        android:layout_width="match_parent"
+        android:layout_height="0dp">
+
+        <ImageView
+                android:id="@+id/image_view"
+                android:layout_height="match_parent"
+                android:layout_width="match_parent"
+                android:scaleType="fitXY"/>
+
+        </com.example.android.foldinglayout.FoldingLayout>
+
+    <SeekBar
+        android:id="@+id/anchor_seek_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:max="100"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/layout/spinner.xml b/samples/devbytes/graphics/FoldingLayout/res/layout/spinner.xml
new file mode 100644
index 0000000..c35133d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/layout/spinner.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+-->
+<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:entries="@array/num_of_folds_array" />
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml b/samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml
new file mode 100644
index 0000000..a0231bd
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml
@@ -0,0 +1,41 @@
+<!-- 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+        android:id="@+id/toggle_orientation"
+        android:showAsAction="never"
+        android:title="@string/vertical"/>
+
+    <item
+        android:id="@+id/num_of_folds"
+        android:showAsAction="ifRoom"
+        android:actionLayout="@layout/spinner"/>
+
+    <item
+        android:title="@string/sepia_effect_off"
+        android:id="@+id/sepia"
+        android:checkable="true"/>
+
+    <item
+        android:title="@string/camera_feed"
+        android:id="@+id/camera_feed"/>
+
+    <item
+        android:id="@+id/animate_fold"
+        android:showAsAction="never"
+        android:title="@string/animate"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/menu/fold_with_bug.xml b/samples/devbytes/graphics/FoldingLayout/res/menu/fold_with_bug.xml
new file mode 100644
index 0000000..44631ee
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/menu/fold_with_bug.xml
@@ -0,0 +1,32 @@
+<!-- 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+            android:id="@+id/toggle_orientation"
+            android:showAsAction="never"
+            android:title="@string/vertical"/>
+
+    <item
+            android:id="@+id/num_of_folds"
+            android:showAsAction="ifRoom"
+            android:actionLayout="@layout/spinner"/>
+
+    <item
+            android:id="@+id/animate_fold"
+            android:showAsAction="never"
+            android:title="@string/animate"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/values/strings.xml b/samples/devbytes/graphics/FoldingLayout/res/values/strings.xml
new file mode 100644
index 0000000..181b15d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/values/strings.xml
@@ -0,0 +1,42 @@
+<!-- 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.
+-->
+<resources>
+
+    <string name="app_name">FoldingLayout</string>
+    <string name="title_activity_fold">FoldActivity</string>
+
+    <string name="horizontal">Horizontal</string>
+    <string name="vertical">Vertical</string>
+    <string name="num_of_folds">Number Of Folds</string>
+
+    <string name="animate">Animate</string>
+
+    <string name="camera_feed">Camera Feed</string>
+    <string name="static_image">Static Image</string>
+
+    <string name="sepia_effect_off">Sepia Off</string>
+
+    <string-array name="num_of_folds_array">
+        <item>2</item>
+        <item>3</item>
+        <item>4</item>
+        <item>5</item>
+        <item>6</item>
+        <item>7</item>
+        <item>8</item>
+        <item>1</item>
+    </string-array>
+
+</resources>
diff --git a/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayout.java b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayout.java
new file mode 100644
index 0000000..8afb27e
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayout.java
@@ -0,0 +1,545 @@
+/*
+ * 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.example.android.foldinglayout;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * The folding layout where the number of folds, the anchor point and the
+ * orientation of the fold can be specified. Each of these parameters can
+ * be modified individually and updates and resets the fold to a default
+ * (unfolded) state. The fold factor varies between 0 (completely unfolded
+ * flat image) to 1.0 (completely folded, non-visible image).
+ *
+ * This layout throws an exception if there is more than one child added to the view.
+ * For more complicated view hierarchy's inside the folding layout, the views should all
+ * be nested inside 1 parent layout.
+ *
+ * This layout folds the contents of its child in real time. By applying matrix
+ * transformations when drawing to canvas, the contents of the child may change as
+ * the fold takes place. It is important to note that there are jagged edges about
+ * the perimeter of the layout as a result of applying transformations to a rectangle.
+ * This can be avoided by having the child of this layout wrap its content inside a
+ * 1 pixel transparent border. This will cause an anti-aliasing like effect and smoothen
+ * out the edges.
+ *
+ */
+public class FoldingLayout extends ViewGroup {
+
+    public static enum Orientation {
+        VERTICAL,
+        HORIZONTAL
+    }
+
+    private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at " +
+            "most";
+
+    private final float SHADING_ALPHA = 0.8f;
+    private final float SHADING_FACTOR = 0.5f;
+    private final int DEPTH_CONSTANT = 1500;
+    private final int NUM_OF_POLY_POINTS = 8;
+
+    private Rect[] mFoldRectArray;
+
+    private Matrix [] mMatrix;
+
+    private Orientation mOrientation = Orientation.HORIZONTAL;
+
+    private float mAnchorFactor = 0;
+    private float mFoldFactor = 0;
+
+    private int mNumberOfFolds = 2;
+
+    private boolean mIsHorizontal = true;
+
+    private int mOriginalWidth = 0;
+    private int mOriginalHeight = 0;
+
+    private float mFoldMaxWidth = 0;
+    private float mFoldMaxHeight = 0;
+    private float mFoldDrawWidth = 0;
+    private float mFoldDrawHeight = 0;
+
+    private boolean mIsFoldPrepared = false;
+    private boolean mShouldDraw = true;
+
+    private Paint mSolidShadow;
+    private Paint mGradientShadow;
+    private LinearGradient mShadowLinearGradient;
+    private Matrix mShadowGradientMatrix;
+
+    private float [] mSrc;
+    private float [] mDst;
+
+    private OnFoldListener mFoldListener;
+
+    private float mPreviousFoldFactor = 0;
+
+    private Bitmap mFullBitmap;
+    private Rect mDstRect;
+
+    public FoldingLayout(Context context) {
+        super(context);
+    }
+
+    public FoldingLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FoldingLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected boolean addViewInLayout(View child, int index, LayoutParams params,
+                                      boolean preventRequestLayout) {
+        throwCustomException(getChildCount());
+        boolean returnValue = super.addViewInLayout(child, index, params, preventRequestLayout);
+        return returnValue;
+    }
+
+    @Override
+    public void addView(View child, int index, LayoutParams params) {
+        throwCustomException(getChildCount());
+        super.addView(child, index, params);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        View child = getChildAt(0);
+        measureChild(child,widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        View child = getChildAt(0);
+        child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
+        updateFold();
+    }
+
+    /**
+     * The custom exception to be thrown so as to limit the number of views in this
+     * layout to at most one.
+     */
+    private class NumberOfFoldingLayoutChildrenException extends RuntimeException {
+        public NumberOfFoldingLayoutChildrenException(String message) {
+            super(message);
+        }
+    }
+
+    /** Throws an exception if the number of views added to this layout exceeds one.*/
+    private void throwCustomException (int numOfChildViews) {
+        if (numOfChildViews == 1) {
+            throw new NumberOfFoldingLayoutChildrenException(FOLDING_VIEW_EXCEPTION_MESSAGE);
+        }
+    }
+
+    public void setFoldListener(OnFoldListener foldListener) {
+        mFoldListener = foldListener;
+    }
+
+    /**
+     * Sets the fold factor of the folding view and updates all the corresponding
+     * matrices and values to account for the new fold factor. Once that is complete,
+     * it redraws itself with the new fold. */
+    public void setFoldFactor(float foldFactor) {
+        if (foldFactor != mFoldFactor) {
+            mFoldFactor = foldFactor;
+            calculateMatrices();
+            invalidate();
+        }
+    }
+
+    public void setOrientation(Orientation orientation) {
+        if (orientation != mOrientation) {
+            mOrientation = orientation;
+            updateFold();
+        }
+    }
+
+    public void setAnchorFactor(float anchorFactor) {
+        if (anchorFactor != mAnchorFactor) {
+            mAnchorFactor = anchorFactor;
+            updateFold();
+        }
+    }
+
+    public void setNumberOfFolds(int numberOfFolds) {
+        if (numberOfFolds != mNumberOfFolds) {
+            mNumberOfFolds = numberOfFolds;
+            updateFold();
+        }
+    }
+
+    public float getAnchorFactor() {
+        return mAnchorFactor;
+    }
+
+    public Orientation getOrientation() {
+        return mOrientation;
+    }
+
+    public float getFoldFactor() {
+        return mFoldFactor;
+    }
+
+    public int getNumberOfFolds() {
+        return mNumberOfFolds;
+    }
+
+    private void updateFold() {
+        prepareFold(mOrientation, mAnchorFactor, mNumberOfFolds);
+        calculateMatrices();
+        invalidate();
+    }
+
+    /**
+     * This method is called in order to update the fold's orientation, anchor
+     * point and number of folds. This creates the necessary setup in order to
+     * prepare the layout for a fold with the specified parameters. Some of the
+     * dimensions required for the folding transformation are also acquired here.
+     *
+     * After this method is called, it will be in a completely unfolded state by default.
+     */
+    private void prepareFold(Orientation orientation, float anchorFactor, int numberOfFolds) {
+
+        mSrc = new float[NUM_OF_POLY_POINTS];
+        mDst = new float[NUM_OF_POLY_POINTS];
+
+        mDstRect = new Rect();
+
+        mFoldFactor = 0;
+        mPreviousFoldFactor = 0;
+
+        mIsFoldPrepared = false;
+
+        mSolidShadow = new Paint();
+        mGradientShadow = new Paint();
+
+        mOrientation = orientation;
+        mIsHorizontal = (orientation == Orientation.HORIZONTAL);
+
+        if (mIsHorizontal) {
+            mShadowLinearGradient = new LinearGradient(0, 0, SHADING_FACTOR, 0, Color.BLACK,
+                    Color.TRANSPARENT, TileMode.CLAMP);
+        } else {
+            mShadowLinearGradient = new LinearGradient(0, 0, 0, SHADING_FACTOR, Color.BLACK,
+                    Color.TRANSPARENT, TileMode.CLAMP);
+        }
+
+        mGradientShadow.setStyle(Style.FILL);
+        mGradientShadow.setShader(mShadowLinearGradient);
+        mShadowGradientMatrix = new Matrix();
+
+        mAnchorFactor = anchorFactor;
+        mNumberOfFolds = numberOfFolds;
+
+        mOriginalWidth = getMeasuredWidth();
+        mOriginalHeight = getMeasuredHeight();
+
+        mFoldRectArray = new Rect[mNumberOfFolds];
+        mMatrix = new Matrix [mNumberOfFolds];
+
+        for (int x = 0; x < mNumberOfFolds; x++) {
+            mMatrix[x] = new Matrix();
+        }
+
+        int h = mOriginalHeight;
+        int w = mOriginalWidth;
+
+        if (FoldingLayoutActivity.IS_JBMR2) {
+            mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(mFullBitmap);
+            getChildAt(0).draw(canvas);
+        }
+
+        int delta = Math.round(mIsHorizontal ? ((float) w) / ((float) mNumberOfFolds) :
+                ((float) h) /((float) mNumberOfFolds));
+
+        /* Loops through the number of folds and segments the full layout into a number
+         * of smaller equal components. If the number of folds is odd, then one of the
+         * components will be smaller than all the rest. Note that deltap below handles
+         * the calculation for an odd number of folds.*/
+        for (int x = 0; x < mNumberOfFolds; x++) {
+            if (mIsHorizontal) {
+                int deltap = (x + 1) * delta > w ? w - x * delta : delta;
+                mFoldRectArray[x] = new Rect(x * delta, 0, x * delta + deltap, h);
+            } else {
+                int deltap = (x + 1) * delta > h ? h - x * delta : delta;
+                mFoldRectArray[x] = new Rect(0, x * delta, w, x * delta + deltap);
+            }
+        }
+
+        if (mIsHorizontal) {
+            mFoldMaxHeight = h;
+            mFoldMaxWidth = delta;
+        } else {
+            mFoldMaxHeight = delta;
+            mFoldMaxWidth = w;
+        }
+
+        mIsFoldPrepared = true;
+    }
+
+    /*
+    * Calculates the transformation matrices used to draw each of the separate folding
+    * segments from this view.
+    */
+    private void calculateMatrices() {
+
+        mShouldDraw = true;
+
+        if (!mIsFoldPrepared) {
+            return;
+        }
+
+        /** If the fold factor is 1 than the folding view should not be seen
+         * and the canvas can be left completely empty. */
+        if (mFoldFactor == 1) {
+            mShouldDraw = false;
+            return;
+        }
+
+        if (mFoldFactor == 0 &&  mPreviousFoldFactor > 0) {
+            mFoldListener.onEndFold();
+        }
+
+        if (mPreviousFoldFactor == 0 && mFoldFactor > 0) {
+            mFoldListener.onStartFold();
+        }
+
+        mPreviousFoldFactor = mFoldFactor;
+
+        /* Reset all the transformation matrices back to identity before computing
+         * the new transformation */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+            mMatrix[x].reset();
+        }
+
+        float cTranslationFactor = 1 - mFoldFactor;
+
+        float translatedDistance = mIsHorizontal ? mOriginalWidth * cTranslationFactor :
+                mOriginalHeight * cTranslationFactor;
+
+        float translatedDistancePerFold = Math.round(translatedDistance / mNumberOfFolds);
+
+        /* For an odd number of folds, the rounding error may cause the
+         * translatedDistancePerFold to be grater than the max fold width or height. */
+        mFoldDrawWidth = mFoldMaxWidth < translatedDistancePerFold ?
+                translatedDistancePerFold : mFoldMaxWidth;
+        mFoldDrawHeight = mFoldMaxHeight < translatedDistancePerFold ?
+                translatedDistancePerFold : mFoldMaxHeight;
+
+        float translatedDistanceFoldSquared = translatedDistancePerFold * translatedDistancePerFold;
+
+        /* Calculate the depth of the fold into the screen using pythagorean theorem. */
+        float depth = mIsHorizontal ?
+                (float)Math.sqrt((double)(mFoldDrawWidth * mFoldDrawWidth -
+                        translatedDistanceFoldSquared)) :
+                (float)Math.sqrt((double)(mFoldDrawHeight * mFoldDrawHeight -
+                        translatedDistanceFoldSquared));
+
+        /* The size of some object is always inversely proportional to the distance
+        *  it is away from the viewpoint. The constant can be varied to to affect the
+        *  amount of perspective. */
+        float scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + depth);
+
+        float scaledWidth, scaledHeight, bottomScaledPoint, topScaledPoint, rightScaledPoint,
+                leftScaledPoint;
+
+        if (mIsHorizontal) {
+            scaledWidth = mFoldDrawWidth * cTranslationFactor;
+            scaledHeight = mFoldDrawHeight * scaleFactor;
+        } else {
+            scaledWidth = mFoldDrawWidth * scaleFactor;
+            scaledHeight = mFoldDrawHeight * cTranslationFactor;
+        }
+
+        topScaledPoint = (mFoldDrawHeight - scaledHeight) / 2.0f;
+        bottomScaledPoint = topScaledPoint + scaledHeight;
+
+        leftScaledPoint = (mFoldDrawWidth - scaledWidth) / 2.0f;
+        rightScaledPoint = leftScaledPoint + scaledWidth;
+
+        float anchorPoint = mIsHorizontal ? mAnchorFactor * mOriginalWidth :
+                mAnchorFactor * mOriginalHeight;
+
+        /* The fold along which the anchor point is located. */
+        float midFold = mIsHorizontal ? (anchorPoint / mFoldDrawWidth) : anchorPoint /
+                mFoldDrawHeight;
+
+        mSrc[0] = 0;
+        mSrc[1] = 0;
+        mSrc[2] = 0;
+        mSrc[3] = mFoldDrawHeight;
+        mSrc[4] = mFoldDrawWidth;
+        mSrc[5] = 0;
+        mSrc[6] = mFoldDrawWidth;
+        mSrc[7] = mFoldDrawHeight;
+
+        /* Computes the transformation matrix for each fold using the values calculated above. */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+
+            boolean isEven = (x % 2 == 0);
+
+            if (mIsHorizontal) {
+                mDst[0] = (anchorPoint > x * mFoldDrawWidth) ? anchorPoint + (x - midFold) *
+                        scaledWidth : anchorPoint - (midFold - x) * scaledWidth;
+                mDst[1] = isEven ? 0 : topScaledPoint;
+                mDst[2] = mDst[0];
+                mDst[3] = isEven ? mFoldDrawHeight: bottomScaledPoint;
+                mDst[4] = (anchorPoint > (x + 1) * mFoldDrawWidth) ? anchorPoint + (x + 1 - midFold)
+                        * scaledWidth : anchorPoint - (midFold - x - 1) * scaledWidth;
+                mDst[5] = isEven ? topScaledPoint : 0;
+                mDst[6] = mDst[4];
+                mDst[7] = isEven ? bottomScaledPoint : mFoldDrawHeight;
+
+            } else {
+                mDst[0] = isEven ? 0 : leftScaledPoint;
+                mDst[1] = (anchorPoint > x * mFoldDrawHeight) ? anchorPoint + (x - midFold) *
+                        scaledHeight : anchorPoint - (midFold - x) * scaledHeight;
+                mDst[2] = isEven ? leftScaledPoint: 0;
+                mDst[3] = (anchorPoint > (x + 1) * mFoldDrawHeight) ? anchorPoint + (x + 1 -
+                        midFold) * scaledHeight : anchorPoint - (midFold - x - 1) * scaledHeight;
+                mDst[4] = isEven ? mFoldDrawWidth : rightScaledPoint;
+                mDst[5] = mDst[1];
+                mDst[6] = isEven ? rightScaledPoint : mFoldDrawWidth;
+                mDst[7] = mDst[3];
+            }
+
+            /* Pixel fractions are present for odd number of folds which need to be
+             * rounded off here.*/
+            for (int y = 0; y < 8; y ++) {
+                mDst[y] = Math.round(mDst[y]);
+            }
+
+            /* If it so happens that any of the folds have reached a point where
+            *  the width or height of that fold is 0, then nothing needs to be
+            *  drawn onto the canvas because the view is essentially completely
+            *  folded.*/
+            if (mIsHorizontal) {
+                if (mDst[4] <= mDst[0] || mDst[6] <= mDst[2]) {
+                    mShouldDraw = false;
+                    return;
+                }
+            } else {
+                if (mDst[3] <= mDst[1] || mDst[7] <= mDst[5]) {
+                    mShouldDraw = false;
+                    return;
+                }
+            }
+
+            /* Sets the shadow and bitmap transformation matrices.*/
+            mMatrix[x].setPolyToPoly(mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2);
+        }
+        /* The shadows on the folds are split into two parts: Solid shadows and gradients.
+         * Every other fold has a solid shadow which overlays the whole fold. Similarly,
+         * the folds in between these alternating folds also have an overlaying shadow.
+         * However, it is a gradient that takes up part of the fold as opposed to a solid
+         * shadow overlaying the whole fold.*/
+
+        /* Solid shadow paint object. */
+        int alpha = (int) (mFoldFactor * 255 * SHADING_ALPHA);
+
+        mSolidShadow.setColor(Color.argb(alpha, 0, 0, 0));
+
+        if (mIsHorizontal) {
+            mShadowGradientMatrix.setScale(mFoldDrawWidth, 1);
+            mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
+        } else {
+            mShadowGradientMatrix.setScale(1, mFoldDrawHeight);
+            mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
+        }
+
+        mGradientShadow.setAlpha(alpha);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        /** If prepareFold has not been called or if preparation has not completed yet,
+         * then no custom drawing will take place so only need to invoke super's
+         * onDraw and return. */
+        if (!mIsFoldPrepared || mFoldFactor == 0) {
+            super.dispatchDraw(canvas);
+            return;
+        }
+
+        if (!mShouldDraw) {
+            return;
+        }
+
+        Rect src;
+         /* Draws the bitmaps and shadows on the canvas with the appropriate transformations. */
+        for (int x = 0; x < mNumberOfFolds; x++) {
+
+            src = mFoldRectArray[x];
+            /* The canvas is saved and restored for every individual fold*/
+            canvas.save();
+
+            /* Concatenates the canvas with the transformation matrix for the
+             *  the segment of the view corresponding to the actual image being
+             *  displayed. */
+            canvas.concat(mMatrix[x]);
+            if (FoldingLayoutActivity.IS_JBMR2) {
+                mDstRect.set(0, 0, src.width(), src.height());
+                canvas.drawBitmap(mFullBitmap, src, mDstRect, null);
+            } else {
+                /* The same transformation matrix is used for both the shadow and the image
+                 * segment. The canvas is clipped to account for the size of each fold and
+                 * is translated so they are drawn in the right place. The shadow is then drawn on
+                 * top of the different folds using the sametransformation matrix.*/
+                canvas.clipRect(0, 0, src.right - src.left, src.bottom - src.top);
+
+                if (mIsHorizontal) {
+                    canvas.translate(-src.left, 0);
+                } else {
+                    canvas.translate(0, -src.top);
+                }
+
+                super.dispatchDraw(canvas);
+
+                if (mIsHorizontal) {
+                    canvas.translate(src.left, 0);
+                } else {
+                    canvas.translate(0, src.top);
+                }
+            }
+            /* Draws the shadows corresponding to this specific fold. */
+            if (x % 2 == 0) {
+                canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mSolidShadow);
+            } else {
+                canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mGradientShadow);
+            }
+
+            canvas.restore();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayoutActivity.java b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayoutActivity.java
new file mode 100644
index 0000000..1a1033d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayoutActivity.java
@@ -0,0 +1,429 @@
+/*
+ * 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.example.android.foldinglayout;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.GestureDetector;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+
+import com.example.android.foldinglayout.FoldingLayout.Orientation;
+
+import java.io.IOException;
+
+/**
+ * This application creates  a paper like folding effect of some view.
+ * The number of folds, orientation (vertical or horizontal) of the fold, and the
+ * anchor point about which the view will fold can be set to achieve different
+ * folding effects.
+ *
+ * Using bitmap and canvas scaling techniques, the foldingLayout can be scaled so as
+ * to depict a paper-like folding effect. The addition of shadows on the separate folds
+ * adds a sense of realism to the visual effect.
+ *
+ * This application shows folding of a TextureView containing a live camera feed,
+ * as well as the folding of an ImageView with a static image. The TextureView experiences
+ * jagged edges as a result of scaling operations on rectangles. The ImageView however
+ * contains a 1 pixel transparent border around its contents which can be used to avoid
+ * this unwanted artifact.
+ */
+public class FoldingLayoutActivity extends Activity {
+
+    private final int ANTIALIAS_PADDING = 1;
+
+    private final int FOLD_ANIMATION_DURATION = 1000;
+
+    /* A bug was introduced in Android 4.3 that ignores changes to the Canvas state
+     * between multiple calls to super.dispatchDraw() when running with hardware acceleration.
+     * To account for this bug, a slightly different approach was taken to fold a
+     * static image whereby a bitmap of the original contents is captured and drawn
+     * in segments onto the canvas. However, this method does not permit the folding
+     * of a TextureView hosting a live camera feed which continuously updates.
+     * Furthermore, the sepia effect was removed from the bitmap variation of the
+     * demo to simplify the logic when running with this workaround."
+     */
+    static final boolean IS_JBMR2 = Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2;
+
+    private FoldingLayout mFoldLayout;
+    private SeekBar mAnchorSeekBar;
+    private Orientation mOrientation = Orientation.HORIZONTAL;
+
+    private int mTranslation = 0;
+    private int mNumberOfFolds = 2;
+    private int mParentPositionY = -1;
+    private int mTouchSlop = -1;
+
+    private float mAnchorFactor = 0;
+
+    private boolean mDidLoadSpinner = true;
+    private boolean mDidNotStartScroll = true;
+
+    private boolean mIsCameraFeed = false;
+    private boolean mIsSepiaOn = true;
+
+    private GestureDetector mScrollGestureDetector;
+    private ItemSelectedListener mItemSelectedListener;
+
+    private Camera mCamera;
+    private TextureView mTextureView;
+    private ImageView mImageView;
+
+    private Paint mSepiaPaint;
+    private Paint mDefaultPaint;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_fold);
+
+        mImageView = (ImageView)findViewById(R.id.image_view);
+        mImageView.setPadding(ANTIALIAS_PADDING, ANTIALIAS_PADDING, ANTIALIAS_PADDING,
+                ANTIALIAS_PADDING);
+        mImageView.setScaleType(ImageView.ScaleType.FIT_XY);
+        mImageView.setImageDrawable(getResources().getDrawable(R.drawable.image));
+
+        mTextureView = new TextureView(this);
+        mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+        mAnchorSeekBar = (SeekBar)findViewById(R.id.anchor_seek_bar);
+        mFoldLayout = (FoldingLayout)findViewById(R.id.fold_view);
+        mFoldLayout.setBackgroundColor(Color.BLACK);
+        mFoldLayout.setFoldListener(mOnFoldListener);
+
+        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+
+        mAnchorSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
+
+        mScrollGestureDetector = new GestureDetector(this, new ScrollGestureDetector());
+        mItemSelectedListener = new ItemSelectedListener();
+
+        mDefaultPaint = new Paint();
+        mSepiaPaint = new Paint();
+
+        ColorMatrix m1 = new ColorMatrix();
+        ColorMatrix m2 = new ColorMatrix();
+        m1.setSaturation(0);
+        m2.setScale(1f, .95f, .82f, 1.0f);
+        m1.setConcat(m2, m1);
+        mSepiaPaint.setColorFilter(new ColorMatrixColorFilter(m1));
+    }
+
+    /**
+     * This listener, along with the setSepiaLayer method below, show a possible use case
+     * of the OnFoldListener provided with the FoldingLayout. This is a fun extra addition
+     * to the demo showing what kind of visual effects can be applied to the child of the
+     * FoldingLayout by setting the layer type to hardware. With a hardware layer type
+     * applied to the child, a paint object can also be applied to the same layer. Using
+     * the concatenation of two different color matrices (above), a color filter was created
+     * which simulates a sepia effect on the layer.*/
+    private OnFoldListener mOnFoldListener =
+            new OnFoldListener() {
+        @Override
+        public void onStartFold() {
+            if (mIsSepiaOn) {
+                setSepiaLayer(mFoldLayout.getChildAt(0), true);
+            }
+        }
+
+        @Override
+        public void onEndFold() {
+            setSepiaLayer(mFoldLayout.getChildAt(0), false);
+        }
+    };
+
+    private void setSepiaLayer (View view, boolean isSepiaLayerOn) {
+        if (!IS_JBMR2) {
+            if (isSepiaLayerOn) {
+                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                view.setLayerPaint(mSepiaPaint);
+            } else {
+                view.setLayerPaint(mDefaultPaint);
+            }
+        }
+    }
+
+    /**
+     * Creates a SurfaceTextureListener in order to prepare a TextureView
+     * which displays a live, and continuously updated, feed from the Camera.
+     */
+    private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView
+            .SurfaceTextureListener() {
+        @Override
+        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
+            mCamera = Camera.open();
+
+            if (mCamera == null && Camera.getNumberOfCameras() > 1) {
+                mCamera = mCamera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
+            }
+
+            if (mCamera == null) {
+                return;
+            }
+
+            try {
+                mCamera.setPreviewTexture(surfaceTexture);
+                mCamera.setDisplayOrientation(90);
+                mCamera.startPreview();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        @Override
+        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {
+            // Ignored, Camera does all the work for us
+        }
+
+        @Override
+        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+            if (mCamera != null) {
+                mCamera.stopPreview();
+                mCamera.release();
+            }
+            return true;
+        }
+
+        @Override
+        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+            // Invoked every time there's a new Camera preview frame
+        }
+    };
+
+    /**
+     * A listener for scrolling changes in the seekbar. The anchor point of the folding
+     * view is updated every time the seekbar stops tracking touch events. Every time the
+     * anchor point is updated, the folding view is restored to a default unfolded state.
+     */
+    private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar
+            .OnSeekBarChangeListener() {
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            mTranslation = 0;
+            mAnchorFactor = ((float)mAnchorSeekBar.getProgress())/100.0f;
+            mFoldLayout.setAnchorFactor(mAnchorFactor);
+        }
+    };
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        if (IS_JBMR2) {
+            getMenuInflater().inflate(R.menu.fold_with_bug, menu);
+        } else {
+            getMenuInflater().inflate(R.menu.fold, menu);
+        }
+        Spinner s = (Spinner) menu.findItem(R.id.num_of_folds).getActionView();
+        s.setOnItemSelectedListener(mItemSelectedListener);
+        return true;
+    }
+
+    @Override
+    public void onWindowFocusChanged (boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+
+        int[] loc = new int[2];
+        mFoldLayout.getLocationOnScreen(loc);
+        mParentPositionY = loc[1];
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        return mScrollGestureDetector.onTouchEvent(me);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected (MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.animate_fold:
+                animateFold();
+                break;
+            case R.id.toggle_orientation:
+                mOrientation = (mOrientation == Orientation.HORIZONTAL) ? Orientation.VERTICAL :
+                        Orientation.HORIZONTAL;
+                item.setTitle((mOrientation == Orientation.HORIZONTAL) ? R.string.vertical :
+                R.string.horizontal);
+                mTranslation = 0;
+                mFoldLayout.setOrientation(mOrientation);
+                break;
+            case R.id.camera_feed:
+                mIsCameraFeed = !mIsCameraFeed;
+                item.setTitle(mIsCameraFeed ? R.string.static_image : R.string.camera_feed);
+                item.setChecked(mIsCameraFeed);
+                if (mIsCameraFeed) {
+                    mFoldLayout.removeView(mImageView);
+                    mFoldLayout.addView(mTextureView, new ViewGroup.LayoutParams(
+                            mFoldLayout.getWidth(), mFoldLayout.getHeight()));
+                } else {
+                    mFoldLayout.removeView(mTextureView);
+                    mFoldLayout.addView(mImageView, new ViewGroup.LayoutParams(
+                            mFoldLayout.getWidth(), mFoldLayout.getHeight()));
+                }
+                mTranslation = 0;
+                break;
+            case R.id.sepia:
+                mIsSepiaOn = !mIsSepiaOn;
+                item.setChecked(!mIsSepiaOn);
+                if (mIsSepiaOn && mFoldLayout.getFoldFactor() != 0) {
+                    setSepiaLayer(mFoldLayout.getChildAt(0), true);
+                } else {
+                    setSepiaLayer(mFoldLayout.getChildAt(0), false);
+                }
+                break;
+            default:
+               break;
+
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Animates the folding view inwards (to a completely folded state) from its
+     * current state and then back out to its original state.
+     */
+    public void animateFold ()
+    {
+        float foldFactor = mFoldLayout.getFoldFactor();
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(mFoldLayout, "foldFactor", foldFactor, 1);
+        animator.setRepeatMode(ValueAnimator.REVERSE);
+        animator.setRepeatCount(1);
+        animator.setDuration(FOLD_ANIMATION_DURATION);
+        animator.setInterpolator(new AccelerateInterpolator());
+        animator.start();
+    }
+
+    /**
+     * Listens for selection events of the spinner located on the action bar. Every
+     * time a new value is selected, the number of folds in the folding view is updated
+     * and is also restored to a default unfolded state.
+     */
+    private class ItemSelectedListener implements OnItemSelectedListener {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+            mNumberOfFolds = Integer.parseInt(parent.getItemAtPosition(pos).toString());
+            if (mDidLoadSpinner) {
+                mDidLoadSpinner = false;
+            } else {
+                mTranslation = 0;
+                mFoldLayout.setNumberOfFolds(mNumberOfFolds);
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> arg0) {
+        }
+    }
+
+    /** This class uses user touch events to fold and unfold the folding view. */
+    private class ScrollGestureDetector extends GestureDetector.SimpleOnGestureListener {
+        @Override
+        public boolean onDown (MotionEvent e) {
+            mDidNotStartScroll = true;
+            return true;
+        }
+
+        /**
+         * All the logic here is used to determine by what factor the paper view should
+         * be folded in response to the user's touch events. The logic here uses vertical
+         * scrolling to fold a vertically oriented view and horizontal scrolling to fold
+         * a horizontally oriented fold. Depending on where the anchor point of the fold is,
+         * movements towards or away from the anchor point will either fold or unfold
+         * the paper respectively.
+         *
+         * The translation logic here also accounts for the touch slop when a new user touch
+         * begins, but before a scroll event is first invoked.
+         */
+        @Override
+        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+            int touchSlop = 0;
+            float factor;
+            if (mOrientation == Orientation.VERTICAL) {
+                factor = Math.abs((float)(mTranslation) / (float)(mFoldLayout.getHeight()));
+
+                if (e2.getY() - mParentPositionY <= mFoldLayout.getHeight()
+                        && e2.getY() - mParentPositionY >= 0) {
+                    if ((e2.getY() - mParentPositionY) > mFoldLayout.getHeight() * mAnchorFactor) {
+                        mTranslation -= (int)distanceY;
+                        touchSlop = distanceY < 0 ? -mTouchSlop : mTouchSlop;
+                    } else {
+                        mTranslation += (int)distanceY;
+                        touchSlop = distanceY < 0 ? mTouchSlop : -mTouchSlop;
+                    }
+                    mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
+
+                    if (mTranslation < -mFoldLayout.getHeight()) {
+                        mTranslation = -mFoldLayout.getHeight();
+                    }
+                }
+            } else {
+                factor = Math.abs(((float)mTranslation) / ((float) mFoldLayout.getWidth()));
+
+                if (e2.getRawX() > mFoldLayout.getWidth() * mAnchorFactor) {
+                    mTranslation -= (int)distanceX;
+                    touchSlop = distanceX < 0 ? -mTouchSlop : mTouchSlop;
+                } else {
+                    mTranslation += (int)distanceX;
+                    touchSlop = distanceX < 0 ? mTouchSlop : -mTouchSlop;
+                }
+                mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
+
+                if (mTranslation < -mFoldLayout.getWidth()) {
+                    mTranslation = -mFoldLayout.getWidth();
+                }
+            }
+
+            mDidNotStartScroll = false;
+
+            if (mTranslation > 0) {
+                mTranslation = 0;
+            }
+
+            mFoldLayout.setFoldFactor(factor);
+
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/OnFoldListener.java b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/OnFoldListener.java
new file mode 100644
index 0000000..a305568
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/OnFoldListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.example.android.foldinglayout;
+
+/**
+ * This interface listens for when the folding layout begins folding (enters
+ * a folded state from a completely unfolded state), or ends folding (enters a
+ * completely unfolded state from a folded state).
+ */
+public interface OnFoldListener {
+    public void onStartFold();
+    public void onEndFold();
+}