First commit for ListViewCellInsertion.
diff --git a/samples/devbytes/animation/ListViewCellInsertion/AndroidManifest.xml b/samples/devbytes/animation/ListViewCellInsertion/AndroidManifest.xml
new file mode 100644
index 0000000..230c2e7
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.android.insertingcells"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-sdk android:minSdkVersion="14"
+              android:targetSdkVersion="17"/>
+    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+        <activity android:name=".InsertingCells"
+                  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/animation/ListViewCellInsertion/res/drawable-hdpi/border.9.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/border.9.png
new file mode 100644
index 0000000..f76e008
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/border.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/chameleon.jpg b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/chameleon.jpg
new file mode 100644
index 0000000..686cc88
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/chameleon.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/flower.jpg b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/flower.jpg
new file mode 100644
index 0000000..8842483
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/flower.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/rock.jpg b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/rock.jpg
new file mode 100644
index 0000000..8ea0e85
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/rock.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/layout/activity_main.xml b/samples/devbytes/animation/ListViewCellInsertion/res/layout/activity_main.xml
new file mode 100644
index 0000000..e515309
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/layout/activity_main.xml
@@ -0,0 +1,53 @@
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:id="@+id/relative_layout"
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="vertical"
+                tools:context=".MainActivity"
+                android:background="#111">
+
+    <LinearLayout
+            android:id="@+id/linear_layout"
+            android:layout_height="@dimen/cell_height"
+            android:layout_width="match_parent"
+            android:orientation="horizontal">
+
+        <com.example.android.insertingcells.RoundView
+                android:id="@+id/round_view"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+
+        <Button
+                android:id="@+id/add_row_button"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:text="@string/add_row"
+                android:onClick="addRow"
+                android:layout_weight="2"/>
+
+    </LinearLayout>
+
+    <com.example.android.insertingcells.InsertionListView
+            android:id="@+id/listview"
+            android:layout_below="@id/linear_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+</RelativeLayout>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/layout/list_view_item.xml b/samples/devbytes/animation/ListViewCellInsertion/res/layout/list_view_item.xml
new file mode 100644
index 0000000..e984b2e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/layout/list_view_item.xml
@@ -0,0 +1,40 @@
+<!-- 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"
+              android:id="@+id/item_linear_layout"
+              android:layout_height="match_parent"
+              android:layout_width="match_parent"
+              android:orientation="horizontal"
+              android:background="@drawable/border">
+
+    <ImageView
+            android:id="@+id/image_view"
+            android:layout_height="match_parent"
+            android:layout_width="0dp"
+            android:gravity="center_vertical"
+            android:layout_weight="1"
+            android:scaleType="center"/>
+
+    <TextView
+            android:id="@+id/text_view"
+            android:layout_height="fill_parent"
+            android:layout_width="0dp"
+            android:gravity="center"
+            android:layout_weight="2"
+            android:textStyle="bold"
+            android:textSize="22sp"
+            android:textColor="#ffffff"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/values/dimensions.xml b/samples/devbytes/animation/ListViewCellInsertion/res/values/dimensions.xml
new file mode 100644
index 0000000..a18add3
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/values/dimensions.xml
@@ -0,0 +1,21 @@
+<!-- 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>
+
+    <dimen name="circle_radius">50dp</dimen>
+    <dimen name="cell_height">150dp</dimen>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/values/strings.xml b/samples/devbytes/animation/ListViewCellInsertion/res/values/strings.xml
new file mode 100644
index 0000000..cb57d69
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/values/strings.xml
@@ -0,0 +1,21 @@
+<!-- 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">InsertingCells</string>
+    <string name="add_row">Add Row</string>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/CustomArrayAdapter.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/CustomArrayAdapter.java
new file mode 100644
index 0000000..870d279
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/CustomArrayAdapter.java
@@ -0,0 +1,138 @@
+/*
+ * 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.insertingcells;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This custom array adapter is used to populate the ListView in this application.
+ * This adapter also maintains a map of unique stable ids for each object in the data set.
+ * Since this adapter has to support the addition of a new cell to the 1ist index, it also
+ * provides a mechanism to add a stable ID for new data that was recently inserted.
+ */
+public class CustomArrayAdapter extends ArrayAdapter<ListItemObject> {
+
+    HashMap<ListItemObject, Integer> mIdMap = new HashMap<ListItemObject, Integer>();
+    List<ListItemObject> mData;
+    Context mContext;
+    int mLayoutViewResourceId;
+    int mCounter;
+
+    public CustomArrayAdapter(Context context, int layoutViewResourceId,
+                              List <ListItemObject> data) {
+        super(context, layoutViewResourceId, data);
+        mData = data;
+        mContext = context;
+        mLayoutViewResourceId = layoutViewResourceId;
+        updateStableIds();
+    }
+
+    public long getItemId(int position) {
+        ListItemObject item = getItem(position);
+        if (mIdMap.containsKey(item)) {
+            return mIdMap.get(item);
+        }
+        return -1;
+    }
+
+    public void updateStableIds() {
+        mIdMap.clear();
+        mCounter = 0;
+        for (int i = 0; i < mData.size(); ++i) {
+            mIdMap.put(mData.get(i), mCounter++);
+        }
+    }
+
+    public void addStableIdForDataAtPosition(int position) {
+        mIdMap.put(mData.get(position), ++mCounter);
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+
+        ListItemObject obj = mData.get(position);
+
+        if(convertView == null) {
+            LayoutInflater inflater = ((Activity)mContext).getLayoutInflater();
+            convertView = inflater.inflate(mLayoutViewResourceId, parent, false);
+        }
+
+        convertView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams
+                .MATCH_PARENT, obj.getHeight()));
+
+        ImageView imgView = (ImageView)convertView.findViewById(R.id.image_view);
+        TextView textView = (TextView)convertView.findViewById(R.id.text_view);
+
+        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
+                obj.getImgResource(), null);
+
+        textView.setText(obj.getTitle());
+        imgView.setImageBitmap(CustomArrayAdapter.getCroppedBitmap(bitmap));
+
+        return convertView;
+    }
+
+    /**
+     * Returns a circular cropped version of the bitmap passed in.
+     */
+    public static Bitmap getCroppedBitmap(Bitmap bitmap) {
+        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
+                Config.ARGB_8888);
+
+        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+        Canvas canvas = new Canvas(output);
+
+        final Paint paint = new Paint();
+        paint.setAntiAlias(true);
+
+        int halfWidth = bitmap.getWidth() / 2;
+        int halfHeight = bitmap.getHeight() / 2;
+
+        canvas.drawCircle(halfWidth, halfHeight, Math.max(halfWidth, halfHeight), paint);
+
+        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+
+        canvas.drawBitmap(bitmap, rect, rect, paint);
+
+        return output;
+    }
+}
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertingCells.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertingCells.java
new file mode 100644
index 0000000..a58dbfb
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertingCells.java
@@ -0,0 +1,113 @@
+/*
+ * 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.insertingcells;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This application creates a ListView to which new elements can be added from the
+ * top. When a new element is added, it is animated from above the bounds
+ * of the list to the top. When the list is scrolled all the way to the top and a new
+ * element is added, the row animation is accompanied by an image animation that pops
+ * out of the round view and pops into the correct position in the top cell.
+ */
+public class InsertingCells extends Activity implements OnRowAdditionAnimationListener {
+
+    private ListItemObject mValues[];
+
+    private InsertionListView mListView;
+
+    private Button mButton;
+
+    private Integer mItemNum = 0;
+
+    private RoundView mRoundView;
+
+    private int mCellHeight;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        mValues = new ListItemObject[] {
+                new ListItemObject("Chameleon", R.drawable.chameleon, 0),
+                new ListItemObject("Rock", R.drawable.rock, 0),
+                new ListItemObject("Flower", R.drawable.flower, 0),
+        };
+
+        mCellHeight = (int)(getResources().getDimension(R.dimen.cell_height));
+
+        List<ListItemObject> mData = new ArrayList<ListItemObject>();
+        CustomArrayAdapter mAdapter = new CustomArrayAdapter(this, R.layout.list_view_item, mData);
+        RelativeLayout mLayout = (RelativeLayout)findViewById(R.id.relative_layout);
+
+        mRoundView = (RoundView)findViewById(R.id.round_view);
+        mButton = (Button)findViewById(R.id.add_row_button);
+        mListView = (InsertionListView)findViewById(R.id.listview);
+
+        mListView.setAdapter(mAdapter);
+        mListView.setData(mData);
+        mListView.setLayout(mLayout);
+        mListView.setRowAdditionAnimationListener(this);
+    }
+
+    public void addRow(View view) {
+        mButton.setEnabled(false);
+
+        mItemNum++;
+        ListItemObject obj = mValues[mItemNum % mValues.length];
+        final ListItemObject newObj = new ListItemObject(obj.getTitle(), obj.getImgResource(),
+                mCellHeight);
+
+        boolean shouldAnimateInNewImage = mListView.shouldAnimateInNewImage();
+        if (!shouldAnimateInNewImage) {
+            mListView.addRow(newObj);
+            return;
+        }
+
+        mListView.setEnabled(false);
+        ObjectAnimator animator = mRoundView.getScalingAnimator();
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationRepeat(Animator animation) {
+                mListView.addRow(newObj);
+            }
+        });
+        animator.start();
+    }
+
+    @Override
+    public void onRowAdditionAnimationStart() {
+        mButton.setEnabled(false);
+    }
+
+    @Override
+    public void onRowAdditionAnimationEnd() {
+        mButton.setEnabled(true);
+    }
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertionListView.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertionListView.java
new file mode 100644
index 0000000..66866ba
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertionListView.java
@@ -0,0 +1,376 @@
+/*
+ * 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.insertingcells;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This ListView displays a set of ListItemObjects. By calling addRow with a new
+ * ListItemObject, it is added to the top of the ListView and the new row is animated
+ * in. If the ListView content is at the top (the scroll offset is 0), the animation of
+ * the new row is accompanied by an extra image animation that pops into place in its
+ * corresponding item in the ListView.
+ */
+public class InsertionListView extends ListView {
+
+    private static final int NEW_ROW_DURATION = 500;
+    private static final int OVERSHOOT_INTERPOLATOR_TENSION = 5;
+
+    private OvershootInterpolator sOvershootInterpolator;
+
+    private RelativeLayout mLayout;
+
+    private Context mContext;
+
+    private OnRowAdditionAnimationListener mRowAdditionAnimationListener;
+
+    private List<ListItemObject> mData;
+    private List<BitmapDrawable> mCellBitmapDrawables;
+
+    public InsertionListView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public InsertionListView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public InsertionListView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context);
+    }
+
+    public void init(Context context) {
+        setDivider(null);
+        mContext = context;
+        mCellBitmapDrawables = new ArrayList<BitmapDrawable>();
+        sOvershootInterpolator = new OvershootInterpolator(OVERSHOOT_INTERPOLATOR_TENSION);
+    }
+
+    /**
+     * Modifies the underlying data set and adapter through the addition of the new object
+     * to the first item of the ListView. The new cell is then animated into place from
+     * above the bounds of the ListView.
+     */
+    public void addRow(ListItemObject newObj) {
+
+        final CustomArrayAdapter adapter = (CustomArrayAdapter)getAdapter();
+
+        /**
+         * Stores the starting bounds and the corresponding bitmap drawables of every
+         * cell present in the ListView before the data set change takes place.
+         */
+        final HashMap<Long, Rect> listViewItemBounds = new HashMap<Long, Rect>();
+        final HashMap<Long, BitmapDrawable> listViewItemDrawables = new HashMap<Long,
+                BitmapDrawable>();
+
+        int firstVisiblePosition = getFirstVisiblePosition();
+        for (int i = 0; i < getChildCount(); ++i) {
+            View child = getChildAt(i);
+            int position = firstVisiblePosition + i;
+            long itemID = adapter.getItemId(position);
+            Rect startRect = new Rect(child.getLeft(), child.getTop(), child.getRight(),
+                    child.getBottom());
+            listViewItemBounds.put(itemID, startRect);
+            listViewItemDrawables.put(itemID, getBitmapDrawableFromView(child));
+        }
+
+        /** Adds the new object to the data set, thereby modifying the adapter,
+         *  as well as adding a stable Id for that specified object.*/
+        mData.add(0, newObj);
+        adapter.addStableIdForDataAtPosition(0);
+        adapter.notifyDataSetChanged();
+
+        final ViewTreeObserver observer = getViewTreeObserver();
+        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+            public boolean onPreDraw() {
+                observer.removeOnPreDrawListener(this);
+
+                ArrayList<Animator> animations = new ArrayList<Animator>();
+
+                final View newCell = getChildAt(0);
+                final ImageView imgView = (ImageView)newCell.findViewById(R.id.image_view);
+                final ImageView copyImgView = new ImageView(mContext);
+
+                int firstVisiblePosition = getFirstVisiblePosition();
+                final boolean shouldAnimateInNewRow = shouldAnimateInNewRow();
+                final boolean shouldAnimateInImage = shouldAnimateInNewImage();
+
+                if (shouldAnimateInNewRow) {
+                    /** Fades in the text of the first cell. */
+                    TextView textView = (TextView)newCell.findViewById(R.id.text_view);
+                    ObjectAnimator textAlphaAnimator = ObjectAnimator.ofFloat(textView,
+                            View.ALPHA, 0.0f, 1.0f);
+                    animations.add(textAlphaAnimator);
+
+                    /** Animates in the extra hover view corresponding to the image
+                     * in the top row of the ListView. */
+                    if (shouldAnimateInImage) {
+
+                        int width = imgView.getWidth();
+                        int height = imgView.getHeight();
+
+                        Point childLoc = getLocationOnScreen(newCell);
+                        Point layoutLoc = getLocationOnScreen(mLayout);
+
+                        ListItemObject obj = mData.get(0);
+                        Bitmap bitmap = CustomArrayAdapter.getCroppedBitmap(BitmapFactory
+                                .decodeResource(mContext.getResources(), obj.getImgResource(),
+                                        null));
+                        copyImgView.setImageBitmap(bitmap);
+
+                        imgView.setVisibility(View.INVISIBLE);
+
+                        copyImgView.setScaleType(ImageView.ScaleType.CENTER);
+
+                        ObjectAnimator imgViewTranslation = ObjectAnimator.ofFloat(copyImgView,
+                                View.Y, childLoc.y - layoutLoc.y);
+
+                        PropertyValuesHolder imgViewScaleY = PropertyValuesHolder.ofFloat
+                                (View.SCALE_Y, 0, 1.0f);
+                        PropertyValuesHolder imgViewScaleX = PropertyValuesHolder.ofFloat
+                                (View.SCALE_X, 0, 1.0f);
+                        ObjectAnimator imgViewScaleAnimator = ObjectAnimator
+                                .ofPropertyValuesHolder(copyImgView, imgViewScaleX, imgViewScaleY);
+                        imgViewScaleAnimator.setInterpolator(sOvershootInterpolator);
+                        animations.add(imgViewTranslation);
+                        animations.add(imgViewScaleAnimator);
+
+                        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams
+                                (width, height);
+
+                        mLayout.addView(copyImgView, params);
+                    }
+                }
+
+                /** Loops through all the current visible cells in the ListView and animates
+                 * all of them into their post layout positions from their original positions.*/
+                for (int i = 0; i < getChildCount(); i++) {
+                    View child = getChildAt(i);
+                    int position = firstVisiblePosition + i;
+                    long itemId = adapter.getItemId(position);
+                    Rect startRect = listViewItemBounds.get(itemId);
+                    int top = child.getTop();
+                    if (startRect != null) {
+                        /** If the cell was visible before the data set change and
+                         * after the data set change, then animate the cell between
+                         * the two positions.*/
+                        int startTop = startRect.top;
+                        int delta = startTop - top;
+                        ObjectAnimator animation = ObjectAnimator.ofFloat(child,
+                                View.TRANSLATION_Y, delta, 0);
+                        animations.add(animation);
+                    } else {
+                        /** If the cell was not visible (or present) before the data set
+                         * change but is visible after the data set change, then use its
+                         * height to determine the delta by which it should be animated.*/
+                        int childHeight = child.getHeight() + getDividerHeight();
+                        int startTop = top + (i > 0 ? childHeight : -childHeight);
+                        int delta = startTop - top;
+                        ObjectAnimator animation = ObjectAnimator.ofFloat(child,
+                                View.TRANSLATION_Y, delta, 0);
+                        animations.add(animation);
+                    }
+                    listViewItemBounds.remove(itemId);
+                    listViewItemDrawables.remove(itemId);
+                }
+
+                /**
+                 * Loops through all the cells that were visible before the data set
+                 * changed but not after, and keeps track of their corresponding
+                 * drawables. The bounds of each drawable are then animated from the
+                 * original state to the new one (off the screen). By storing all
+                 * the drawables that meet this criteria, they can be redrawn on top
+                 * of the ListView via dispatchDraw as they are animating.
+                 */
+                for (Long itemId: listViewItemBounds.keySet()) {
+                    BitmapDrawable bitmapDrawable = listViewItemDrawables.get(itemId);
+                    Rect startBounds = listViewItemBounds.get(itemId);
+                    bitmapDrawable.setBounds(startBounds);
+
+                    int childHeight = startBounds.bottom - startBounds.top + getDividerHeight();
+                    Rect endBounds = new Rect(startBounds);
+                    endBounds.offset(0, childHeight);
+
+                    ObjectAnimator animation = ObjectAnimator.ofObject(bitmapDrawable,
+                            "bounds", sBoundsEvaluator, startBounds, endBounds);
+                    animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                        private Rect mLastBound = null;
+                        private Rect mCurrentBound = new Rect();
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                            Rect bounds = (Rect)valueAnimator.getAnimatedValue();
+                            mCurrentBound.set(bounds);
+                            if (mLastBound != null) {
+                                mCurrentBound.union(mLastBound);
+                            }
+                            mLastBound = bounds;
+                            invalidate(mCurrentBound);
+                        }
+                    });
+
+                    listViewItemBounds.remove(itemId);
+                    listViewItemDrawables.remove(itemId);
+
+                    mCellBitmapDrawables.add(bitmapDrawable);
+
+                    animations.add(animation);
+                }
+
+                /** Animates all the cells from their old position to their new position
+                 *  at the same time.*/
+                setEnabled(false);
+                mRowAdditionAnimationListener.onRowAdditionAnimationStart();
+                AnimatorSet set = new AnimatorSet();
+                set.setDuration(NEW_ROW_DURATION);
+                set.playTogether(animations);
+                set.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mCellBitmapDrawables.clear();
+                        imgView.setVisibility(View.VISIBLE);
+                        mLayout.removeView(copyImgView);
+                        mRowAdditionAnimationListener.onRowAdditionAnimationEnd();
+                        setEnabled(true);
+                        invalidate();
+                    }
+                });
+                set.start();
+
+                listViewItemBounds.clear();
+                listViewItemDrawables.clear();
+                return true;
+            }
+        });
+    }
+
+    /**
+     * By overriding dispatchDraw, the BitmapDrawables of all the cells that were on the
+     * screen before (but not after) the layout are drawn and animated off the screen.
+     */
+    @Override
+    protected void dispatchDraw (Canvas canvas) {
+        super.dispatchDraw(canvas);
+        if (mCellBitmapDrawables.size() > 0) {
+            for (BitmapDrawable bitmapDrawable: mCellBitmapDrawables) {
+                bitmapDrawable.draw(canvas);
+            }
+        }
+    }
+
+    public boolean shouldAnimateInNewRow() {
+        int firstVisiblePosition = getFirstVisiblePosition();
+        return (firstVisiblePosition == 0);
+    }
+
+    public boolean shouldAnimateInNewImage() {
+        if (getChildCount() == 0) {
+            return true;
+        }
+        boolean shouldAnimateInNewRow = shouldAnimateInNewRow();
+        View topCell = getChildAt(0);
+        return (shouldAnimateInNewRow && topCell.getTop() == 0);
+    }
+
+    /** Returns a bitmap drawable showing a screenshot of the view passed in. */
+    private BitmapDrawable getBitmapDrawableFromView(View v) {
+        Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas (bitmap);
+        v.draw(canvas);
+        return new BitmapDrawable(getResources(), bitmap);
+    }
+
+    /**
+     * Returns the absolute x,y coordinates of the view relative to the top left
+     * corner of the phone screen.
+     */
+    public Point getLocationOnScreen(View v) {
+        DisplayMetrics dm = new DisplayMetrics();
+        ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);
+
+        int[] location = new int[2];
+        v.getLocationOnScreen(location);
+
+        return new Point(location[0], location[1]);
+    }
+
+    /** Setter for the underlying data set controlling the adapter. */
+    public void setData(List<ListItemObject> data) {
+        mData = data;
+    }
+
+    /**
+     * Setter for the parent RelativeLayout of this ListView. A reference to this
+     * ViewGroup is required in order to add the custom animated overlaying bitmap
+     * when adding a new row.
+     */
+    public void setLayout(RelativeLayout layout) {
+        mLayout = layout;
+    }
+
+    public void setRowAdditionAnimationListener(OnRowAdditionAnimationListener
+                                                        rowAdditionAnimationListener) {
+        mRowAdditionAnimationListener = rowAdditionAnimationListener;
+    }
+
+    /**
+     * This TypeEvaluator is used to animate the position of a BitmapDrawable
+     * by updating its bounds.
+     */
+    static final TypeEvaluator<Rect> sBoundsEvaluator = new TypeEvaluator<Rect>() {
+        public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
+            return new Rect(interpolate(startValue.left, endValue.left, fraction),
+                    interpolate(startValue.top, endValue.top, fraction),
+                    interpolate(startValue.right, endValue.right, fraction),
+                    interpolate(startValue.bottom, endValue.bottom, fraction));
+        }
+
+        public int interpolate(int start, int end, float fraction) {
+            return (int)(start + fraction * (end - start));
+        }
+    };
+
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/ListItemObject.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/ListItemObject.java
new file mode 100644
index 0000000..8ca56f5
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/ListItemObject.java
@@ -0,0 +1,48 @@
+/*
+ * 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.insertingcells;
+
+/**
+ * The data model for every cell in the ListView for this application. This model stores
+ * a title, an image resource and a default cell height for every item in the ListView.
+ */
+public class ListItemObject {
+
+    private String mTitle;
+    private int mImgResource;
+    private int mHeight;
+
+    public ListItemObject(String title, int imgResource, int height) {
+        super();
+        mTitle = title;
+        mImgResource = imgResource;
+        mHeight = height;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public int getImgResource() {
+        return mImgResource;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/OnRowAdditionAnimationListener.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/OnRowAdditionAnimationListener.java
new file mode 100644
index 0000000..f371f3e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/OnRowAdditionAnimationListener.java
@@ -0,0 +1,28 @@
+/*
+ * 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.insertingcells;
+
+/**
+ * This listener is used to determine when the animation of a new row addition
+ * begins and ends. The primary use of this interface is to create a callback
+ * under which certain elements, such as the listview itself, can be disabled
+ * to prevent unpredictable behaviour during the actual cell animation.
+ */
+public interface OnRowAdditionAnimationListener {
+    public void onRowAdditionAnimationStart();
+    public void onRowAdditionAnimationEnd();
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/RoundView.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/RoundView.java
new file mode 100644
index 0000000..320c8b9
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/RoundView.java
@@ -0,0 +1,85 @@
+/*
+ * 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.insertingcells;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * This round view draws a circle from which the image pops out of and into
+ * the corresponding cell in the list.
+ */
+public class RoundView extends View {
+
+    private final int STROKE_WIDTH = 6;
+    private final int RADIUS = 20;
+    private final int ANIMATION_DURATION = 300;
+    private final float SCALE_FACTOR = 0.3f;
+
+    private Paint mPaint;
+
+    public RoundView(Context context) {
+        super(context);
+        init();
+    }
+
+    public RoundView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public RoundView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    private void init() {
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setColor(Color.WHITE);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(STROKE_WIDTH);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2,
+                RADIUS, mPaint);
+    }
+
+    public ObjectAnimator getScalingAnimator() {
+        PropertyValuesHolder imgViewScaleY = PropertyValuesHolder.ofFloat(View
+                .SCALE_Y, SCALE_FACTOR);
+        PropertyValuesHolder imgViewScaleX = PropertyValuesHolder.ofFloat(View
+                .SCALE_X, SCALE_FACTOR);
+
+        ObjectAnimator imgViewScaleAnimator = ObjectAnimator
+                .ofPropertyValuesHolder(this, imgViewScaleX, imgViewScaleY);
+        imgViewScaleAnimator.setRepeatCount(1);
+        imgViewScaleAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        imgViewScaleAnimator.setDuration(ANIMATION_DURATION);
+
+        return imgViewScaleAnimator;
+    }
+}