am 75c7ca08: am c6f2450d: Merge "Add libstdc++ symbols for 64-bit archs"

* commit '75c7ca08ec34c51c2cf6e2f9da08b5a7c071a345':
  Add libstdc++ symbols for 64-bit archs
diff --git a/build/Android.mk b/build/Android.mk
index 6d798de..a3f1464 100644
--- a/build/Android.mk
+++ b/build/Android.mk
@@ -119,10 +119,13 @@
 endef
 
 ANDROID_SUPPORT_LIBRARIES := \
+    android-support-annotations \
     android-support-v4 \
     android-support-v7-gridlayout \
     android-support-v7-appcompat \
     android-support-v7-mediarouter \
-    android-support-v13
+    android-support-v7-recyclerview \
+    android-support-v13 \
+    android-support-v17-leanback
 
 $(foreach lib, $(ANDROID_SUPPORT_LIBRARIES), $(eval $(call _package_sdk_library,$(lib))))
diff --git a/build/sdk.atree b/build/sdk.atree
index 5025ba5..88b448a 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -83,25 +83,27 @@
 external/clang/LICENSE.TXT                    build-tools/${PLATFORM_NAME}/renderscript/clang-include/LICENSE.TXT
 
 prebuilts/sdk/renderscript/lib/javalib.jar            build-tools/${PLATFORM_NAME}/renderscript/lib/renderscript-v8.jar
-prebuilts/sdk/renderscript/lib/arm/libclcore.bc       build-tools/${PLATFORM_NAME}/renderscript/lib/libclcore.bc
 
 prebuilts/sdk/renderscript/lib/arm/libc.so            build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/armeabi-v7a/libc.so
 prebuilts/sdk/renderscript/lib/arm/libm.so            build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/armeabi-v7a/libm.so
 prebuilts/sdk/renderscript/lib/arm/libcompiler_rt.a   build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/armeabi-v7a/libcompiler_rt.a
 prebuilts/sdk/renderscript/lib/arm/libRSSupport.so    build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/armeabi-v7a/libRSSupport.so
 prebuilts/sdk/renderscript/lib/arm/librsjni.so        build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/armeabi-v7a/librsjni.so
+prebuilts/sdk/renderscript/lib/arm/libclcore.bc       build-tools/${PLATFORM_NAME}/renderscript/lib/bc/armeabi-v7a/libclcore.bc
 
 prebuilts/sdk/renderscript/lib/mips/libc.so            build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/mips/libc.so
 prebuilts/sdk/renderscript/lib/mips/libm.so            build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/mips/libm.so
 prebuilts/sdk/renderscript/lib/mips/libcompiler_rt.a   build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/mips/libcompiler_rt.a
 prebuilts/sdk/renderscript/lib/mips/libRSSupport.so    build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/mips/libRSSupport.so
 prebuilts/sdk/renderscript/lib/mips/librsjni.so        build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/mips/librsjni.so
+prebuilts/sdk/renderscript/lib/mips/libclcore.bc       build-tools/${PLATFORM_NAME}/renderscript/lib/bc/mips/libclcore.bc
 
 prebuilts/sdk/renderscript/lib/x86/libc.so            build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/x86/libc.so
 prebuilts/sdk/renderscript/lib/x86/libm.so            build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/x86/libm.so
 prebuilts/sdk/renderscript/lib/x86/libcompiler_rt.a   build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/x86/libcompiler_rt.a
 prebuilts/sdk/renderscript/lib/x86/libRSSupport.so    build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/x86/libRSSupport.so
 prebuilts/sdk/renderscript/lib/x86/librsjni.so        build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/x86/librsjni.so
+prebuilts/sdk/renderscript/lib/x86/libclcore.bc       build-tools/${PLATFORM_NAME}/renderscript/lib/bc/x86/libclcore.bc
 
 
 
@@ -168,6 +170,7 @@
 
 # fonts for layoutlib.
 frameworks/base/data/fonts    platforms/${PLATFORM_NAME}/data/fonts
+external/noto-fonts           platforms/${PLATFORM_NAME}/data/fonts
 
 # NOTICE files are copied by build/core/Makefile from sdk.git
 sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
@@ -218,14 +221,54 @@
 # Note: Some samples reference a shared "common" directory. In the future
 # this will be copied in automatically via a templating system. For now,
 # we need to copy it in here as needed.
-developers/samples/android/connectivity        samples/${PLATFORM_NAME}/connectivity
-developers/samples/android/common              samples/${PLATFORM_NAME}/connectivity/sync/BasicSyncAdapter/BasicSyncAdapter/src/main/java/com/example/android/common
-developers/samples/android/content             samples/${PLATFORM_NAME}/content
-developers/samples/android/input               samples/${PLATFORM_NAME}/input
-developers/samples/android/media               samples/${PLATFORM_NAME}/media
-developers/samples/android/security            samples/${PLATFORM_NAME}/security
-developers/samples/android/testing             samples/${PLATFORM_NAME}/testing
-developers/samples/android/ui                  samples/${PLATFORM_NAME}/ui
+developers/build/prebuilts/gradle/BasicAndroidKeyStore/                      samples/${PLATFORM_NAME}/security/BasicAndroidKeyStore
+developers/build/prebuilts/gradle/BasicSyncAdapter/                          samples/${PLATFORM_NAME}/connectivity/BasicSyncAdapter
+developers/build/prebuilts/gradle/NetworkConnect/                            samples/${PLATFORM_NAME}/connectivity/NetworkConnect
+developers/build/prebuilts/gradle/BasicNetworking/                           samples/${PLATFORM_NAME}/connectivity/BasicNetworking
+developers/build/prebuilts/gradle/BluetoothLeGatt/                           samples/${PLATFORM_NAME}/connectivity/BluetoothLeGatt
+developers/build/prebuilts/gradle/AppRestrictions/                           samples/${PLATFORM_NAME}/content/AppRestrictions
+developers/build/prebuilts/gradle/BasicContactables/                         samples/${PLATFORM_NAME}/content/BasicContactables
+developers/build/prebuilts/gradle/StorageClient/                             samples/${PLATFORM_NAME}/content/StorageClient
+developers/build/prebuilts/gradle/StorageProvider/                           samples/${PLATFORM_NAME}/content/StorageProvider
+developers/build/prebuilts/gradle/BasicGestureDetect/                        samples/${PLATFORM_NAME}/input/BasicGestureDetect
+developers/build/prebuilts/gradle/BasicMultitouch/                           samples/${PLATFORM_NAME}/input/BasicMultitouch
+developers/build/prebuilts/gradle/ActivityInstrumentation/                   samples/${PLATFORM_NAME}/testing/ActivityInstrumentation
+developers/build/prebuilts/gradle/MediaRecorder/                             samples/${PLATFORM_NAME}/media/MediaRecorder
+developers/build/prebuilts/gradle/BasicMediaRouter/                          samples/${PLATFORM_NAME}/media/BasicMediaRouter
+developers/build/prebuilts/gradle/BasicMediaDecoder/                         samples/${PLATFORM_NAME}/media/BasicMediaDecoder
+developers/build/prebuilts/gradle/BorderlessButtons/                         samples/${PLATFORM_NAME}/ui/BorderlessButtons
+developers/build/prebuilts/gradle/BasicAccessibility/                        samples/${PLATFORM_NAME}/ui/BasicAccessibility
+developers/build/prebuilts/gradle/CustomChoiceList/                          samples/${PLATFORM_NAME}/ui/CustomChoiceList
+developers/build/prebuilts/gradle/TextSwitcher/                              samples/${PLATFORM_NAME}/ui/TextSwitcher
+developers/build/prebuilts/gradle/HorizontalPaging/                          samples/${PLATFORM_NAME}/ui/HorizontalPaging
+developers/build/prebuilts/gradle/ActionBarCompat-Styled/                    samples/${PLATFORM_NAME}/ui/ActionBarCompat-Styled
+developers/build/prebuilts/gradle/ActionBarCompat-ListPopupMenu/             samples/${PLATFORM_NAME}/ui/ActionBarCompat-ListPopupMenu
+developers/build/prebuilts/gradle/ActionBarCompat-ShareActionProvider/       samples/${PLATFORM_NAME}/ui/ActionBarCompat-ShareActionProvider
+developers/build/prebuilts/gradle/ActionBarCompat-Basic/                     samples/${PLATFORM_NAME}/ui/ActionBarCompat-Basic
+developers/build/prebuilts/gradle/BasicNotifications/                        samples/${PLATFORM_NAME}/ui/BasicNotifications
+developers/build/prebuilts/gradle/CustomNotifications/                       samples/${PLATFORM_NAME}/ui/CustomNotifications
+developers/build/prebuilts/gradle/DoneBar/                                   samples/${PLATFORM_NAME}/ui/DoneBar
+developers/build/prebuilts/gradle/BasicImmersiveMode/                        samples/${PLATFORM_NAME}/ui/BasicImmersiveMode
+developers/build/prebuilts/gradle/AdvancedImmersiveMode/                     samples/${PLATFORM_NAME}/ui/AdvancedImmersiveMode
+developers/build/prebuilts/gradle/ImmersiveMode/                             samples/${PLATFORM_NAME}/ui/ImmersiveMode
+developers/build/prebuilts/gradle/RepeatingAlarm/                            samples/${PLATFORM_NAME}/background/RepeatingAlarm
+developers/build/prebuilts/gradle/TextLinkify/                               samples/${PLATFORM_NAME}/ui/TextLinkify
+developers/build/prebuilts/gradle/BasicRenderScript                          samples/${PLATFORM_NAME}/renderscript/BasicRenderScript
+developers/build/prebuilts/gradle/RenderScriptIntrinsic                      samples/${PLATFORM_NAME}/renderscript/RenderScriptIntrinsic
+developers/build/prebuilts/gradle/SlidingTabsBasic                           samples/${PLATFORM_NAME}/ui/SlidingTabsBasic
+developers/build/prebuilts/gradle/SlidingTabsColors                          samples/${PLATFORM_NAME}/ui/SlidingTabsColors
+developers/build/prebuilts/gradle/CardEmulation                              samples/${PLATFORM_NAME}/connectivity/CardEmulation
+developers/build/prebuilts/gradle/CardReader                                 samples/${PLATFORM_NAME}/connectivity/CardReader
+developers/build/prebuilts/gradle/BatchStepSensor                            samples/${PLATFORM_NAME}/sensors/BatchStepSensor
+developers/build/prebuilts/gradle/DisplayingBitmaps                          samples/${PLATFORM_NAME}/ui/DisplayingBitmaps
+developers/build/prebuilts/gradle/BasicTransition                            samples/${PLATFORM_NAME}/ui/BasicTransition
+developers/build/prebuilts/gradle/AdapterTransition                          samples/${PLATFORM_NAME}/ui/AdapterTransition
+developers/build/prebuilts/gradle/CustomTransition                           samples/${PLATFORM_NAME}/ui/CustomTransition
+developers/build/prebuilts/gradle/FragmentTransition                         samples/${PLATFORM_NAME}/ui/FragmentTransition
+developers/build/prebuilts/gradle/SwipeRefreshLayoutBasic                    samples/${PLATFORM_NAME}/ui/SwipeRefreshLayoutBasic
+developers/build/prebuilts/gradle/SwipeRefreshListFragment                   samples/${PLATFORM_NAME}/ui/SwipeRefreshListFragment
+developers/build/prebuilts/gradle/SwipeRefreshMultipleViews                  samples/${PLATFORM_NAME}/ui/SwipeRefreshMultipleViews
+developers/build/prebuilts/gradle/MediaRouter                                samples/${PLATFORM_NAME}/media/MediaRouter
 
 # Old sample tree
 development/samples/AccelerometerPlay          samples/${PLATFORM_NAME}/legacy/AccelerometerPlay
@@ -296,6 +339,8 @@
 development/sdk/support_README.txt                                                                extras/android/support/README.txt
 sdk/files/sdk_files_NOTICE.txt                                                                    extras/android/support/NOTICE.txt
 
+${OUT_DIR}/target/common/obj/PACKAGING/android-support-annotations_intermediates/android-support-annotations.jar extras/android/support/annotations/android-support-annotations.jar
+
 ${OUT_DIR}/target/common/obj/PACKAGING/android-support-v4_intermediates/android-support-v4.jar    extras/android/support/v4/android-support-v4.jar
 frameworks/support/v4                                                                             extras/android/support/v4/src
 development/samples/Support4Demos                                                                 extras/android/support/samples/Support4Demos
@@ -335,6 +380,20 @@
 
 development/samples/Support7Demos                                                                 extras/android/support/samples/Support7Demos
 
+# TODO: add .project, .classpath, etc.
+frameworks/support/v7/recyclerview/README.txt                                                     extras/android/support/v7/recyclerview/README.txt
+frameworks/support/v7/recyclerview/AndroidManifest.xml                                            extras/android/support/v7/recyclerview/AndroidManifest.xml
+${OUT_DIR}/target/common/obj/PACKAGING/android-support-v7-recyclerview_intermediates/android-support-v7-recyclerview.jar    extras/android/support/v7/recyclerview/libs/android-support-v7-recyclerview.jar
+
+frameworks/support/v17/leanback/README.txt                                                        extras/android/support/v17/leanback/README.txt
+frameworks/support/v17/leanback/.project                                                          extras/android/support/v17/leanback/.project
+frameworks/support/v17/leanback/.classpath                                                        extras/android/support/v17/leanback/.classpath
+frameworks/support/v17/leanback/AndroidManifest.xml                                               extras/android/support/v17/leanback/AndroidManifest.xml
+frameworks/support/v17/leanback/project.properties                                                extras/android/support/v17/leanback/project.properties
+frameworks/support/v17/leanback/res                                                               extras/android/support/v17/leanback/res
+frameworks/support/v17/leanback/src/.readme                                                       extras/android/support/v17/leanback/src/.readme
+${OUT_DIR}/target/common/obj/PACKAGING/android-support-v17-leanback_intermediates/android-support-v17-leanback.jar    extras/android/support/v17/leanback/libs/android-support-v17-leanback.jar
+
 ##############################################################################
 # Tests Component
 ##############################################################################
diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath
index dda0606..af99325 100644
--- a/ide/eclipse/.classpath
+++ b/ide/eclipse/.classpath
@@ -65,7 +65,12 @@
 	<classpathentry kind="src" path="frameworks/base/packages/SystemUI/src"/>
 	<classpathentry kind="src" path="frameworks/base/policy/src"/>
 	<classpathentry kind="src" path="frameworks/base/sax/java"/>
-	<classpathentry kind="src" path="frameworks/base/services/java"/>
+	<classpathentry kind="src" path="frameworks/base/services/core/java"/>
+        <classpathentry kind="src" path="frameworks/base/services/accessibility/java"/>
+        <classpathentry kind="src" path="frameworks/base/services/print/java"/>
+        <classpathentry kind="src" path="frameworks/base/services/backup/java"/>
+        <classpathentry kind="src" path="frameworks/base/services/devicepolicy/java"/>
+        <classpathentry kind="src" path="frameworks/base/services/appwidget/java"/>
 	<classpathentry kind="src" path="frameworks/base/telephony/java"/>
 	<classpathentry kind="src" path="frameworks/base/test-runner/src"/>
 	<classpathentry kind="src" path="frameworks/base/wifi/java"/>
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index d7bda31..f202d59 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -2469,6 +2469,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".view.GameActivity"
+                android:label="Views/System UI Visibility/Game"
+                android:theme="@android:style/Theme.Holo.NoActionBar"
+                android:enabled="@bool/atLeastKitKat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".view.Switches" android:label="Views/Switches">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/res/layout/game.xml b/samples/ApiDemos/res/layout/game.xml
new file mode 100644
index 0000000..809a253
--- /dev/null
+++ b/samples/ApiDemos/res/layout/game.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- BEGIN_INCLUDE(complete) -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent" android:layout_height="match_parent"
+    >
+    <!-- This is the outer area of the entire game screen, extending out under
+         system UI elements. -->
+    <view class="com.example.android.apis.view.GameActivity$Content"
+        android:id="@+id/content"
+        android:src="@drawable/frantic"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        />
+    <!-- This is the inner area of the game, not covered by system UI elements.
+        Any UI elements that need to be accessible when the game is paused or other
+        states where the system UI is shown (such as in menus) should go here. -->
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fitsSystemWindows="true"
+        android:animateLayoutChanges="true"
+        >
+        <Button
+            android:id="@+id/play"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|right"
+            android:textSize="28dp"
+            />
+    </FrameLayout>
+</FrameLayout>
+<!-- END_INCLUDE(complete) -->
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
index ffcf890..e21ca80 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
@@ -266,7 +266,7 @@
                     onReleaseResources(apps);
                 }
             }
-            List<AppEntry> oldApps = apps;
+            List<AppEntry> oldApps = mApps;
             mApps = apps;
 
             if (isStarted()) {
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
index c3c8a6c..a863014 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.java
@@ -26,6 +26,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.util.AttributeSet;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -87,9 +88,6 @@
     /** Is fading mode enabled? */
     boolean mFading;
 
-    /** The index of the current color to use. */
-    int mColorIndex;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -104,10 +102,10 @@
         // the contents of the bitmap.
         if (savedInstanceState != null) {
             mFading = savedInstanceState.getBoolean("fading", true);
-            mColorIndex = savedInstanceState.getInt("color", 0);
+            mView.mColorIndex = savedInstanceState.getInt("color", 0);
         } else {
             mFading = true;
-            mColorIndex = 0;
+            mView.mColorIndex = 0;
         }
     }
 
@@ -161,7 +159,7 @@
         // Save away the fading state to restore if needed later.  Note that
         // we do not currently save the contents of the display.
         outState.putBoolean("fading", mFading);
-        outState.putInt("color", mColorIndex);
+        outState.putInt("color", mView.mColorIndex);
     }
 
     @Override
@@ -225,9 +223,9 @@
      *
      * It handles all of the input events and drawing functions.
      */
-    class PaintView extends View {
+    public static class PaintView extends View {
         private static final int FADE_ALPHA = 0x06;
-        private static final int MAX_FADE_STEPS = 256 / FADE_ALPHA + 4;
+        private static final int MAX_FADE_STEPS = 256 / (FADE_ALPHA/2) + 4;
         private static final int TRACKBALL_SCALE = 10;
 
         private static final int SPLAT_VECTORS = 40;
@@ -235,21 +233,31 @@
         private final Random mRandom = new Random();
         private Bitmap mBitmap;
         private Canvas mCanvas;
-        private final Paint mPaint;
-        private final Paint mFadePaint;
+        private final Paint mPaint = new Paint();
+        private final Paint mFadePaint = new Paint();
         private float mCurX;
         private float mCurY;
         private int mOldButtonState;
         private int mFadeSteps = MAX_FADE_STEPS;
 
+        /** The index of the current color to use. */
+        int mColorIndex;
+
         public PaintView(Context c) {
             super(c);
+            init();
+        }
+
+        public PaintView(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            init();
+        }
+
+        private void init() {
             setFocusable(true);
 
-            mPaint = new Paint();
             mPaint.setAntiAlias(true);
 
-            mFadePaint = new Paint();
             mFadePaint.setColor(BACKGROUND_COLOR);
             mFadePaint.setAlpha(FADE_ALPHA);
         }
@@ -273,6 +281,31 @@
             }
         }
 
+        public void text(String text) {
+            if (mBitmap != null) {
+                final int width = mBitmap.getWidth();
+                final int height = mBitmap.getHeight();
+                mPaint.setColor(COLORS[mColorIndex]);
+                mPaint.setAlpha(255);
+                int size = height;
+                mPaint.setTextSize(size);
+                Rect bounds = new Rect();
+                mPaint.getTextBounds(text, 0, text.length(), bounds);
+                int twidth = bounds.width();
+                twidth += (twidth/4);
+                if (twidth > width) {
+                    size = (size*width)/twidth;
+                    mPaint.setTextSize(size);
+                    mPaint.getTextBounds(text, 0, text.length(), bounds);
+                }
+                Paint.FontMetrics fm = mPaint.getFontMetrics();
+                mCanvas.drawText(text, (width-bounds.width())/2,
+                        ((height-size)/2) - fm.ascent, mPaint);
+                mFadeSteps = 0;
+                invalidate();
+            }
+        }
+
         @Override
         protected void onSizeChanged(int w, int h, int oldw, int oldh) {
             int curW = mBitmap != null ? mBitmap.getWidth() : 0;
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GameActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/GameActivity.java
new file mode 100644
index 0000000..a025427
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GameActivity.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2011 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.apis.view;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SeekBar;
+import android.widget.ShareActionProvider;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.SearchView.OnQueryTextListener;
+
+import com.example.android.apis.R;
+import com.example.android.apis.graphics.TouchPaint;
+
+/**
+ * This activity demonstrates how to use the system UI flags to
+ * implement an immersive game.
+ */
+public class GameActivity extends Activity {
+
+    /**
+     * Implementation of a view for the game, filling the entire screen.
+     */
+//BEGIN_INCLUDE(content)
+    public static class Content extends TouchPaint.PaintView implements
+            View.OnSystemUiVisibilityChangeListener, View.OnClickListener {
+        Activity mActivity;
+        Button mPlayButton;
+        boolean mPaused;
+        int mLastSystemUiVis;
+        boolean mUpdateSystemUi;
+
+        Runnable mFader = new Runnable() {
+            @Override public void run() {
+                fade();
+                if (mUpdateSystemUi) {
+                    updateNavVisibility();
+                }
+                if (!mPaused) {
+                    getHandler().postDelayed(mFader, 1000/30);
+                }
+            }
+        };
+
+        public Content(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            setOnSystemUiVisibilityChangeListener(this);
+        }
+
+        public void init(Activity activity, Button playButton) {
+            // This called by the containing activity to supply the surrounding
+            // state of the game that it will interact with.
+            mActivity = activity;
+            mPlayButton = playButton;
+            mPlayButton.setOnClickListener(this);
+            setGamePaused(true);
+        }
+
+        @Override public void onSystemUiVisibilityChange(int visibility) {
+            // Detect when we go out of nav-hidden mode, to reset back to having
+            // it hidden; our game wants those elements to stay hidden as long
+            // as it is being played and stay shown when paused.
+            int diff = mLastSystemUiVis ^ visibility;
+            mLastSystemUiVis = visibility;
+            if (!mPaused && (diff&SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
+                    && (visibility&SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
+                // We are running and the system UI navigation has become
+                // shown...  we want it to remain hidden, so update our system
+                // UI state at the next game loop.
+                mUpdateSystemUi = true;
+            }
+        }
+
+        @Override protected void onWindowVisibilityChanged(int visibility) {
+            super.onWindowVisibilityChanged(visibility);
+
+            // When we become visible or invisible, play is paused.
+            setGamePaused(true);
+        }
+
+        @Override
+        public void onWindowFocusChanged(boolean hasWindowFocus) {
+            super.onWindowFocusChanged(hasWindowFocus);
+
+            // When we become visible or invisible, play is paused.
+            // Optional: pause game when window loses focus.  This will cause it to
+            // pause, for example, when the notification shade is pulled down.
+            if (!hasWindowFocus) {
+                //setGamePaused(true);
+            }
+        }
+
+        @Override public void onClick(View v) {
+            if (v == mPlayButton) {
+                // Clicking on the play/pause button toggles its state.
+                setGamePaused(!mPaused);
+            }
+        }
+
+        void setGamePaused(boolean paused) {
+            mPaused = paused;
+            mPlayButton.setText(paused ? R.string.play : R.string.pause);
+            setKeepScreenOn(!paused);
+            updateNavVisibility();
+            Handler h = getHandler();
+            if (h != null) {
+                getHandler().removeCallbacks(mFader);
+                if (!paused) {
+                    mFader.run();
+                    text("Draw!");
+                }
+            }
+        }
+
+        void updateNavVisibility() {
+            int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | SYSTEM_UI_FLAG_LAYOUT_STABLE;
+            if (!mPaused) {
+                newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
+                        | SYSTEM_UI_FLAG_HIDE_NAVIGATION  | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+            }
+
+            // Set the new desired visibility.
+            setSystemUiVisibility(newVis);
+            mUpdateSystemUi = false;
+        }
+    }
+//END_INCLUDE(content)
+
+    Content mContent;
+
+    public GameActivity() {
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.game);
+        mContent = (Content)findViewById(R.id.content);
+        mContent.init(this, (Button)findViewById(R.id.play));
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        // Pause game when its activity is paused.
+        mContent.setGamePaused(true);
+    }
+}
diff --git a/samples/ControllerSample/AndroidManifest.xml b/samples/ControllerSample/AndroidManifest.xml
new file mode 100644
index 0000000..49b67d7
--- /dev/null
+++ b/samples/ControllerSample/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.controllersample"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-permission android:name="android.permission.VIBRATE" />
+
+    <uses-sdk
+        android:minSdkVersion="9"
+        android:targetSdkVersion="18" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name=".GameViewActivity"
+            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>
\ No newline at end of file
diff --git a/samples/ControllerSample/libs/android-support-v4.jar b/samples/ControllerSample/libs/android-support-v4.jar
new file mode 100644
index 0000000..65ebaf8
--- /dev/null
+++ b/samples/ControllerSample/libs/android-support-v4.jar
Binary files differ
diff --git a/samples/ControllerSample/proguard-project.txt b/samples/ControllerSample/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/samples/ControllerSample/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/samples/ControllerSample/project.properties b/samples/ControllerSample/project.properties
new file mode 100644
index 0000000..ce39f2d
--- /dev/null
+++ b/samples/ControllerSample/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-18
diff --git a/samples/ControllerSample/res/drawable-hdpi/ic_launcher.png b/samples/ControllerSample/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..4f421f9
--- /dev/null
+++ b/samples/ControllerSample/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ControllerSample/res/drawable-mdpi/ic_launcher.png b/samples/ControllerSample/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..38651fd
--- /dev/null
+++ b/samples/ControllerSample/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ControllerSample/res/drawable-xhdpi/ic_launcher.png b/samples/ControllerSample/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..c6f6d82
--- /dev/null
+++ b/samples/ControllerSample/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ControllerSample/res/drawable-xxhdpi/ic_launcher.png b/samples/ControllerSample/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..eef1c76
--- /dev/null
+++ b/samples/ControllerSample/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/ControllerSample/res/layout/game_controller_input.xml b/samples/ControllerSample/res/layout/game_controller_input.xml
new file mode 100644
index 0000000..4e4a735
--- /dev/null
+++ b/samples/ControllerSample/res/layout/game_controller_input.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+
+<!-- Game controller input demo. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="12dip"
+        android:text="@string/game_controller_input_description" />
+
+    <com.example.controllersample.GameView
+        android:id="@+id/game"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_margin="15dip"
+        android:layout_weight="1"
+        android:background="#000000" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/ControllerSample/res/values-v11/styles.xml b/samples/ControllerSample/res/values-v11/styles.xml
new file mode 100644
index 0000000..541752f
--- /dev/null
+++ b/samples/ControllerSample/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/ControllerSample/res/values-v14/styles.xml b/samples/ControllerSample/res/values-v14/styles.xml
new file mode 100644
index 0000000..f20e015
--- /dev/null
+++ b/samples/ControllerSample/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/ControllerSample/res/values/strings.xml b/samples/ControllerSample/res/values/strings.xml
new file mode 100644
index 0000000..ba8e7d7
--- /dev/null
+++ b/samples/ControllerSample/res/values/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Controller Sample</string>
+    <string name="game_controller_input_description">
+        This activity demonstrates how to process input events received from
+        game controllers.  Please connect your game controller now and try
+        moving the joysticks or pressing buttons.  If it helps, try to imagine
+        that you are a lone space cowboy in hot pursuit of the aliens who kidnapped
+        your favorite llama on their way back to Andromeda&#8230;
+    </string>
+</resources>
\ No newline at end of file
diff --git a/samples/ControllerSample/res/values/styles.xml b/samples/ControllerSample/res/values/styles.xml
new file mode 100644
index 0000000..4a10ca4
--- /dev/null
+++ b/samples/ControllerSample/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/ControllerSample/src/com/example/controllersample/GameView.java b/samples/ControllerSample/src/com/example/controllersample/GameView.java
new file mode 100644
index 0000000..6481a2a
--- /dev/null
+++ b/samples/ControllerSample/src/com/example/controllersample/GameView.java
@@ -0,0 +1,1159 @@
+/*
+ * 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.controllersample;
+
+import com.example.inputmanagercompat.InputManagerCompat;
+import com.example.inputmanagercompat.InputManagerCompat.InputDeviceListener;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.Vibrator;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/*
+ * A trivial joystick based physics game to demonstrate joystick handling. If
+ * the game controller has a vibrator, then it is used to provide feedback when
+ * a bullet is fired or the ship crashes into an obstacle. Otherwise, the system
+ * vibrator is used for that purpose.
+ */
+@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
+public class GameView extends View implements InputDeviceListener {
+    private static final int MAX_OBSTACLES = 12;
+
+    private static final int DPAD_STATE_LEFT = 1 << 0;
+    private static final int DPAD_STATE_RIGHT = 1 << 1;
+    private static final int DPAD_STATE_UP = 1 << 2;
+    private static final int DPAD_STATE_DOWN = 1 << 3;
+
+    private final Random mRandom;
+    /*
+     * Each ship is created as an event comes in from a new Joystick device
+     */
+    private final SparseArray<Ship> mShips;
+    private final Map<String, Integer> mDescriptorMap;
+    private final List<Bullet> mBullets;
+    private final List<Obstacle> mObstacles;
+
+    private long mLastStepTime;
+    private final InputManagerCompat mInputManager;
+
+    private final float mBaseSpeed;
+
+    private final float mShipSize;
+
+    private final float mBulletSize;
+
+    private final float mMinObstacleSize;
+    private final float mMaxObstacleSize;
+    private final float mMinObstacleSpeed;
+    private final float mMaxObstacleSpeed;
+
+    public GameView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mRandom = new Random();
+        mShips = new SparseArray<Ship>();
+        mDescriptorMap = new HashMap<String, Integer>();
+        mBullets = new ArrayList<Bullet>();
+        mObstacles = new ArrayList<Obstacle>();
+
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+
+        float baseSize = getContext().getResources().getDisplayMetrics().density * 5f;
+        mBaseSpeed = baseSize * 3;
+
+        mShipSize = baseSize * 3;
+
+        mBulletSize = baseSize;
+
+        mMinObstacleSize = baseSize * 2;
+        mMaxObstacleSize = baseSize * 12;
+        mMinObstacleSpeed = mBaseSpeed;
+        mMaxObstacleSpeed = mBaseSpeed * 3;
+
+        mInputManager = InputManagerCompat.Factory.getInputManager(this.getContext());
+        mInputManager.registerInputDeviceListener(this, null);
+    }
+
+    // Iterate through the input devices, looking for controllers. Create a ship
+    // for every device that reports itself as a gamepad or joystick.
+    void findControllersAndAttachShips() {
+        int[] deviceIds = mInputManager.getInputDeviceIds();
+        for (int deviceId : deviceIds) {
+            InputDevice dev = mInputManager.getInputDevice(deviceId);
+            int sources = dev.getSources();
+            // if the device is a gamepad/joystick, create a ship to represent it
+            if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
+                    ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
+                // if the device has a gamepad or joystick
+                getShipForId(deviceId);
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        int deviceId = event.getDeviceId();
+        if (deviceId != -1) {
+            Ship currentShip = getShipForId(deviceId);
+            if (currentShip.onKeyDown(keyCode, event)) {
+                step(event.getEventTime());
+                return true;
+            }
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        int deviceId = event.getDeviceId();
+        if (deviceId != -1) {
+            Ship currentShip = getShipForId(deviceId);
+            if (currentShip.onKeyUp(keyCode, event)) {
+                step(event.getEventTime());
+                return true;
+            }
+        }
+
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        mInputManager.onGenericMotionEvent(event);
+
+        // Check that the event came from a joystick or gamepad since a generic
+        // motion event could be almost anything. API level 18 adds the useful
+        // event.isFromSource() helper function.
+        int eventSource = event.getSource();
+        if ((((eventSource & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
+                ((eventSource & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK))
+                && event.getAction() == MotionEvent.ACTION_MOVE) {
+            int id = event.getDeviceId();
+            if (-1 != id) {
+                Ship curShip = getShipForId(id);
+                if (curShip.onGenericMotionEvent(event)) {
+                    return true;
+                }
+            }
+        }
+        return super.onGenericMotionEvent(event);
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        // Turn on and off animations based on the window focus.
+        // Alternately, we could update the game state using the Activity
+        // onResume()
+        // and onPause() lifecycle events.
+        if (hasWindowFocus) {
+            mLastStepTime = SystemClock.uptimeMillis();
+            mInputManager.onResume();
+        } else {
+            int numShips = mShips.size();
+            for (int i = 0; i < numShips; i++) {
+                Ship currentShip = mShips.valueAt(i);
+                if (currentShip != null) {
+                    currentShip.setHeading(0, 0);
+                    currentShip.setVelocity(0, 0);
+                    currentShip.mDPadState = 0;
+                }
+            }
+            mInputManager.onPause();
+        }
+
+        super.onWindowFocusChanged(hasWindowFocus);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        // Reset the game when the view changes size.
+        reset();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        // Update the animation
+        animateFrame();
+
+        // Draw the ships.
+        int numShips = mShips.size();
+        for (int i = 0; i < numShips; i++) {
+            Ship currentShip = mShips.valueAt(i);
+            if (currentShip != null) {
+                currentShip.draw(canvas);
+            }
+        }
+
+        // Draw bullets.
+        int numBullets = mBullets.size();
+        for (int i = 0; i < numBullets; i++) {
+            final Bullet bullet = mBullets.get(i);
+            bullet.draw(canvas);
+        }
+
+        // Draw obstacles.
+        int numObstacles = mObstacles.size();
+        for (int i = 0; i < numObstacles; i++) {
+            final Obstacle obstacle = mObstacles.get(i);
+            obstacle.draw(canvas);
+        }
+    }
+
+    /**
+     * Uses the device descriptor to try to assign the same color to the same
+     * joystick. If there are two joysticks of the same type connected over USB,
+     * or the API is < API level 16, it will be unable to distinguish the two
+     * devices.
+     *
+     * @param shipID
+     * @return
+     */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    private Ship getShipForId(int shipID) {
+        Ship currentShip = mShips.get(shipID);
+        if (null == currentShip) {
+
+            // do we know something about this ship already?
+            InputDevice dev = InputDevice.getDevice(shipID);
+            String deviceString = null;
+            Integer shipColor = null;
+            if (null != dev) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+                    deviceString = dev.getDescriptor();
+                } else {
+                    deviceString = dev.getName();
+                }
+                shipColor = mDescriptorMap.get(deviceString);
+            }
+
+            if (null != shipColor) {
+                int color = shipColor;
+                int numShips = mShips.size();
+                // do we already have a ship with this color?
+                for (int i = 0; i < numShips; i++) {
+                    if (mShips.valueAt(i).getColor() == color) {
+                        shipColor = null;
+                        // we won't store this value either --- if the first
+                        // controller gets disconnected/connected, it will get
+                        // the same color.
+                        deviceString = null;
+                    }
+                }
+            }
+            if (null != shipColor) {
+                currentShip = new Ship(shipColor);
+                if (null != deviceString) {
+                    mDescriptorMap.remove(deviceString);
+                }
+            } else {
+                currentShip = new Ship(getNextShipColor());
+            }
+            mShips.append(shipID, currentShip);
+            currentShip.setInputDevice(dev);
+
+            if (null != deviceString) {
+                mDescriptorMap.put(deviceString, currentShip.getColor());
+            }
+        }
+        return currentShip;
+    }
+
+    /**
+     * Remove the ship from the array of active ships by ID.
+     *
+     * @param shipID
+     */
+    private void removeShipForID(int shipID) {
+        mShips.remove(shipID);
+    }
+
+    private void reset() {
+        mShips.clear();
+        mBullets.clear();
+        mObstacles.clear();
+        findControllersAndAttachShips();
+    }
+
+    private void animateFrame() {
+        long currentStepTime = SystemClock.uptimeMillis();
+        step(currentStepTime);
+        invalidate();
+    }
+
+    private void step(long currentStepTime) {
+        float tau = (currentStepTime - mLastStepTime) * 0.001f;
+        mLastStepTime = currentStepTime;
+
+        // Move the ships
+        int numShips = mShips.size();
+        for (int i = 0; i < numShips; i++) {
+            Ship currentShip = mShips.valueAt(i);
+            if (currentShip != null) {
+                currentShip.accelerate(tau);
+                if (!currentShip.step(tau)) {
+                    currentShip.reincarnate();
+                }
+            }
+        }
+
+        // Move the bullets.
+        int numBullets = mBullets.size();
+        for (int i = 0; i < numBullets; i++) {
+            final Bullet bullet = mBullets.get(i);
+            if (!bullet.step(tau)) {
+                mBullets.remove(i);
+                i -= 1;
+                numBullets -= 1;
+            }
+        }
+
+        // Move obstacles.
+        int numObstacles = mObstacles.size();
+        for (int i = 0; i < numObstacles; i++) {
+            final Obstacle obstacle = mObstacles.get(i);
+            if (!obstacle.step(tau)) {
+                mObstacles.remove(i);
+                i -= 1;
+                numObstacles -= 1;
+            }
+        }
+
+        // Check for collisions between bullets and obstacles.
+        for (int i = 0; i < numBullets; i++) {
+            final Bullet bullet = mBullets.get(i);
+            for (int j = 0; j < numObstacles; j++) {
+                final Obstacle obstacle = mObstacles.get(j);
+                if (bullet.collidesWith(obstacle)) {
+                    bullet.destroy();
+                    obstacle.destroy();
+                    break;
+                }
+            }
+        }
+
+        // Check for collisions between the ship and obstacles --- this could
+        // get slow
+        for (int i = 0; i < numObstacles; i++) {
+            final Obstacle obstacle = mObstacles.get(i);
+            for (int j = 0; j < numShips; j++) {
+                Ship currentShip = mShips.valueAt(j);
+                if (currentShip != null) {
+                    if (currentShip.collidesWith(obstacle)) {
+                        currentShip.destroy();
+                        obstacle.destroy();
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Spawn more obstacles offscreen when needed.
+        // Avoid putting them right on top of the ship.
+        int tries = MAX_OBSTACLES - mObstacles.size() + 10;
+        final float minDistance = mShipSize * 4;
+        while (mObstacles.size() < MAX_OBSTACLES && tries-- > 0) {
+            float size = mRandom.nextFloat() * (mMaxObstacleSize - mMinObstacleSize)
+                    + mMinObstacleSize;
+            float positionX, positionY;
+            int edge = mRandom.nextInt(4);
+            switch (edge) {
+                case 0:
+                    positionX = -size;
+                    positionY = mRandom.nextInt(getHeight());
+                    break;
+                case 1:
+                    positionX = getWidth() + size;
+                    positionY = mRandom.nextInt(getHeight());
+                    break;
+                case 2:
+                    positionX = mRandom.nextInt(getWidth());
+                    positionY = -size;
+                    break;
+                default:
+                    positionX = mRandom.nextInt(getWidth());
+                    positionY = getHeight() + size;
+                    break;
+            }
+            boolean positionSafe = true;
+
+            // If the obstacle is too close to any ships, we don't want to
+            // spawn it.
+            for (int i = 0; i < numShips; i++) {
+                Ship currentShip = mShips.valueAt(i);
+                if (currentShip != null) {
+                    if (currentShip.distanceTo(positionX, positionY) < minDistance) {
+                        // try to spawn again
+                        positionSafe = false;
+                        break;
+                    }
+                }
+            }
+
+            // if the position is safe, add the obstacle and reset the retry
+            // counter
+            if (positionSafe) {
+                tries = MAX_OBSTACLES - mObstacles.size() + 10;
+                // we can add the obstacle now since it isn't close to any ships
+                float direction = mRandom.nextFloat() * (float) Math.PI * 2;
+                float speed = mRandom.nextFloat() * (mMaxObstacleSpeed - mMinObstacleSpeed)
+                        + mMinObstacleSpeed;
+                float velocityX = (float) Math.cos(direction) * speed;
+                float velocityY = (float) Math.sin(direction) * speed;
+
+                Obstacle obstacle = new Obstacle();
+                obstacle.setPosition(positionX, positionY);
+                obstacle.setSize(size);
+                obstacle.setVelocity(velocityX, velocityY);
+                mObstacles.add(obstacle);
+            }
+        }
+    }
+
+    private static float pythag(float x, float y) {
+        return (float) Math.sqrt(x * x + y * y);
+    }
+
+    private static int blend(float alpha, int from, int to) {
+        return from + (int) ((to - from) * alpha);
+    }
+
+    private static void setPaintARGBBlend(Paint paint, float alpha,
+            int a1, int r1, int g1, int b1,
+            int a2, int r2, int g2, int b2) {
+        paint.setARGB(blend(alpha, a1, a2), blend(alpha, r1, r2),
+                blend(alpha, g1, g2), blend(alpha, b1, b2));
+    }
+
+    private static float getCenteredAxis(MotionEvent event, InputDevice device,
+            int axis, int historyPos) {
+        final InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource());
+        if (range != null) {
+            final float flat = range.getFlat();
+            final float value = historyPos < 0 ? event.getAxisValue(axis)
+                    : event.getHistoricalAxisValue(axis, historyPos);
+
+            // Ignore axis values that are within the 'flat' region of the
+            // joystick axis center.
+            // A joystick at rest does not always report an absolute position of
+            // (0,0).
+            if (Math.abs(value) > flat) {
+                return value;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Any gamepad button + the spacebar or DPAD_CENTER will be used as the fire
+     * key.
+     *
+     * @param keyCode
+     * @return true of it's a fire key.
+     */
+    private static boolean isFireKey(int keyCode) {
+        return KeyEvent.isGamepadButton(keyCode)
+                || keyCode == KeyEvent.KEYCODE_DPAD_CENTER
+                || keyCode == KeyEvent.KEYCODE_SPACE;
+    }
+
+    private abstract class Sprite {
+        protected float mPositionX;
+        protected float mPositionY;
+        protected float mVelocityX;
+        protected float mVelocityY;
+        protected float mSize;
+        protected boolean mDestroyed;
+        protected float mDestroyAnimProgress;
+
+        public void setPosition(float x, float y) {
+            mPositionX = x;
+            mPositionY = y;
+        }
+
+        public void setVelocity(float x, float y) {
+            mVelocityX = x;
+            mVelocityY = y;
+        }
+
+        public void setSize(float size) {
+            mSize = size;
+        }
+
+        public float distanceTo(float x, float y) {
+            return pythag(mPositionX - x, mPositionY - y);
+        }
+
+        public float distanceTo(Sprite other) {
+            return distanceTo(other.mPositionX, other.mPositionY);
+        }
+
+        public boolean collidesWith(Sprite other) {
+            // Really bad collision detection.
+            return !mDestroyed && !other.mDestroyed
+                    && distanceTo(other) <= Math.max(mSize, other.mSize)
+                            + Math.min(mSize, other.mSize) * 0.5f;
+        }
+
+        public boolean isDestroyed() {
+            return mDestroyed;
+        }
+
+        /**
+         * Moves the sprite based on the elapsed time defined by tau.
+         *
+         * @param tau the elapsed time in seconds since the last step
+         * @return false if the sprite is to be removed from the display
+         */
+        public boolean step(float tau) {
+            mPositionX += mVelocityX * tau;
+            mPositionY += mVelocityY * tau;
+
+            if (mDestroyed) {
+                mDestroyAnimProgress += tau / getDestroyAnimDuration();
+                if (mDestroyAnimProgress >= getDestroyAnimCycles()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Draws the sprite.
+         *
+         * @param canvas the Canvas upon which to draw the sprite.
+         */
+        public abstract void draw(Canvas canvas);
+
+        /**
+         * Returns the duration of the destruction animation of the sprite in
+         * seconds.
+         *
+         * @return the float duration in seconds of the destruction animation
+         */
+        public abstract float getDestroyAnimDuration();
+
+        /**
+         * Returns the number of cycles to play the destruction animation. A
+         * destruction animation has a duration and a number of cycles to play
+         * it for, so we can have an extended death sequence when a ship or
+         * object is destroyed.
+         *
+         * @return the float number of cycles to play the destruction animation
+         */
+        public abstract float getDestroyAnimCycles();
+
+        protected boolean isOutsidePlayfield() {
+            final int width = GameView.this.getWidth();
+            final int height = GameView.this.getHeight();
+            return mPositionX < 0 || mPositionX >= width
+                    || mPositionY < 0 || mPositionY >= height;
+        }
+
+        protected void wrapAtPlayfieldBoundary() {
+            final int width = GameView.this.getWidth();
+            final int height = GameView.this.getHeight();
+            while (mPositionX <= -mSize) {
+                mPositionX += width + mSize * 2;
+            }
+            while (mPositionX >= width + mSize) {
+                mPositionX -= width + mSize * 2;
+            }
+            while (mPositionY <= -mSize) {
+                mPositionY += height + mSize * 2;
+            }
+            while (mPositionY >= height + mSize) {
+                mPositionY -= height + mSize * 2;
+            }
+        }
+
+        public void destroy() {
+            mDestroyed = true;
+            step(0);
+        }
+    }
+
+    private static int sShipColor = 0;
+
+    /**
+     * Returns the next ship color in the sequence. Very simple. Does not in any
+     * way guarantee that there are not multiple ships with the same color on
+     * the screen.
+     *
+     * @return an int containing the index of the next ship color
+     */
+    private static int getNextShipColor() {
+        int color = sShipColor & 0x07;
+        if (0 == color) {
+            color++;
+            sShipColor++;
+        }
+        sShipColor++;
+        return color;
+    }
+
+    /*
+     * Static constants associated with Ship inner class
+     */
+    private static final long[] sDestructionVibratePattern = new long[] {
+            0, 20, 20, 40, 40, 80, 40, 300
+    };
+
+    private class Ship extends Sprite {
+        private static final float CORNER_ANGLE = (float) Math.PI * 2 / 3;
+        private static final float TO_DEGREES = (float) (180.0 / Math.PI);
+
+        private final float mMaxShipThrust = mBaseSpeed * 0.25f;
+        private final float mMaxSpeed = mBaseSpeed * 12;
+
+        // The ship actually determines the speed of the bullet, not the bullet
+        // itself
+        private final float mBulletSpeed = mBaseSpeed * 12;
+
+        private final Paint mPaint;
+        private final Path mPath;
+        private final int mR, mG, mB;
+        private final int mColor;
+
+        // The current device that is controlling the ship
+        private InputDevice mInputDevice;
+
+        private float mHeadingX;
+        private float mHeadingY;
+        private float mHeadingAngle;
+        private float mHeadingMagnitude;
+
+        private int mDPadState;
+
+        /**
+         * The colorIndex is used to create the color based on the lower three
+         * bits of the value in the current implementation.
+         *
+         * @param colorIndex
+         */
+        public Ship(int colorIndex) {
+            mPaint = new Paint();
+            mPaint.setStyle(Style.FILL);
+
+            setPosition(getWidth() * 0.5f, getHeight() * 0.5f);
+            setVelocity(0, 0);
+            setSize(mShipSize);
+
+            mPath = new Path();
+            mPath.moveTo(0, 0);
+            mPath.lineTo((float) Math.cos(-CORNER_ANGLE) * mSize,
+                    (float) Math.sin(-CORNER_ANGLE) * mSize);
+            mPath.lineTo(mSize, 0);
+            mPath.lineTo((float) Math.cos(CORNER_ANGLE) * mSize,
+                    (float) Math.sin(CORNER_ANGLE) * mSize);
+            mPath.lineTo(0, 0);
+
+            mR = (colorIndex & 0x01) == 0 ? 63 : 255;
+            mG = (colorIndex & 0x02) == 0 ? 63 : 255;
+            mB = (colorIndex & 0x04) == 0 ? 63 : 255;
+
+            mColor = colorIndex;
+        }
+
+        public boolean onKeyUp(int keyCode, KeyEvent event) {
+
+            // Handle keys going up.
+            boolean handled = false;
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                    setHeadingX(0);
+                    mDPadState &= ~DPAD_STATE_LEFT;
+                    handled = true;
+                    break;
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    setHeadingX(0);
+                    mDPadState &= ~DPAD_STATE_RIGHT;
+                    handled = true;
+                    break;
+                case KeyEvent.KEYCODE_DPAD_UP:
+                    setHeadingY(0);
+                    mDPadState &= ~DPAD_STATE_UP;
+                    handled = true;
+                    break;
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    setHeadingY(0);
+                    mDPadState &= ~DPAD_STATE_DOWN;
+                    handled = true;
+                    break;
+                default:
+                    if (isFireKey(keyCode)) {
+                        handled = true;
+                    }
+                    break;
+            }
+            return handled;
+        }
+
+        /*
+         * Firing is a unique case where a ship creates a bullet. A bullet needs
+         * to be created with a position near the ship that is firing with a
+         * velocity that is based upon the speed of the ship.
+         */
+        private void fire() {
+            if (!isDestroyed()) {
+                Bullet bullet = new Bullet();
+                bullet.setPosition(getBulletInitialX(), getBulletInitialY());
+                bullet.setVelocity(getBulletVelocityX(),
+                        getBulletVelocityY());
+                mBullets.add(bullet);
+                vibrateController(20);
+            }
+        }
+
+        public boolean onKeyDown(int keyCode, KeyEvent event) {
+            // Handle DPad keys and fire button on initial down but not on
+            // auto-repeat.
+            boolean handled = false;
+            if (event.getRepeatCount() == 0) {
+                switch (keyCode) {
+                    case KeyEvent.KEYCODE_DPAD_LEFT:
+                        setHeadingX(-1);
+                        mDPadState |= DPAD_STATE_LEFT;
+                        handled = true;
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_RIGHT:
+                        setHeadingX(1);
+                        mDPadState |= DPAD_STATE_RIGHT;
+                        handled = true;
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_UP:
+                        setHeadingY(-1);
+                        mDPadState |= DPAD_STATE_UP;
+                        handled = true;
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_DOWN:
+                        setHeadingY(1);
+                        mDPadState |= DPAD_STATE_DOWN;
+                        handled = true;
+                        break;
+                    default:
+                        if (isFireKey(keyCode)) {
+                            fire();
+                            handled = true;
+                        }
+                        break;
+                }
+            }
+            return handled;
+        }
+
+        /**
+         * Gets the vibrator from the controller if it is present. Note that it
+         * would be easy to get the system vibrator here if the controller one
+         * is not present, but we don't choose to do it in this case.
+         *
+         * @return the Vibrator for the controller, or null if it is not
+         *         present. or the API level cannot support it
+         */
+        @SuppressLint("NewApi")
+        private final Vibrator getVibrator() {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
+                    null != mInputDevice) {
+                return mInputDevice.getVibrator();
+            }
+            return null;
+        }
+
+        private void vibrateController(int time) {
+            Vibrator vibrator = getVibrator();
+            if (null != vibrator) {
+                vibrator.vibrate(time);
+            }
+        }
+
+        private void vibrateController(long[] pattern, int repeat) {
+            Vibrator vibrator = getVibrator();
+            if (null != vibrator) {
+                vibrator.vibrate(pattern, repeat);
+            }
+        }
+
+        /**
+         * The ship directly handles joystick input.
+         *
+         * @param event
+         * @param historyPos
+         */
+        private void processJoystickInput(MotionEvent event, int historyPos) {
+            // Get joystick position.
+            // Many game pads with two joysticks report the position of the
+            // second
+            // joystick
+            // using the Z and RZ axes so we also handle those.
+            // In a real game, we would allow the user to configure the axes
+            // manually.
+            if (null == mInputDevice) {
+                mInputDevice = event.getDevice();
+            }
+            float x = getCenteredAxis(event, mInputDevice, MotionEvent.AXIS_X, historyPos);
+            if (x == 0) {
+                x = getCenteredAxis(event, mInputDevice, MotionEvent.AXIS_HAT_X, historyPos);
+            }
+            if (x == 0) {
+                x = getCenteredAxis(event, mInputDevice, MotionEvent.AXIS_Z, historyPos);
+            }
+
+            float y = getCenteredAxis(event, mInputDevice, MotionEvent.AXIS_Y, historyPos);
+            if (y == 0) {
+                y = getCenteredAxis(event, mInputDevice, MotionEvent.AXIS_HAT_Y, historyPos);
+            }
+            if (y == 0) {
+                y = getCenteredAxis(event, mInputDevice, MotionEvent.AXIS_RZ, historyPos);
+            }
+
+            // Set the ship heading.
+            setHeading(x, y);
+            GameView.this.step(historyPos < 0 ? event.getEventTime() : event
+                    .getHistoricalEventTime(historyPos));
+        }
+
+        public boolean onGenericMotionEvent(MotionEvent event) {
+            if (0 == mDPadState) {
+                // Process all historical movement samples in the batch.
+                final int historySize = event.getHistorySize();
+                for (int i = 0; i < historySize; i++) {
+                    processJoystickInput(event, i);
+                }
+
+                // Process the current movement sample in the batch.
+                processJoystickInput(event, -1);
+            }
+            return true;
+        }
+
+        /**
+         * Set the game controller to be used to control the ship.
+         *
+         * @param dev the input device that will be controlling the ship
+         */
+        public void setInputDevice(InputDevice dev) {
+            mInputDevice = dev;
+        }
+
+        /**
+         * Sets the X component of the joystick heading value, defined by the
+         * platform as being from -1.0 (left) to 1.0 (right). This function is
+         * generally used to change the heading in response to a button-style
+         * DPAD event.
+         *
+         * @param x the float x component of the joystick heading value
+         */
+        public void setHeadingX(float x) {
+            mHeadingX = x;
+            updateHeading();
+        }
+
+        /**
+         * Sets the Y component of the joystick heading value, defined by the
+         * platform as being from -1.0 (top) to 1.0 (bottom). This function is
+         * generally used to change the heading in response to a button-style
+         * DPAD event.
+         *
+         * @param y the float y component of the joystick heading value
+         */
+        public void setHeadingY(float y) {
+            mHeadingY = y;
+            updateHeading();
+        }
+
+        /**
+         * Sets the heading as floating point values returned by a joystick.
+         * These values are normalized by the Android platform to be from -1.0
+         * (left, top) to 1.0 (right, bottom)
+         *
+         * @param x the float x component of the joystick heading value
+         * @param y the float y component of the joystick heading value
+         */
+        public void setHeading(float x, float y) {
+            mHeadingX = x;
+            mHeadingY = y;
+            updateHeading();
+        }
+
+        /**
+         * Converts the heading values from joystick devices to the polar
+         * representation of the heading angle if the magnitude of the heading
+         * is significant (> 0.1f).
+         */
+        private void updateHeading() {
+            mHeadingMagnitude = pythag(mHeadingX, mHeadingY);
+            if (mHeadingMagnitude > 0.1f) {
+                mHeadingAngle = (float) Math.atan2(mHeadingY, mHeadingX);
+            }
+        }
+
+        /**
+         * Bring our ship back to life, stopping the destroy animation.
+         */
+        public void reincarnate() {
+            mDestroyed = false;
+            mDestroyAnimProgress = 0.0f;
+        }
+
+        private float polarX(float radius) {
+            return (float) Math.cos(mHeadingAngle) * radius;
+        }
+
+        private float polarY(float radius) {
+            return (float) Math.sin(mHeadingAngle) * radius;
+        }
+
+        /**
+         * Gets the initial x coordinate for the bullet.
+         *
+         * @return the x coordinate of the bullet adjusted for the position and
+         *         direction of the ship
+         */
+        public float getBulletInitialX() {
+            return mPositionX + polarX(mSize);
+        }
+
+        /**
+         * Gets the initial y coordinate for the bullet.
+         *
+         * @return the y coordinate of the bullet adjusted for the position and
+         *         direction of the ship
+         */
+        public float getBulletInitialY() {
+            return mPositionY + polarY(mSize);
+        }
+
+        /**
+         * Returns the bullet speed Y component.
+         *
+         * @return adjusted Y component bullet speed for the velocity and
+         *         direction of the ship
+         */
+        public float getBulletVelocityY() {
+            return mVelocityY + polarY(mBulletSpeed);
+        }
+
+        /**
+         * Returns the bullet speed X component
+         *
+         * @return adjusted X component bullet speed for the velocity and
+         *         direction of the ship
+         */
+        public float getBulletVelocityX() {
+            return mVelocityX + polarX(mBulletSpeed);
+        }
+
+        /**
+         * Uses the heading magnitude and direction to change the acceleration
+         * of the ship. In theory, this should be scaled according to the
+         * elapsed time.
+         *
+         * @param tau the elapsed time in seconds between the last step
+         */
+        public void accelerate(float tau) {
+            final float thrust = mHeadingMagnitude * mMaxShipThrust;
+            mVelocityX += polarX(thrust) * tau * mMaxSpeed / 4;
+            mVelocityY += polarY(thrust) * tau * mMaxSpeed / 4;
+
+            final float speed = pythag(mVelocityX, mVelocityY);
+            if (speed > mMaxSpeed) {
+                final float scale = mMaxSpeed / speed;
+                mVelocityX = mVelocityX * scale * scale;
+                mVelocityY = mVelocityY * scale * scale;
+            }
+        }
+
+        @Override
+        public boolean step(float tau) {
+            if (!super.step(tau)) {
+                return false;
+            }
+            wrapAtPlayfieldBoundary();
+            return true;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            setPaintARGBBlend(mPaint, mDestroyAnimProgress - (int) (mDestroyAnimProgress),
+                    255, mR, mG, mB,
+                    0, 255, 0, 0);
+
+            canvas.save(Canvas.MATRIX_SAVE_FLAG);
+            canvas.translate(mPositionX, mPositionY);
+            canvas.rotate(mHeadingAngle * TO_DEGREES);
+            canvas.drawPath(mPath, mPaint);
+            canvas.restore();
+        }
+
+        @Override
+        public float getDestroyAnimDuration() {
+            return 1.0f;
+        }
+
+        @Override
+        public void destroy() {
+            super.destroy();
+            vibrateController(sDestructionVibratePattern, -1);
+        }
+
+        @Override
+        public float getDestroyAnimCycles() {
+            return 5.0f;
+        }
+
+        public int getColor() {
+            return mColor;
+        }
+    }
+
+    private static final Paint mBulletPaint;
+    static {
+        mBulletPaint = new Paint();
+        mBulletPaint.setStyle(Style.FILL);
+    }
+
+    private class Bullet extends Sprite {
+
+        public Bullet() {
+            setSize(mBulletSize);
+        }
+
+        @Override
+        public boolean step(float tau) {
+            if (!super.step(tau)) {
+                return false;
+            }
+            return !isOutsidePlayfield();
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            setPaintARGBBlend(mBulletPaint, mDestroyAnimProgress,
+                    255, 255, 255, 0,
+                    0, 255, 255, 255);
+            canvas.drawCircle(mPositionX, mPositionY, mSize, mBulletPaint);
+        }
+
+        @Override
+        public float getDestroyAnimDuration() {
+            return 0.125f;
+        }
+
+        @Override
+        public float getDestroyAnimCycles() {
+            return 1.0f;
+        }
+
+    }
+
+    private static final Paint mObstaclePaint;
+    static {
+        mObstaclePaint = new Paint();
+        mObstaclePaint.setARGB(255, 127, 127, 255);
+        mObstaclePaint.setStyle(Style.FILL);
+    }
+
+    private class Obstacle extends Sprite {
+
+        @Override
+        public boolean step(float tau) {
+            if (!super.step(tau)) {
+                return false;
+            }
+            wrapAtPlayfieldBoundary();
+            return true;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            setPaintARGBBlend(mObstaclePaint, mDestroyAnimProgress,
+                    255, 127, 127, 255,
+                    0, 255, 0, 0);
+            canvas.drawCircle(mPositionX, mPositionY,
+                    mSize * (1.0f - mDestroyAnimProgress), mObstaclePaint);
+        }
+
+        @Override
+        public float getDestroyAnimDuration() {
+            return 0.25f;
+        }
+
+        @Override
+        public float getDestroyAnimCycles() {
+            return 1.0f;
+        }
+    }
+
+    /*
+     * When an input device is added, we add a ship based upon the device.
+     * @see
+     * com.example.inputmanagercompat.InputManagerCompat.InputDeviceListener
+     * #onInputDeviceAdded(int)
+     */
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+        getShipForId(deviceId);
+    }
+
+    /*
+     * This is an unusual case. Input devices don't typically change, but they
+     * certainly can --- for example a device may have different modes. We use
+     * this to make sure that the ship has an up-to-date InputDevice.
+     * @see
+     * com.example.inputmanagercompat.InputManagerCompat.InputDeviceListener
+     * #onInputDeviceChanged(int)
+     */
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+        Ship ship = getShipForId(deviceId);
+        ship.setInputDevice(InputDevice.getDevice(deviceId));
+    }
+
+    /*
+     * Remove any ship associated with the ID.
+     * @see
+     * com.example.inputmanagercompat.InputManagerCompat.InputDeviceListener
+     * #onInputDeviceRemoved(int)
+     */
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        removeShipForID(deviceId);
+    }
+}
diff --git a/samples/ControllerSample/src/com/example/controllersample/GameViewActivity.java b/samples/ControllerSample/src/com/example/controllersample/GameViewActivity.java
new file mode 100644
index 0000000..aaf8bae
--- /dev/null
+++ b/samples/ControllerSample/src/com/example/controllersample/GameViewActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.controllersample;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class GameViewActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.setContentView(R.layout.game_controller_input);
+    }
+
+}
diff --git a/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerCompat.java b/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerCompat.java
new file mode 100644
index 0000000..fabc876
--- /dev/null
+++ b/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerCompat.java
@@ -0,0 +1,140 @@
+/*
+ * 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.inputmanagercompat;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+public interface InputManagerCompat {
+    /**
+     * Gets information about the input device with the specified id.
+     *
+     * @param id The device id
+     * @return The input device or null if not found
+     */
+    public InputDevice getInputDevice(int id);
+
+    /**
+     * Gets the ids of all input devices in the system.
+     *
+     * @return The input device ids.
+     */
+    public int[] getInputDeviceIds();
+
+    /**
+     * Registers an input device listener to receive notifications about when
+     * input devices are added, removed or changed.
+     *
+     * @param listener The listener to register.
+     * @param handler The handler on which the listener should be invoked, or
+     *            null if the listener should be invoked on the calling thread's
+     *            looper.
+     */
+    public void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener,
+            Handler handler);
+
+    /**
+     * Unregisters an input device listener.
+     *
+     * @param listener The listener to unregister.
+     */
+    public void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener);
+
+    /*
+     * The following three calls are to simulate V16 behavior on pre-Jellybean
+     * devices. If you don't call them, your callback will never be called
+     * pre-API 16.
+     */
+
+    /**
+     * Pass the motion events to the InputManagerCompat. This is used to
+     * optimize for polling for controllers. If you do not pass these events in,
+     * polling will cause regular object creation.
+     *
+     * @param event the motion event from the app
+     */
+    public void onGenericMotionEvent(MotionEvent event);
+
+    /**
+     * Tell the V9 input manager that it should stop polling for disconnected
+     * devices. You can call this during onPause in your activity, although you
+     * might want to call it whenever your game is not active (or whenever you
+     * don't care about being notified of new input devices)
+     */
+    public void onPause();
+
+    /**
+     * Tell the V9 input manager that it should start polling for disconnected
+     * devices. You can call this during onResume in your activity, although you
+     * might want to call it less often (only when the gameplay is actually
+     * active)
+     */
+    public void onResume();
+
+    public interface InputDeviceListener {
+        /**
+         * Called whenever the input manager detects that a device has been
+         * added. This will only be called in the V9 version when a motion event
+         * is detected.
+         *
+         * @param deviceId The id of the input device that was added.
+         */
+        void onInputDeviceAdded(int deviceId);
+
+        /**
+         * Called whenever the properties of an input device have changed since
+         * they were last queried. This will not be called for the V9 version of
+         * the API.
+         *
+         * @param deviceId The id of the input device that changed.
+         */
+        void onInputDeviceChanged(int deviceId);
+
+        /**
+         * Called whenever the input manager detects that a device has been
+         * removed. For the V9 version, this can take some time depending on the
+         * poll rate.
+         *
+         * @param deviceId The id of the input device that was removed.
+         */
+        void onInputDeviceRemoved(int deviceId);
+    }
+
+    /**
+     * Use this to construct a compatible InputManager.
+     */
+    public static class Factory {
+
+        /**
+         * Constructs and returns a compatible InputManger
+         *
+         * @param context the Context that will be used to get the system
+         *            service from
+         * @return a compatible implementation of InputManager
+         */
+        public static InputManagerCompat getInputManager(Context context) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+                return new InputManagerV16(context);
+            } else {
+                return new InputManagerV9();
+            }
+        }
+    }
+}
diff --git a/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerV16.java b/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerV16.java
new file mode 100644
index 0000000..d26581e
--- /dev/null
+++ b/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerV16.java
@@ -0,0 +1,107 @@
+/*
+ * 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.inputmanagercompat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.Build;
+import android.os.Handler;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class InputManagerV16 implements InputManagerCompat {
+
+    private final InputManager mInputManager;
+    private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners;
+
+    public InputManagerV16(Context context) {
+        mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+        mListeners = new HashMap<InputManagerCompat.InputDeviceListener, V16InputDeviceListener>();
+    }
+
+    @Override
+    public InputDevice getInputDevice(int id) {
+        return mInputManager.getInputDevice(id);
+    }
+
+    @Override
+    public int[] getInputDeviceIds() {
+        return mInputManager.getInputDeviceIds();
+    }
+
+    static class V16InputDeviceListener implements InputManager.InputDeviceListener {
+        final InputManagerCompat.InputDeviceListener mIDL;
+
+        public V16InputDeviceListener(InputDeviceListener idl) {
+            mIDL = idl;
+        }
+
+        @Override
+        public void onInputDeviceAdded(int deviceId) {
+            mIDL.onInputDeviceAdded(deviceId);
+        }
+
+        @Override
+        public void onInputDeviceChanged(int deviceId) {
+            mIDL.onInputDeviceChanged(deviceId);
+        }
+
+        @Override
+        public void onInputDeviceRemoved(int deviceId) {
+            mIDL.onInputDeviceRemoved(deviceId);
+        }
+
+    }
+
+    @Override
+    public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
+        V16InputDeviceListener v16Listener = new V16InputDeviceListener(listener);
+        mInputManager.registerInputDeviceListener(v16Listener, handler);
+        mListeners.put(listener, v16Listener);
+    }
+
+    @Override
+    public void unregisterInputDeviceListener(InputDeviceListener listener) {
+        V16InputDeviceListener curListener = mListeners.remove(listener);
+        if (null != curListener)
+        {
+            mInputManager.unregisterInputDeviceListener(curListener);
+        }
+
+    }
+
+    @Override
+    public void onGenericMotionEvent(MotionEvent event) {
+        // unused in V16
+    }
+
+    @Override
+    public void onPause() {
+        // unused in V16
+    }
+
+    @Override
+    public void onResume() {
+        // unused in V16
+    }
+
+}
diff --git a/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerV9.java b/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerV9.java
new file mode 100644
index 0000000..dcd8988
--- /dev/null
+++ b/samples/ControllerSample/src/com/example/inputmanagercompat/InputManagerV9.java
@@ -0,0 +1,211 @@
+/*
+ * 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.inputmanagercompat;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+
+public class InputManagerV9 implements InputManagerCompat {
+    private static final String LOG_TAG = "InputManagerV9";
+    private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
+    private static final long CHECK_ELAPSED_TIME = 3000L;
+
+    private static final int ON_DEVICE_ADDED = 0;
+    private static final int ON_DEVICE_CHANGED = 1;
+    private static final int ON_DEVICE_REMOVED = 2;
+
+    private final SparseArray<long[]> mDevices;
+    private final Map<InputDeviceListener, Handler> mListeners;
+    private final Handler mDefaultHandler;
+
+    private static class PollingMessageHandler extends Handler {
+        private final WeakReference<InputManagerV9> mInputManager;
+
+        PollingMessageHandler(InputManagerV9 im) {
+            mInputManager = new WeakReference<InputManagerV9>(im);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+            switch (msg.what) {
+                case MESSAGE_TEST_FOR_DISCONNECT:
+                    InputManagerV9 imv = mInputManager.get();
+                    if (null != imv) {
+                        long time = SystemClock.elapsedRealtime();
+                        int size = imv.mDevices.size();
+                        for (int i = 0; i < size; i++) {
+                            long[] lastContact = imv.mDevices.valueAt(i);
+                            if (null != lastContact) {
+                                if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
+                                    // check to see if the device has been
+                                    // disconnected
+                                    int id = imv.mDevices.keyAt(i);
+                                    if (null == InputDevice.getDevice(id)) {
+                                        // disconnected!
+                                        imv.notifyListeners(ON_DEVICE_REMOVED, id);
+                                        imv.mDevices.remove(id);
+                                    } else {
+                                        lastContact[0] = time;
+                                    }
+                                }
+                            }
+                        }
+                        sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
+                                CHECK_ELAPSED_TIME);
+                    }
+                    break;
+            }
+        }
+
+    }
+
+    public InputManagerV9() {
+        mDevices = new SparseArray<long[]>();
+        mListeners = new HashMap<InputDeviceListener, Handler>();
+        mDefaultHandler = new PollingMessageHandler(this);
+        // as a side-effect, populates our collection of watched
+        // input devices
+        getInputDeviceIds();
+    }
+
+    @Override
+    public InputDevice getInputDevice(int id) {
+        return InputDevice.getDevice(id);
+    }
+
+    @Override
+    public int[] getInputDeviceIds() {
+        // add any hitherto unknown devices to our
+        // collection of watched input devices
+        int[] activeDevices = InputDevice.getDeviceIds();
+        long time = SystemClock.elapsedRealtime();
+        for ( int id : activeDevices ) {
+            long[] lastContact = mDevices.get(id);
+            if ( null == lastContact ) {
+                // we have a new device
+                mDevices.put(id, new long[] { time });
+            }
+        }
+        return activeDevices;
+    }
+
+    @Override
+    public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
+        mListeners.remove(listener);
+        if (handler == null) {
+            handler = mDefaultHandler;
+        }
+        mListeners.put(listener, handler);
+    }
+
+    @Override
+    public void unregisterInputDeviceListener(InputDeviceListener listener) {
+        mListeners.remove(listener);
+    }
+
+    private void notifyListeners(int why, int deviceId) {
+        // the state of some device has changed
+        if (!mListeners.isEmpty()) {
+            // yes... this will cause an object to get created... hopefully
+            // it won't happen very often
+            for (InputDeviceListener listener : mListeners.keySet()) {
+                Handler handler = mListeners.get(listener);
+                DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId, listener);
+                handler.post(odc);
+            }
+        }
+    }
+
+    private static class DeviceEvent implements Runnable {
+        private int mMessageType;
+        private int mId;
+        private InputDeviceListener mListener;
+        private static Queue<DeviceEvent> sEventQueue = new ArrayDeque<DeviceEvent>();
+
+        private DeviceEvent() {
+        }
+
+        static DeviceEvent getDeviceEvent(int messageType, int id,
+                InputDeviceListener listener) {
+            DeviceEvent curChanged = sEventQueue.poll();
+            if (null == curChanged) {
+                curChanged = new DeviceEvent();
+            }
+            curChanged.mMessageType = messageType;
+            curChanged.mId = id;
+            curChanged.mListener = listener;
+            return curChanged;
+        }
+
+        @Override
+        public void run() {
+            switch (mMessageType) {
+                case ON_DEVICE_ADDED:
+                    mListener.onInputDeviceAdded(mId);
+                    break;
+                case ON_DEVICE_CHANGED:
+                    mListener.onInputDeviceChanged(mId);
+                    break;
+                case ON_DEVICE_REMOVED:
+                    mListener.onInputDeviceRemoved(mId);
+                    break;
+                default:
+                    Log.e(LOG_TAG, "Unknown Message Type");
+                    break;
+            }
+            // dump this runnable back in the queue
+            sEventQueue.offer(this);
+        }
+    }
+
+    @Override
+    public void onGenericMotionEvent(MotionEvent event) {
+        // detect new devices
+        int id = event.getDeviceId();
+        long[] timeArray = mDevices.get(id);
+        if (null == timeArray) {
+            notifyListeners(ON_DEVICE_ADDED, id);
+            timeArray = new long[1];
+            mDevices.put(id, timeArray);
+        }
+        long time = SystemClock.elapsedRealtime();
+        timeArray[0] = time;
+    }
+
+    @Override
+    public void onPause() {
+        mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
+    }
+
+    @Override
+    public void onResume() {
+        mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT);
+    }
+
+}
diff --git a/samples/Support4Demos/AndroidManifest.xml b/samples/Support4Demos/AndroidManifest.xml
index 46a4831..5b43488 100644
--- a/samples/Support4Demos/AndroidManifest.xml
+++ b/samples/Support4Demos/AndroidManifest.xml
@@ -305,6 +305,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".widget.SwipeRefreshLayoutActivity"
+                  android:label="@string/swipe"
+                  android:theme="@style/ThemeHoloLight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv4.SUPPORT4_SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".widget.ContentLoadingProgressBarActivity"
                   android:label="@string/content_loading_progress_bar">
             <intent-filter>
diff --git a/samples/Support4Demos/res/drawable-hdpi/refresh.png b/samples/Support4Demos/res/drawable-hdpi/refresh.png
new file mode 100644
index 0000000..d65c15f
--- /dev/null
+++ b/samples/Support4Demos/res/drawable-hdpi/refresh.png
Binary files differ
diff --git a/samples/Support4Demos/res/drawable-mdpi/refresh.png b/samples/Support4Demos/res/drawable-mdpi/refresh.png
new file mode 100644
index 0000000..dc96718
--- /dev/null
+++ b/samples/Support4Demos/res/drawable-mdpi/refresh.png
Binary files differ
diff --git a/samples/Support4Demos/res/drawable-xhdpi/refresh.png b/samples/Support4Demos/res/drawable-xhdpi/refresh.png
new file mode 100644
index 0000000..47da13e
--- /dev/null
+++ b/samples/Support4Demos/res/drawable-xhdpi/refresh.png
Binary files differ
diff --git a/samples/Support4Demos/res/layout/swipe_refresh_widget_sample.xml b/samples/Support4Demos/res/layout/swipe_refresh_widget_sample.xml
new file mode 100644
index 0000000..698024e
--- /dev/null
+++ b/samples/Support4Demos/res/layout/swipe_refresh_widget_sample.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/swipe_refresh_widget"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <!-- some full screen pullable view that will be the offsetable content -->
+    <ListView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/content"/>
+</android.support.v4.widget.SwipeRefreshLayout>
\ No newline at end of file
diff --git a/samples/Support4Demos/res/menu/swipe_refresh_menu.xml b/samples/Support4Demos/res/menu/swipe_refresh_menu.xml
new file mode 100644
index 0000000..214c637
--- /dev/null
+++ b/samples/Support4Demos/res/menu/swipe_refresh_menu.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/force_refresh"
+        android:showAsAction="ifRoom"
+        android:icon="@drawable/refresh"
+        android:title="Refresh" />
+</menu>
\ No newline at end of file
diff --git a/samples/Support4Demos/res/values-v11/styles.xml b/samples/Support4Demos/res/values-v11/styles.xml
index 04c6f3f..c21e6d8 100644
--- a/samples/Support4Demos/res/values-v11/styles.xml
+++ b/samples/Support4Demos/res/values-v11/styles.xml
@@ -19,6 +19,10 @@
     <style name="ThemeHolo" parent="android:Theme.Holo">
     </style>
 
+    <!-- For API level 11 or later, the Holo theme is available and we prefer that. -->
+    <style name="ThemeHoloLight" parent="android:Theme.Holo.Light">
+    </style>
+
     <!-- For API level 11 or later, we can use the magical DialogWhenLarge theme. -->
     <style name="ThemeDialogWhenLarge" parent="android:style/Theme.Holo.DialogWhenLarge">
     </style>
diff --git a/samples/Support4Demos/res/values/colors.xml b/samples/Support4Demos/res/values/colors.xml
index a52502e..ce3d633 100644
--- a/samples/Support4Demos/res/values/colors.xml
+++ b/samples/Support4Demos/res/values/colors.xml
@@ -18,5 +18,9 @@
     <drawable name="red">#7f00</drawable>
     <drawable name="blue">#770000ff</drawable>
     <drawable name="green">#7700ff00</drawable>
-	<drawable name="yellow">#77ffff00</drawable>
+    <drawable name="yellow">#77ffff00</drawable>
+    <color name="color1">#ff0f9d58</color>
+    <color name="color2">#ffdb4437</color>
+    <color name="color3">#ff4285f4</color>
+    <color name="color4">#fff4b400</color>
 </resources>
diff --git a/samples/Support4Demos/res/values/strings.xml b/samples/Support4Demos/res/values/strings.xml
index ce41aaf..91b5c42 100644
--- a/samples/Support4Demos/res/values/strings.xml
+++ b/samples/Support4Demos/res/values/strings.xml
@@ -160,7 +160,13 @@
 
     <string name="drawer_layout_summary">This activity illustrates the use of sliding drawers. The drawer may be pulled out from the starting edge, which is left on left-to-right locales, with an edge swipe. If this demo is running on Ice Cream Sandwich or newer you may tap the icon at the starting side of the action bar to open the drawer as well.</string>
 
+    <!-- Title of the navigation drawer, used by accessibility to announce state changes. -->
+    <string name="drawer_title">Navigation</string>
+
+    <!-- Description of the icon that opens the navigation drawer, used by accessibility. -->
     <string name="drawer_open">Open navigation drawer</string>
+
+    <!-- Description of the icon that closes the navigation drawer, used by accessibility. -->
     <string name="drawer_close">Close navigation drawer</string>
 
     <string name="sliding_pane_layout_support">Widget/Sliding pane layout</string>
@@ -176,4 +182,6 @@
     <!-- ContentLoadingProgressBar -->
     <string name="content_loading_progress_bar">Widget/Content Loading Progress Bar</string>
 
+    <!--  Swipe refresh -->
+    <string name="swipe">Widget/SwipeRefreshLayout</string>
 </resources>
diff --git a/samples/Support4Demos/res/values/styles.xml b/samples/Support4Demos/res/values/styles.xml
index 97cdb6f..689555b 100644
--- a/samples/Support4Demos/res/values/styles.xml
+++ b/samples/Support4Demos/res/values/styles.xml
@@ -21,7 +21,14 @@
          selected when the holographic theme is available. -->
     <style name="ThemeHolo" parent="android:Theme">
     </style>
-    
+
+    <!-- This is a theme that will adjust itself depending on the API version.
+         The default definition is the safe one, using a theme that has always
+         been defined.  Look at values-11/styles.xml for a variation that is
+         selected when the holographic theme is available. -->
+    <style name="ThemeHoloLight" parent="android:Theme.Light">
+    </style>
+
     <!-- Older platforms don't have Theme.Holo.DialogWhenLarge; we will define
          our own wrapper theme that uses it only when running on the appropriate
          platform version.  On older platforms, we always use the generic
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/DrawerLayoutActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/DrawerLayoutActivity.java
index 7b88faa..b8735b4 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/DrawerLayoutActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/DrawerLayoutActivity.java
@@ -89,6 +89,11 @@
         mDrawerLayout.setDrawerListener(new DemoDrawerListener());
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
 
+        // The drawer title must be set in order to announce state changes when
+        // accessibility is turned on. This is typically a simple description,
+        // e.g. "Navigation".
+        mDrawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.drawer_title));
+
         mDrawer.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
                 Shakespeare.TITLES));
         mDrawer.setOnItemClickListener(new DrawerItemClickListener());
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/SwipeRefreshLayoutActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/SwipeRefreshLayoutActivity.java
new file mode 100644
index 0000000..7a0543c
--- /dev/null
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/SwipeRefreshLayoutActivity.java
@@ -0,0 +1,139 @@
+/*
+ * 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.supportv4.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.example.android.supportv4.R;
+import com.example.android.supportv4.Shakespeare;
+
+import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
+
+/**
+ * Example of using the SwipeRefreshLayout.
+ */
+public class SwipeRefreshLayoutActivity extends Activity implements OnRefreshListener {
+    public static final String[] TITLES =
+    {
+            "Henry IV (1)",
+            "Henry V",
+            "Henry VIII",
+            "Richard II",
+            "Richard III",
+            "Merchant of Venice",
+            "Othello",
+            "King Lear",
+            "Henry IV (1)",
+            "Henry V",
+            "Henry VIII",
+            "Richard II",
+            "Richard III",
+            "Merchant of Venice",
+            "Othello",
+            "King Lear",
+            "Henry IV (1)",
+            "Henry V",
+            "Henry VIII",
+            "Richard II",
+            "Richard III",
+            "Merchant of Venice",
+            "Othello",
+            "King Lear",
+            "Henry IV (1)",
+            "Henry V",
+            "Henry VIII",
+            "Richard II",
+            "Richard III",
+            "Merchant of Venice",
+            "Othello",
+            "King Lear"
+    };
+    // Try a SUPER quick refresh to make sure we don't get extra refreshes
+    // while the user's finger is still down.
+    private static final boolean SUPER_QUICK_REFRESH = false;
+    private View mContent;
+    private SwipeRefreshLayout mSwipeRefreshWidget;
+    private ListView mList;
+    private Handler mHandler = new Handler();
+    private final Runnable mRefreshDone = new Runnable() {
+
+        @Override
+        public void run() {
+            mSwipeRefreshWidget.setRefreshing(false);
+        }
+
+    };
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        setContentView(R.layout.swipe_refresh_widget_sample);
+        mSwipeRefreshWidget = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_widget);
+        mSwipeRefreshWidget.setColorScheme(R.color.color1, R.color.color2, R.color.color3,
+                R.color.color4);
+        mList = (ListView) findViewById(R.id.content);
+        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, android.R.id.text1, TITLES);
+        mList.setAdapter(arrayAdapter);
+        mSwipeRefreshWidget.setOnRefreshListener(this);
+    }
+
+    @Override
+    public void onRefresh() {
+        refresh();
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.swipe_refresh_menu, menu);
+        return true;
+    }
+
+    /**
+     * Click handler for the menu item to force a refresh.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int id = item.getItemId();
+        switch(id) {
+            case R.id.force_refresh:
+                mSwipeRefreshWidget.setRefreshing(true);
+                refresh();
+                return true;
+        }
+        return false;
+    }
+
+    private void refresh() {
+        mHandler.removeCallbacks(mRefreshDone);
+        mHandler.postDelayed(mRefreshDone, 1000);
+    }
+}
\ No newline at end of file
diff --git a/samples/Support7Demos/Android.mk b/samples/Support7Demos/Android.mk
index 0e43e3d..ca8310f 100644
--- a/samples/Support7Demos/Android.mk
+++ b/samples/Support7Demos/Android.mk
@@ -26,7 +26,8 @@
         android-support-v4 \
         android-support-v7-appcompat \
         android-support-v7-gridlayout \
-        android-support-v7-mediarouter
+        android-support-v7-mediarouter \
+        android-support-v7-recyclerview
 LOCAL_RESOURCE_DIR = \
         $(LOCAL_PATH)/res \
         frameworks/support/v7/appcompat/res \
diff --git a/samples/Support7Demos/AndroidManifest.xml b/samples/Support7Demos/AndroidManifest.xml
index 4735433..0e9ed0b 100644
--- a/samples/Support7Demos/AndroidManifest.xml
+++ b/samples/Support7Demos/AndroidManifest.xml
@@ -38,6 +38,7 @@
             android:compatibleWidthLimitDp="480" />
 
     <application android:label="@string/activity_sample_code"
+            android:supportsRtl="true"
             android:icon="@drawable/app_sample_code"
             android:hardwareAccelerated="true">
 
@@ -172,5 +173,24 @@
             </intent-filter>
         </activity>
 
+        <!-- RecyclerView samples -->
+        <activity android:name=".widget.RecyclerViewActivity"
+                android:label="@string/recycler_view"
+                android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".widget.LinearLayoutManagerActivity"
+                  android:label="@string/linear_layout_manager"
+                  android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/samples/Support7Demos/res/layout/activity_linear_layout_manager.xml b/samples/Support7Demos/res/layout/activity_linear_layout_manager.xml
new file mode 100644
index 0000000..f4afe95
--- /dev/null
+++ b/samples/Support7Demos/res/layout/activity_linear_layout_manager.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <Spinner
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/spinner"/>
+    <android.support.v7.widget.RecyclerView
+            android:layout_width="fill_parent"
+            android:layout_height="100dp"
+            android:id="@+id/config_recycler_view"/>
+    <android.support.v7.widget.RecyclerView
+            android:scrollbarStyle="insideOverlay"
+            android:scrollbars="horizontal|vertical"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:id="@+id/recycler_view"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/Support7Demos/res/values/strings.xml b/samples/Support7Demos/res/values/strings.xml
index 1b13623..5852d0a 100644
--- a/samples/Support7Demos/res/values/strings.xml
+++ b/samples/Support7Demos/res/values/strings.xml
@@ -95,4 +95,10 @@
     <string name="sample_media_route_activity_local">Local Playback</string>
     <string name="sample_media_route_activity_presentation">Local Playback on Presentation Display</string>
 
+    <string name="recycler_view">RecyclerView</string>
+    <string name="linear_layout_manager">Linear Layout Manager</string>
+    <string name="checkbox_orientation">Horz.</string>
+    <string name="checkbox_reverse">Rev.</string>
+    <string name="checkbox_layout_dir">Layout Dir</string>
+    <string name="checkbox_stack_from_end">Stack From End</string>
 </resources>
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/Cheeses.java b/samples/Support7Demos/src/com/example/android/supportv7/Cheeses.java
new file mode 100644
index 0000000..a66b1d6
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/Cheeses.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2011 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.supportv7;
+
+public class Cheeses {
+
+    public static final String[] sCheeseStrings = {
+            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+            "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+            "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+            "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+            "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+            "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+            "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+            "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+            "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+            "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+            "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+            "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+            "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+            "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+            "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+            "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+            "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+            "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+            "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+            "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+            "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+            "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+            "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+            "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+            "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+            "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+            "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+            "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+            "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+            "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+            "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+            "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+            "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+            "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+            "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+            "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+            "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+            "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+            "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+            "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+            "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+            "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+            "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+            "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+            "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+            "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+            "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+            "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+            "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+            "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+            "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+            "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+            "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+            "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+            "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+            "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+            "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+            "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+            "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+            "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+            "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+            "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+            "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+            "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+            "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+            "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+            "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+            "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+            "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+            "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+            "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+            "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+            "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+            "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+            "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+            "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+            "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+            "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+            "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+            "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+            "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+            "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+            "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+            "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+            "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+            "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+            "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+            "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+            "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+            "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+            "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+            "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+            "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+            "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+            "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+            "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+            "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+            "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+            "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+            "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+            "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+            "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+            "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+            "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+            "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+            "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+            "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+            "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+            "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+            "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+    };
+
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java
new file mode 100644
index 0000000..eb49cff
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014 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.supportv7.widget;
+
+import com.example.android.supportv7.Cheeses;
+import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
+import com.example.android.supportv7.widget.decorator.DividerItemDecoration;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.example.android.supportv7.R;
+
+/**
+ * A sample activity that uses {@link android.support.v7.widget.LinearLayoutManager}.
+ */
+public class LinearLayoutManagerActivity extends Activity {
+
+    private LinearLayoutManager mListLayoutManager;
+
+    private RecyclerView mRecyclerView;
+
+    private DividerItemDecoration mDividerItemDecoration;
+
+    private ConfigToggle[] mConfigToggles;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_linear_layout_manager);
+        initConfig();
+        initRecyclerView();
+        initSpinner();
+    }
+
+    private void initRecyclerView() {
+        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+        mRecyclerView.setHasFixedSize(true);
+        mListLayoutManager = new LinearLayoutManager(this);
+        mRecyclerView.setLayoutManager(mListLayoutManager);
+        mRecyclerView.setAdapter(new SimpleStringAdapter(this, Cheeses.sCheeseStrings));
+        mDividerItemDecoration = new DividerItemDecoration(this,
+                mListLayoutManager.getOrientation());
+        mRecyclerView.addItemDecoration(mDividerItemDecoration);
+    }
+
+    private void initConfig() {
+        RecyclerView configView = (RecyclerView) findViewById(R.id.config_recycler_view);
+        initToggles();
+        configView.setAdapter(mConfigAdapter);
+        configView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,
+                false));
+        configView.setHasFixedSize(true);
+    }
+
+    private void initSpinner() {
+        Spinner spinner = (Spinner) findViewById(R.id.spinner);
+        spinner.setAdapter(new BaseAdapter() {
+            @Override
+            public int getCount() {
+                return mRecyclerView.getAdapter().getItemCount();
+            }
+
+            @Override
+            public Integer getItem(int position) {
+                return position;
+            }
+
+            @Override
+            public long getItemId(int position) {
+                return position;
+            }
+
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+                if(convertView == null) {
+                    convertView = new TextView(parent.getContext());
+                }
+                ((TextView) convertView).setText("" + position);
+                return convertView;
+            }
+        });
+        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                mRecyclerView.scrollToPosition(position);
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+
+            }
+        });
+    }
+
+    private void initToggles() {
+        mConfigToggles = new ConfigToggle[]{
+                new ConfigToggle(R.string.checkbox_orientation) {
+                    @Override
+                    public boolean isChecked() {
+                        return mListLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL;
+                    }
+
+                    @Override
+                    public void onChange(boolean newValue) {
+                        mListLayoutManager.setOrientation(newValue ? LinearLayoutManager.HORIZONTAL
+                                : LinearLayoutManager.VERTICAL);
+                        mDividerItemDecoration.setOrientation(mListLayoutManager.getOrientation());
+                    }
+                },
+                new ConfigToggle(R.string.checkbox_reverse) {
+                    @Override
+                    public boolean isChecked() {
+                        return mListLayoutManager.getReverseLayout();
+                    }
+
+                    @Override
+                    public void onChange(boolean newValue) {
+                        mListLayoutManager.setReverseLayout(newValue);
+                    }
+                },
+                new ConfigToggle(R.string.checkbox_layout_dir) {
+                    @Override
+                    public boolean isChecked() {
+                        return ViewCompat.getLayoutDirection(mRecyclerView) ==
+                                ViewCompat.LAYOUT_DIRECTION_RTL;
+                    }
+
+                    @Override
+                    public void onChange(boolean newValue) {
+                        ViewCompat.setLayoutDirection(mRecyclerView, newValue ?
+                                ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);
+                    }
+                },
+                new ConfigToggle(R.string.checkbox_stack_from_end) {
+                    @Override
+                    public boolean isChecked() {
+                        return mListLayoutManager.getStackFromEnd();
+                    }
+
+                    @Override
+                    public void onChange(boolean newValue) {
+                        mListLayoutManager.setStackFromEnd(newValue);
+                    }
+                }
+        };
+    }
+
+    private class ConfigViewHolder extends RecyclerView.ViewHolder
+            implements CompoundButton.OnCheckedChangeListener {
+
+        private CheckBox mCheckBox;
+
+        private ConfigToggle mConfigToggle;
+
+        public ConfigViewHolder(View itemView) {
+            super(itemView);
+            mCheckBox = (CheckBox) itemView;
+            mCheckBox.setOnCheckedChangeListener(this);
+        }
+
+        public void render(ConfigToggle toggle) {
+            mConfigToggle = toggle;
+            mCheckBox.setText(toggle.getText());
+            mCheckBox.setChecked(toggle.isChecked());
+        }
+
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            if (mConfigToggle != null) {
+                mConfigToggle.onChange(isChecked);
+            }
+        }
+    }
+
+
+    private abstract class ConfigToggle {
+
+        private String mLabel;
+
+        protected ConfigToggle(int labelId) {
+            mLabel = getResources().getString(labelId);
+        }
+
+        public String getText() {
+            return mLabel;
+        }
+
+        abstract public boolean isChecked();
+
+        abstract public void onChange(boolean newValue);
+    }
+
+
+
+    private RecyclerView.Adapter mConfigAdapter = new RecyclerView.Adapter<ConfigViewHolder>() {
+        @Override
+        public ConfigViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new ConfigViewHolder(new CheckBox(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(ConfigViewHolder holder, int position) {
+            ConfigToggle toggle = mConfigToggles[position];
+            holder.render(toggle);
+        }
+
+        @Override
+        public int getItemCount() {
+            return mConfigToggles.length;
+        }
+    };
+
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java
new file mode 100644
index 0000000..223da65
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java
@@ -0,0 +1,281 @@
+/*
+ * 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.supportv7.widget;
+
+import com.example.android.supportv7.Cheeses;
+import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
+import com.example.android.supportv7.widget.decorator.DividerItemDecoration;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.widget.RecyclerView;
+import android.util.DisplayMetrics;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class RecyclerViewActivity extends Activity {
+
+    private static final String TAG = "RecyclerViewActivity";
+
+    private RecyclerView mRecyclerView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final RecyclerView rv = new RecyclerView(this);
+        rv.setLayoutManager(new MyLayoutManager(this));
+        rv.setHasFixedSize(true);
+        rv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        rv.setAdapter(new SimpleStringAdapter(this, Cheeses.sCheeseStrings) {
+            @Override
+            public SimpleStringAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+                    int viewType) {
+                final SimpleStringAdapter.ViewHolder vh = super
+                        .onCreateViewHolder(parent, viewType);
+                vh.itemView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        final int pos = vh.getPosition();
+                        if (pos + 1 < getItemCount()) {
+                            swap(pos, pos + 1);
+                        }
+                    }
+                });
+                return vh;
+            }
+        });
+        rv.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
+        setContentView(rv);
+        mRecyclerView = rv;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        MenuItemCompat.setShowAsAction(menu.add("Layout"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        mRecyclerView.requestLayout();
+        return super.onOptionsItemSelected(item);
+    }
+
+    private static final int SCROLL_DISTANCE = 80; // dp
+
+    /**
+     * A basic ListView-style LayoutManager.
+     */
+    class MyLayoutManager extends RecyclerView.LayoutManager {
+
+        private static final String TAG = "MyLayoutManager";
+
+        private int mFirstPosition;
+
+        private final int mScrollDistance;
+
+        public MyLayoutManager(Context c) {
+            final DisplayMetrics dm = c.getResources().getDisplayMetrics();
+            mScrollDistance = (int) (SCROLL_DISTANCE * dm.density + 0.5f);
+        }
+
+        @Override
+        public void layoutChildren(RecyclerView.Adapter adapter, RecyclerView.Recycler recycler,
+                boolean structureChanged) {
+            final int parentBottom = getHeight() - getPaddingBottom();
+
+            final View oldTopView = getChildCount() > 0 ? getChildAt(0) : null;
+            int oldTop = getPaddingTop();
+            if (oldTopView != null) {
+                oldTop = oldTopView.getTop();
+            }
+
+            detachAndScrapAttachedViews(recycler);
+
+            int top = oldTop;
+            int bottom;
+            final int left = getPaddingLeft();
+            final int right = getWidth() - getPaddingRight();
+
+            final int count = adapter.getItemCount();
+            for (int i = 0; mFirstPosition + i < count && top < parentBottom; i++, top = bottom) {
+                View v = recycler.getViewForPosition(adapter, mFirstPosition + i);
+                addView(v, i);
+                measureChildWithMargins(v, 0, 0);
+                bottom = top + getDecoratedMeasuredHeight(v);
+                layoutDecorated(v, left, top, right, bottom);
+            }
+
+            removeAndRecycleScrap(recycler);
+        }
+
+        @Override
+        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
+            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        @Override
+        public boolean canScrollVertically() {
+            return true;
+        }
+
+        @Override
+        public int scrollVerticallyBy(int dy, RecyclerView.Adapter adapter,
+                RecyclerView.Recycler recycler) {
+            if (getChildCount() == 0) {
+                return 0;
+            }
+
+            int scrolled = 0;
+            final int left = getPaddingLeft();
+            final int right = getWidth() - getPaddingRight();
+            if (dy < 0) {
+                while (scrolled > dy) {
+                    final View topView = getChildAt(0);
+                    final int hangingTop = Math.max(-getDecoratedTop(topView), 0);
+                    final int scrollBy = Math.min(scrolled - dy, hangingTop);
+                    scrolled -= scrollBy;
+                    offsetChildrenVertical(scrollBy);
+                    if (mFirstPosition > 0 && scrolled > dy) {
+                        mFirstPosition--;
+                        View v = recycler.getViewForPosition(adapter, mFirstPosition);
+                        addView(v, 0);
+                        measureChildWithMargins(v, 0, 0);
+                        final int bottom = getDecoratedTop(topView);
+                        final int top = bottom - getDecoratedMeasuredHeight(v);
+                        layoutDecorated(v, left, top, right, bottom);
+                    } else {
+                        break;
+                    }
+                }
+            } else if (dy > 0) {
+                final int parentHeight = getHeight();
+                while (scrolled < dy) {
+                    final View bottomView = getChildAt(getChildCount() - 1);
+                    final int hangingBottom =
+                            Math.max(getDecoratedBottom(bottomView) - parentHeight, 0);
+                    final int scrollBy = -Math.min(dy - scrolled, hangingBottom);
+                    scrolled -= scrollBy;
+                    offsetChildrenVertical(scrollBy);
+                    if (scrolled < dy && getItemCount() > mFirstPosition + getChildCount()) {
+                        View v = recycler.getViewForPosition(adapter,
+                                mFirstPosition + getChildCount());
+                        final int top = getDecoratedBottom(getChildAt(getChildCount() - 1));
+                        addView(v);
+                        measureChildWithMargins(v, 0, 0);
+                        final int bottom = top + getDecoratedMeasuredHeight(v);
+                        layoutDecorated(v, left, top, right, bottom);
+                    } else {
+                        break;
+                    }
+                }
+            }
+            recycleViewsOutOfBounds(recycler);
+            return scrolled;
+        }
+
+        @Override
+        public View onFocusSearchFailed(View focused, int direction,
+                RecyclerView.Adapter adapter, RecyclerView.Recycler recycler) {
+            final int oldCount = getChildCount();
+
+            if (oldCount == 0) {
+                return null;
+            }
+
+            final int left = getPaddingLeft();
+            final int right = getWidth() - getPaddingRight();
+
+            View toFocus = null;
+            int newViewsHeight = 0;
+            if (direction == View.FOCUS_UP || direction == View.FOCUS_BACKWARD) {
+                while (mFirstPosition > 0 && newViewsHeight < mScrollDistance) {
+                    mFirstPosition--;
+                    View v = recycler.getViewForPosition(adapter, mFirstPosition);
+                    final int bottom = getDecoratedTop(getChildAt(0));
+                    addView(v, 0);
+                    measureChildWithMargins(v, 0, 0);
+                    final int top = bottom - getDecoratedMeasuredHeight(v);
+                    layoutDecorated(v, left, top, right, bottom);
+                    if (v.isFocusable()) {
+                        toFocus = v;
+                        break;
+                    }
+                }
+            }
+            if (direction == View.FOCUS_DOWN || direction == View.FOCUS_FORWARD) {
+                while (mFirstPosition + getChildCount() < getItemCount() &&
+                        newViewsHeight < mScrollDistance) {
+                    View v = recycler.getViewForPosition(adapter, mFirstPosition + getChildCount());
+                    final int top = getDecoratedBottom(getChildAt(getChildCount() - 1));
+                    addView(v);
+                    measureChildWithMargins(v, 0, 0);
+                    final int bottom = top + getDecoratedMeasuredHeight(v);
+                    layoutDecorated(v, left, top, right, bottom);
+                    if (v.isFocusable()) {
+                        toFocus = v;
+                        break;
+                    }
+                }
+            }
+
+            return toFocus;
+        }
+
+        public void recycleViewsOutOfBounds(RecyclerView.Recycler recycler) {
+            final int childCount = getChildCount();
+            final int parentWidth = getWidth();
+            final int parentHeight = getHeight();
+            boolean foundFirst = false;
+            int first = 0;
+            int last = 0;
+            for (int i = 0; i < childCount; i++) {
+                final View v = getChildAt(i);
+                if (v.hasFocus() || (getDecoratedRight(v) >= 0 &&
+                        getDecoratedLeft(v) <= parentWidth &&
+                        getDecoratedBottom(v) >= 0 &&
+                        getDecoratedTop(v) <= parentHeight)) {
+                    if (!foundFirst) {
+                        first = i;
+                        foundFirst = true;
+                    }
+                    last = i;
+                }
+            }
+            for (int i = childCount - 1; i > last; i--) {
+                removeAndRecycleViewAt(i, recycler);
+            }
+            for (int i = first - 1; i >= 0; i--) {
+                removeAndRecycleViewAt(i, recycler);
+            }
+            if (getChildCount() == 0) {
+                mFirstPosition = 0;
+            } else {
+                mFirstPosition += first;
+            }
+        }
+    }
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java
new file mode 100644
index 0000000..110eb62
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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.supportv7.widget.adapter;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class SimpleStringAdapter extends RecyclerView.Adapter<SimpleStringAdapter.ViewHolder> {
+
+    private int mBackground;
+
+    private ArrayList<String> mValues;
+
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+
+        public TextView textView;
+
+        public ViewHolder(TextView v) {
+            super(v);
+            textView = v;
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + " '" + textView.getText();
+        }
+    }
+
+    public SimpleStringAdapter(Context context, String[] strings) {
+        TypedValue val = new TypedValue();
+        if (context.getTheme() != null) {
+            context.getTheme().resolveAttribute(
+                    android.R.attr.selectableItemBackground, val, true);
+        }
+        mBackground = val.resourceId;
+        mValues = new ArrayList<String>();
+        Collections.addAll(mValues, strings);
+    }
+
+    public void swap(int pos1, int pos2) {
+        String tmp = mValues.get(pos1);
+        mValues.set(pos1, mValues.get(pos2));
+        mValues.set(pos2, tmp);
+        notifyItemRemoved(pos1);
+        notifyItemInserted(pos2);
+    }
+
+    @Override
+    public SimpleStringAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        final ViewHolder h = new ViewHolder(new TextView(parent.getContext()));
+        h.textView.setMinimumHeight(128);
+        h.textView.setPadding(20, 0, 20, 0);
+        h.textView.setFocusable(true);
+        h.textView.setBackgroundResource(mBackground);
+        return h;
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.textView.setText(position + ":" + mValues.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/decorator/DividerItemDecoration.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/decorator/DividerItemDecoration.java
new file mode 100644
index 0000000..4d5d208
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/decorator/DividerItemDecoration.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 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.supportv7.widget.decorator;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+public class DividerItemDecoration extends RecyclerView.ItemDecoration {
+
+    private static final int[] ATTRS = new int[]{
+            android.R.attr.listDivider
+    };
+
+    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
+
+    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
+
+    private Drawable mDivider;
+
+    private int mOrientation;
+
+    public DividerItemDecoration(Context context, int orientation) {
+        final TypedArray a = context.obtainStyledAttributes(ATTRS);
+        mDivider = a.getDrawable(0);
+        a.recycle();
+        setOrientation(orientation);
+    }
+
+    public void setOrientation(int orientation) {
+        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
+            throw new IllegalArgumentException("invalid orientation");
+        }
+        mOrientation = orientation;
+    }
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent) {
+        if (mOrientation == VERTICAL_LIST) {
+            drawVertical(c, parent);
+        } else {
+            drawHorizontal(c, parent);
+        }
+    }
+
+    public void drawVertical(Canvas c, RecyclerView parent) {
+        final int left = parent.getPaddingLeft();
+        final int right = parent.getWidth() - parent.getPaddingRight();
+
+        final int childCount = parent.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = parent.getChildAt(i);
+            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+                    .getLayoutParams();
+            final int top = child.getBottom() + params.bottomMargin;
+            final int bottom = top + mDivider.getIntrinsicHeight();
+            mDivider.setBounds(left, top, right, bottom);
+            mDivider.draw(c);
+        }
+    }
+
+    public void drawHorizontal(Canvas c, RecyclerView parent) {
+        final int top = parent.getPaddingTop();
+        final int bottom = parent.getHeight() - parent.getPaddingBottom();
+
+        final int childCount = parent.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = parent.getChildAt(i);
+            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+                    .getLayoutParams();
+            final int left = child.getRight() + params.rightMargin;
+            final int right = left + mDivider.getIntrinsicHeight();
+            mDivider.setBounds(left, top, right, bottom);
+            mDivider.draw(c);
+        }
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
+        if (mOrientation == VERTICAL_LIST) {
+            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
+        } else {
+            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
+        }
+    }
+}
diff --git a/samples/SupportLeanbackDemos/Android.mk b/samples/SupportLeanbackDemos/Android.mk
new file mode 100644
index 0000000..41424a7
--- /dev/null
+++ b/samples/SupportLeanbackDemos/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Build the samples.
+# We need to add some special AAPT flags to generate R classes
+# for resources that are included from the libraries.
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := SupportLeanbackDemos
+LOCAL_MODULE_TAGS := samples tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+        android-support-v4 \
+        android-support-v7-recyclerview \
+        android-support-v17-leanback
+LOCAL_RESOURCE_DIR = \
+        $(LOCAL_PATH)/res \
+        frameworks/support/v17/leanback/res
+LOCAL_AAPT_FLAGS := \
+        --auto-add-overlay \
+        --extra-packages android.support.v17.leanback
+include $(BUILD_PACKAGE)
diff --git a/samples/SupportLeanbackDemos/AndroidManifest.xml b/samples/SupportLeanbackDemos/AndroidManifest.xml
new file mode 100644
index 0000000..5eeaae5
--- /dev/null
+++ b/samples/SupportLeanbackDemos/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.leanback"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="19" />
+
+    <application
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:banner="@drawable/ic_launcher"
+        android:theme="@style/Theme.Leanback">
+
+        <activity android:name="MainActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="DetailsActivity"
+            android:exported="true" />
+
+        <activity android:name="VerticalGridActivity"
+            android:exported="true" />
+
+    </application>
+</manifest>
diff --git a/samples/SupportLeanbackDemos/res/drawable-hdpi/ic_launcher.png b/samples/SupportLeanbackDemos/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-ldpi/ic_launcher.png b/samples/SupportLeanbackDemos/res/drawable-ldpi/ic_launcher.png
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-ldpi/ic_launcher.png
rename to samples/SupportLeanbackDemos/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-mdpi/ic_launcher.png b/samples/SupportLeanbackDemos/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/grid_bg.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/grid_bg.png
new file mode 100644
index 0000000..476c698
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/grid_bg.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_launcher.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable/details_img.png b/samples/SupportLeanbackDemos/res/drawable/details_img.png
new file mode 100644
index 0000000..7ea688b
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable/details_img.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable/ic_action_a.png b/samples/SupportLeanbackDemos/res/drawable/ic_action_a.png
new file mode 100644
index 0000000..3d555ef
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable/ic_action_a.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable/ic_title.png b/samples/SupportLeanbackDemos/res/drawable/ic_title.png
new file mode 100644
index 0000000..1c62b2e
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable/ic_title.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable/text_bg.xml b/samples/SupportLeanbackDemos/res/drawable/text_bg.xml
new file mode 100644
index 0000000..c67924c
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable/text_bg.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:startColor="#FFFF0000"
+        android:endColor="#80FF00FF"
+        android:angle="45" />
+    <padding
+        android:left="7dp"
+        android:top="7dp"
+        android:right="7dp"
+        android:bottom="7dp" />
+</shape>
diff --git a/samples/SupportLeanbackDemos/res/layout/details.xml b/samples/SupportLeanbackDemos/res/layout/details.xml
new file mode 100644
index 0000000..4af4e6a
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/details.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="com.example.android.leanback.DetailsFragment"
+    android:id="@+id/details_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+/>
diff --git a/samples/SupportLeanbackDemos/res/layout/main.xml b/samples/SupportLeanbackDemos/res/layout/main.xml
new file mode 100644
index 0000000..b6b08b3
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/main.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="com.example.android.leanback.BrowseFragment"
+    android:id="@+id/main_browse_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+/>
diff --git a/samples/SupportLeanbackDemos/res/layout/vertical_grid.xml b/samples/SupportLeanbackDemos/res/layout/vertical_grid.xml
new file mode 100644
index 0000000..1007042
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/vertical_grid.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="com.example.android.leanback.VerticalGridFragment"
+    android:id="@+id/vertical_grid_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+/>
diff --git a/samples/SupportLeanbackDemos/res/values/strings.xml b/samples/SupportLeanbackDemos/res/values/strings.xml
new file mode 100644
index 0000000..e884043
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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">MainActivity</string>
+</resources>
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
new file mode 100644
index 0000000..ec547ec
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.OnItemClickedListener;
+import android.support.v17.leanback.widget.Row;
+import android.util.Log;
+
+public class BrowseFragment extends android.support.v17.leanback.app.BrowseFragment {
+    private static final String TAG = "leanback.BrowseFragment";
+
+    private static final int NUM_ROWS = 3;
+    private ArrayObjectAdapter mRowsAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        Params p = new Params();
+        p.setBadgeImage(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        p.setTitle("Leanback Sample App");
+        p.setHeadersState(HEADERS_ENABLED);
+        setBrowseParams(p);
+
+        setupRows();
+        setOnItemClickedListener(new ItemClickedListener());
+    }
+
+    private void setupRows() {
+        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+
+        for (int i = 0; i < NUM_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
+            listRowAdapter.add("Hello world");
+            listRowAdapter.add("This is a test");
+            HeaderItem header = new HeaderItem(i, "Row " + i, null);
+            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+        }
+
+        setAdapter(mRowsAdapter);
+    }
+
+    private final class ItemClickedListener implements OnItemClickedListener {
+        public void onItemClicked(Object item, Row row) {
+            // TODO: use a fragment transaction instead of launching a new
+            // activity
+            Intent intent = new Intent(getActivity(), DetailsActivity.class);
+            startActivity(intent);
+        }
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java
new file mode 100644
index 0000000..502c77a
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.support.v17.leanback.widget.ImageCardView;
+import android.support.v17.leanback.widget.Presenter;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class CardPresenter extends Presenter {
+    private static final String TAG = "CardPresenter";
+
+    public ViewHolder onCreateViewHolder(ViewGroup parent) {
+        Log.d(TAG, "onCreateViewHolder");
+        ImageCardView v = new ImageCardView(parent.getContext());
+        v.setFocusable(true);
+        v.setFocusableInTouchMode(true);
+        v.setMainImage(
+                parent.getContext().getResources().getDrawable(R.drawable.text_bg));
+        return new ViewHolder(v);
+    }
+
+    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+        Log.d(TAG, "onBindViewHolder for " + item.toString());
+        ((ImageCardView) viewHolder.view).setTitleText(item.toString());
+    }
+
+    public void onUnbindViewHolder(ViewHolder viewHolder) {
+        Log.d(TAG, "onUnbindViewHolder");
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java
new file mode 100644
index 0000000..082b134
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class DetailsActivity extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.details);
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
new file mode 100644
index 0000000..6d376f0
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.support.v17.leanback.widget.DetailsOverviewRow;
+
+public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter {
+
+    @Override
+    protected void onBindDescription(ViewHolder vh, Object item) {
+        vh.getTitle().setText(item.toString());
+        vh.getSubtitle().setText("2013 - 2014   Drama   TV-14");
+        vh.getBody().setText("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+                + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
+                + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+                + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+                + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+                + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
new file mode 100644
index 0000000..c9a85f3
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.DetailsOverviewRow;
+import android.support.v17.leanback.widget.DetailsOverviewRowPresenter;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.util.Log;
+import android.widget.Toast;
+
+public class DetailsFragment extends android.support.v17.leanback.app.DetailsFragment {
+    private static final String TAG = "leanback.BrowseFragment";
+
+    private static final int NUM_ROWS = 3;
+    private ArrayObjectAdapter mRowsAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        setupRows();
+    }
+
+    private void setupRows() {
+        ClassPresenterSelector ps = new ClassPresenterSelector();
+        DetailsOverviewRowPresenter dorPresenter =
+            new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
+        dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
+            public void onActionClicked(Action action) {
+                Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
+            }
+        });
+
+        ps.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
+        ps.addClassPresenter(ListRow.class,
+                new ListRowPresenter());
+        mRowsAdapter = new ArrayObjectAdapter(ps);
+
+        Resources res = getActivity().getResources();
+        DetailsOverviewRow dor = new DetailsOverviewRow("Details Overview");
+        dor.setImageDrawable(res.getDrawable(R.drawable.details_img));
+        dor.addAction(new Action(1, "Buy $9.99"));
+        dor.addAction(new Action(2, "Rent", "$3.99", res.getDrawable(R.drawable.ic_action_a)));
+        mRowsAdapter.add(dor);
+
+        for (int i = 0; i < NUM_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter());
+            listRowAdapter.add("Hello world");
+            listRowAdapter.add("This is a test");
+            HeaderItem header = new HeaderItem(i, "Row " + i, null);
+            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+        }
+
+        setAdapter(mRowsAdapter);
+    }
+
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
new file mode 100644
index 0000000..74e7fb2
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
new file mode 100644
index 0000000..5c80e0b
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.support.v17.leanback.widget.Presenter;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class StringPresenter extends Presenter {
+    private static final String TAG = "StringPresenter";
+
+    public ViewHolder onCreateViewHolder(ViewGroup parent) {
+        Log.d(TAG, "onCreateViewHolder");
+        TextView tv = new TextView(parent.getContext());
+        tv.setFocusable(true);
+        tv.setFocusableInTouchMode(true);
+        tv.setBackground(
+                parent.getContext().getResources().getDrawable(R.drawable.text_bg));
+        return new ViewHolder(tv);
+    }
+
+    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+        Log.d(TAG, "onBindViewHolder for " + item.toString());
+        ((TextView) viewHolder.view).setText(item.toString());
+    }
+
+    public void onUnbindViewHolder(ViewHolder viewHolder) {
+        Log.d(TAG, "onUnbindViewHolder"); 
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridActivity.java
new file mode 100644
index 0000000..c5262b9
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class VerticalGridActivity extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.vertical_grid);
+        getWindow().setBackgroundDrawableResource(R.drawable.grid_bg);
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
new file mode 100644
index 0000000..495bebe
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 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.leanback;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.VerticalGridPresenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.OnItemClickedListener;
+import android.support.v17.leanback.widget.OnItemSelectedListener;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Random;
+
+public class VerticalGridFragment extends android.support.v17.leanback.app.VerticalGridFragment {
+    private static final String TAG = "leanback.VerticalGridFragment";
+
+    private static final int NUM_COLUMNS = 3;
+    private static final int NUM_ITEMS = 50;
+    private static final int HEIGHT = 200;
+
+    private ArrayObjectAdapter mAdapter;
+    private Random mRandom;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        mRandom = new Random();
+
+        Params p = new Params();
+        p.setBadgeImage(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        p.setTitle("Leanback Vertical Grid Demo");
+        setParams(p);
+
+        setupFragment();
+    }
+
+    private void setupFragment() {
+        VerticalGridPresenter gridPresenter = new VerticalGridPresenter();
+        gridPresenter.setNumberOfColumns(NUM_COLUMNS);
+        setGridPresenter(gridPresenter);
+
+        mAdapter = new ArrayObjectAdapter(new GridItemPresenter());
+        for (int i = 0; i < NUM_ITEMS; i++) {
+            mAdapter.add(new MyItem(i));
+        }
+        setAdapter(mAdapter);
+
+        setOnItemSelectedListener(new OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(Object item, Row row) {
+                Log.i(TAG, "item selected: " + ((MyItem) item).id);
+            }
+        });
+
+        setOnItemClickedListener(new OnItemClickedListener() {
+            @Override
+            public void onItemClicked(Object item, Row row) {
+                Log.i(TAG, "item clicked: " + ((MyItem) item).id);
+            }
+        });
+    }
+
+    private class GridItemPresenter extends Presenter {
+        public ViewHolder onCreateViewHolder(ViewGroup parent) {
+            TextView view = new TextView(parent.getContext());
+            // Choose a random height between HEIGHT and 1.5 * HEIGHT to
+            // demonstrate the staggered nature of the grid.
+            final int height = HEIGHT + mRandom.nextInt(HEIGHT / 2);
+            view.setLayoutParams(new ViewGroup.LayoutParams(200, height));
+            view.setFocusable(true);
+            view.setFocusableInTouchMode(true);
+            view.setBackgroundColor(Color.DKGRAY);
+            view.setGravity(Gravity.CENTER);
+            return new ViewHolder(view);
+        }
+
+        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+            ((TextView) viewHolder.view).setText(Integer.toString(((MyItem) item).id));
+        }
+
+        public void onUnbindViewHolder(ViewHolder viewHolder) {}
+    }
+
+    static class MyItem {
+        int id;
+        MyItem(int id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/samples/browseable/Basic/AndroidManifest.xml b/samples/browseable/ActionBarCompat-Basic/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/Basic/AndroidManifest.xml
rename to samples/browseable/ActionBarCompat-Basic/AndroidManifest.xml
diff --git a/samples/browseable/ActionBarCompat-Basic/_index.jd b/samples/browseable/ActionBarCompat-Basic/_index.jd
new file mode 100644
index 0000000..4e77371
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Basic/_index.jd
@@ -0,0 +1,15 @@
+
+
+
+page.tags="ActionBarCompat-Basic"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to create a basic action bar that displays
+action items. The sample shows how to inflate items from a menu resource, and
+how to add items programatically. To reduce clutter, rarely used actions are
+displayed in an action bar overflow.</p>
+<p>The activity in this sample extends from
+{@link android.support.v7.app.ActionBarActivity}, which provides the
+functionality necessary to display a compatible action bar on devices
+running Android 2.1 and higher.</p>
diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_action_location.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_action_location.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_action_refresh.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_action_settings.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_action_settings.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/ActionBarCompat-Basic/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_action_location.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_action_location.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_action_refresh.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_action_settings.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_action_settings.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_action_location.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_action_location.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_action_refresh.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_action_settings.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_action_settings.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Basic/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Basic/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-Basic/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/Basic/res/layout/activity_main.xml
copy to samples/browseable/ActionBarCompat-Basic/res/layout/activity_main.xml
diff --git a/samples/browseable/Basic/res/layout/sample_main.xml b/samples/browseable/ActionBarCompat-Basic/res/layout/sample_main.xml
similarity index 100%
rename from samples/browseable/Basic/res/layout/sample_main.xml
rename to samples/browseable/ActionBarCompat-Basic/res/layout/sample_main.xml
diff --git a/samples/browseable/Basic/res/menu/main.xml b/samples/browseable/ActionBarCompat-Basic/res/menu/main.xml
similarity index 100%
rename from samples/browseable/Basic/res/menu/main.xml
rename to samples/browseable/ActionBarCompat-Basic/res/menu/main.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/ActionBarCompat-Basic/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/dimens.xml
copy to samples/browseable/ActionBarCompat-Basic/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/ActionBarCompat-Basic/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/styles.xml
copy to samples/browseable/ActionBarCompat-Basic/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml b/samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Basic/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/ActionBarCompat-Basic/res/values/base-strings.xml b/samples/browseable/ActionBarCompat-Basic/res/values/base-strings.xml
new file mode 100644
index 0000000..8edd1b2
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Basic/res/values/base-strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">ActionBarCompat-Basic</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample shows you how to use ActionBarCompat to create a basic Activity which
+            displays action items. It covers inflating items from a menu resource, as well as adding
+            an item in code. Items that are not shown as action items on the Action Bar are
+            displayed in the action bar overflow.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/Basic/res/values/ids.xml b/samples/browseable/ActionBarCompat-Basic/res/values/ids.xml
similarity index 100%
rename from samples/browseable/Basic/res/values/ids.xml
rename to samples/browseable/ActionBarCompat-Basic/res/values/ids.xml
diff --git a/samples/browseable/Basic/res/values/strings.xml b/samples/browseable/ActionBarCompat-Basic/res/values/strings.xml
similarity index 100%
rename from samples/browseable/Basic/res/values/strings.xml
rename to samples/browseable/ActionBarCompat-Basic/res/values/strings.xml
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/ActionBarCompat-Basic/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/ActionBarCompat-Basic/res/values/template-dimens.xml
diff --git a/samples/browseable/ActionBarCompat-Basic/res/values/template-styles.xml b/samples/browseable/ActionBarCompat-Basic/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Basic/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java b/samples/browseable/ActionBarCompat-Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java
similarity index 100%
rename from samples/browseable/Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java
rename to samples/browseable/ActionBarCompat-Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java
diff --git a/samples/browseable/ListPopupMenu/AndroidManifest.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/AndroidManifest.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/AndroidManifest.xml
diff --git a/samples/browseable/ActionBarCompat-ListPopupMenu/_index.jd b/samples/browseable/ActionBarCompat-ListPopupMenu/_index.jd
new file mode 100644
index 0000000..9da5bdd
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ListPopupMenu/_index.jd
@@ -0,0 +1,14 @@
+
+
+
+page.tags="ActionBarCompat-ListPopupMenu"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to use a backward compatible
+{@link android.support.v7.widget.PopupMenu PopupMenu} to create a list, where
+each list item contains a dropdown menu.</p>
+<p>The activity in this sample extends from
+{@link android.support.v7.app.ActionBarActivity}, which provides the
+functionality necessary to display a compatible action bar on devices
+running Android 2.1 and higher.</p>
diff --git a/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_overflow.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-hdpi/ic_overflow.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_overflow.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-hdpi/ic_overflow.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-hdpi/tile.9.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-hdpi/tile.9.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_overflow.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-mdpi/ic_overflow.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_overflow.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-mdpi/ic_overflow.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_overflow.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-xhdpi/ic_overflow.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_overflow.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-xhdpi/ic_overflow.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ListPopupMenu/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/activity_main.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/layout/activity_main.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/activity_main.xml
diff --git a/samples/browseable/ListPopupMenu/res/layout/list_item.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/list_item.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/layout/list_item.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/list_item.xml
diff --git a/samples/browseable/ListPopupMenu/res/layout/sample_main.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/sample_main.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/layout/sample_main.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/sample_main.xml
diff --git a/samples/browseable/ListPopupMenu/res/menu/popup.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/menu/popup.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/menu/popup.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/menu/popup.xml
diff --git a/samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/ActionBarCompat-ListPopupMenu/res/values-v11/template-styles.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/base-strings.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/base-strings.xml
new file mode 100644
index 0000000..130e2da
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">ActionBarCompat-ListPopupMenu</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample shows you how to use {@link android.support.v7.widget.PopupMenu PopupMenu}
+            from ActionBarCompat to create a list, with each item having a dropdown menu.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/ListPopupMenu/res/values/strings.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/strings.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/values/strings.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/values/strings.xml
diff --git a/samples/browseable/ListPopupMenu/res/values/dimens.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/ListPopupMenu/res/values/dimens.xml
rename to samples/browseable/ActionBarCompat-ListPopupMenu/res/values/template-dimens.xml
diff --git a/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/template-styles.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ListPopupMenu/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java b/samples/browseable/ActionBarCompat-ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java
similarity index 100%
rename from samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java
rename to samples/browseable/ActionBarCompat-ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java
diff --git a/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java b/samples/browseable/ActionBarCompat-ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java
similarity index 100%
rename from samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java
rename to samples/browseable/ActionBarCompat-ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java
diff --git a/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java b/samples/browseable/ActionBarCompat-ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java
similarity index 100%
rename from samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java
rename to samples/browseable/ActionBarCompat-ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java
diff --git a/samples/browseable/ShareActionProvider/AndroidManifest.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/AndroidManifest.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/AndroidManifest.xml
diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/_index.jd b/samples/browseable/ActionBarCompat-ShareActionProvider/_index.jd
new file mode 100644
index 0000000..63ac59a
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ShareActionProvider/_index.jd
@@ -0,0 +1,14 @@
+
+
+
+page.tags="ActionBarCompat-ShareActionProvider"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to use a
+context-sensitive {@link android.support.v7.widget.ShareActionProvider} that is
+backward compatible.</p>
+<p>The activity in this sample extends from
+{@link android.support.v7.app.ActionBarActivity}, which provides the
+functionality necessary to display a compatible action bar on devices
+running Android 2.1 and higher.</p>
diff --git a/samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png b/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ShareActionProvider/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/activity_main.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/layout/activity_main.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/activity_main.xml
diff --git a/samples/browseable/ShareActionProvider/res/layout/item_image.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_image.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/layout/item_image.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_image.xml
diff --git a/samples/browseable/ShareActionProvider/res/layout/item_text.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_text.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/layout/item_text.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_text.xml
diff --git a/samples/browseable/ShareActionProvider/res/layout/sample_main.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/sample_main.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/layout/sample_main.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/sample_main.xml
diff --git a/samples/browseable/ShareActionProvider/res/menu/main_menu.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/menu/main_menu.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/menu/main_menu.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/menu/main_menu.xml
diff --git a/samples/browseable/ShareActionProvider/res/values-sw600dp/dimens.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/values-sw600dp/dimens.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ShareActionProvider/res/values-sw600dp/styles.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/values-sw600dp/styles.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v11/template-styles.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/base-strings.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/base-strings.xml
new file mode 100644
index 0000000..904f4f1
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">ActionBarCompat-ShareActionProvider</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample shows you how a provide a context-sensitive ShareActionProvider with
+            ActionBarCompat, backwards compatible to API v7.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/ShareActionProvider/res/values/strings.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/strings.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/values/strings.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/values/strings.xml
diff --git a/samples/browseable/ShareActionProvider/res/values/dimens.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/ShareActionProvider/res/values/dimens.xml
rename to samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-dimens.xml
diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-styles.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java b/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java
similarity index 100%
rename from samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java
rename to samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java
diff --git a/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java b/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java
similarity index 100%
rename from samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java
rename to samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java
diff --git a/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java b/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java
similarity index 100%
rename from samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java
rename to samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java
diff --git a/samples/browseable/Styled/AndroidManifest.xml b/samples/browseable/ActionBarCompat-Styled/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/Styled/AndroidManifest.xml
rename to samples/browseable/ActionBarCompat-Styled/AndroidManifest.xml
diff --git a/samples/browseable/ActionBarCompat-Styled/_index.jd b/samples/browseable/ActionBarCompat-Styled/_index.jd
new file mode 100644
index 0000000..06fee1a
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Styled/_index.jd
@@ -0,0 +1,13 @@
+
+
+
+page.tags="ActionBarCompat-Styled"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to use a backward compatible
+{@link android.support.v7.app.ActionBar} with a customized theme.</p>
+<p>The activity in this sample extends from
+{@link android.support.v7.app.ActionBarActivity}, which provides the
+functionality necessary to display a compatible action bar on devices
+running Android 2.1 and higher.</p>
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ab_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ab_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ab_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ab_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_action_location.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_action_location.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ic_action_location.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_action_location.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_action_refresh.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ic_action_refresh.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_action_settings.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_action_settings.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ic_action_settings.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_action_settings.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/list_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/list_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/list_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/list_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/progress_bg_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/progress_bg_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/progress_bg_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/progress_bg_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/progress_primary_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/progress_primary_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/progress_primary_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/progress_primary_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/progress_secondary_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/progress_secondary_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/progress_secondary_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/progress_secondary_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_selected_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_selected_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/tab_selected_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_selected_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-hdpi/tile.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-hdpi/tile.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ab_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ab_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ab_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ab_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_action_location.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_action_location.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ic_action_location.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_action_location.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_action_refresh.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_action_refresh.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ic_action_refresh.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_action_settings.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_action_settings.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ic_action_settings.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_action_settings.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/list_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/list_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/list_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/list_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/progress_bg_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/progress_bg_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/progress_bg_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/progress_bg_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/progress_primary_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/progress_primary_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/progress_primary_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/progress_primary_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/progress_secondary_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/progress_secondary_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/progress_secondary_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/progress_secondary_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_selected_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_selected_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/tab_selected_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_selected_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ab_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ab_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ab_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ab_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_action_location.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_action_location.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ic_action_location.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_action_location.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_action_refresh.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ic_action_refresh.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_action_refresh.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_action_settings.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_action_settings.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ic_action_settings.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_action_settings.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/list_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/list_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/list_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/list_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/progress_bg_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/progress_bg_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/progress_bg_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/progress_bg_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/progress_primary_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/progress_primary_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/progress_primary_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/progress_primary_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/progress_secondary_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/progress_secondary_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/progress_secondary_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/progress_secondary_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_selected_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/tab_selected_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_selected_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ActionBarCompat-Styled/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Styled/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/ActionBarCompat-Styled/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Styled/res/drawable/pressed_background.xml b/samples/browseable/ActionBarCompat-Styled/res/drawable/pressed_background.xml
similarity index 100%
rename from samples/browseable/Styled/res/drawable/pressed_background.xml
rename to samples/browseable/ActionBarCompat-Styled/res/drawable/pressed_background.xml
diff --git a/samples/browseable/Styled/res/drawable/progress_horizontal.xml b/samples/browseable/ActionBarCompat-Styled/res/drawable/progress_horizontal.xml
similarity index 100%
rename from samples/browseable/Styled/res/drawable/progress_horizontal.xml
rename to samples/browseable/ActionBarCompat-Styled/res/drawable/progress_horizontal.xml
diff --git a/samples/browseable/Styled/res/drawable/selectable_background.xml b/samples/browseable/ActionBarCompat-Styled/res/drawable/selectable_background.xml
similarity index 100%
rename from samples/browseable/Styled/res/drawable/selectable_background.xml
rename to samples/browseable/ActionBarCompat-Styled/res/drawable/selectable_background.xml
diff --git a/samples/browseable/Styled/res/drawable/spinner_background_ab.xml b/samples/browseable/ActionBarCompat-Styled/res/drawable/spinner_background_ab.xml
similarity index 100%
rename from samples/browseable/Styled/res/drawable/spinner_background_ab.xml
rename to samples/browseable/ActionBarCompat-Styled/res/drawable/spinner_background_ab.xml
diff --git a/samples/browseable/Styled/res/drawable/tab_indicator_ab.xml b/samples/browseable/ActionBarCompat-Styled/res/drawable/tab_indicator_ab.xml
similarity index 100%
rename from samples/browseable/Styled/res/drawable/tab_indicator_ab.xml
rename to samples/browseable/ActionBarCompat-Styled/res/drawable/tab_indicator_ab.xml
diff --git a/samples/browseable/Styled/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-Styled/res/layout/activity_main.xml
similarity index 100%
rename from samples/browseable/Styled/res/layout/activity_main.xml
rename to samples/browseable/ActionBarCompat-Styled/res/layout/activity_main.xml
diff --git a/samples/browseable/Styled/res/layout/sample_main.xml b/samples/browseable/ActionBarCompat-Styled/res/layout/sample_main.xml
similarity index 100%
rename from samples/browseable/Styled/res/layout/sample_main.xml
rename to samples/browseable/ActionBarCompat-Styled/res/layout/sample_main.xml
diff --git a/samples/browseable/Styled/res/menu/main.xml b/samples/browseable/ActionBarCompat-Styled/res/menu/main.xml
similarity index 100%
rename from samples/browseable/Styled/res/menu/main.xml
rename to samples/browseable/ActionBarCompat-Styled/res/menu/main.xml
diff --git a/samples/browseable/Styled/res/values-sw600dp/dimens.xml b/samples/browseable/ActionBarCompat-Styled/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/Styled/res/values-sw600dp/dimens.xml
rename to samples/browseable/ActionBarCompat-Styled/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Styled/res/values-sw600dp/styles.xml b/samples/browseable/ActionBarCompat-Styled/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/Styled/res/values-sw600dp/styles.xml
rename to samples/browseable/ActionBarCompat-Styled/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/ActionBarCompat-Styled/res/values-v11/template-styles.xml b/samples/browseable/ActionBarCompat-Styled/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Styled/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/Styled/res/values-v14/styles.xml b/samples/browseable/ActionBarCompat-Styled/res/values-v14/styles.xml
similarity index 100%
rename from samples/browseable/Styled/res/values-v14/styles.xml
rename to samples/browseable/ActionBarCompat-Styled/res/values-v14/styles.xml
diff --git a/samples/browseable/ActionBarCompat-Styled/res/values/base-strings.xml b/samples/browseable/ActionBarCompat-Styled/res/values/base-strings.xml
new file mode 100644
index 0000000..7adb0c6
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Styled/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">ActionBarCompat-Styled</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample shows you how to use ActionBarCompat with a customized theme. It utilizes a
+            split action bar when running on a device with a narrow display, and show three tabs.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/Styled/res/values/colors.xml b/samples/browseable/ActionBarCompat-Styled/res/values/colors.xml
similarity index 100%
rename from samples/browseable/Styled/res/values/colors.xml
rename to samples/browseable/ActionBarCompat-Styled/res/values/colors.xml
diff --git a/samples/browseable/Styled/res/values/strings.xml b/samples/browseable/ActionBarCompat-Styled/res/values/strings.xml
similarity index 100%
rename from samples/browseable/Styled/res/values/strings.xml
rename to samples/browseable/ActionBarCompat-Styled/res/values/strings.xml
diff --git a/samples/browseable/ActionBarCompat-Styled/res/values/styles.xml b/samples/browseable/ActionBarCompat-Styled/res/values/styles.xml
new file mode 100644
index 0000000..75b0533
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Styled/res/values/styles.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 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>
+
+    <!--
+        This is the styled theme.
+
+        It extends from Theme.AppCompat.Light, but it could extend from any of
+        the Theme.AppCompat themes depending on your color scheme. This theme can be applied to
+        your application or individual activities in the AndroidManifest.xml. In this sample it is
+        set on the application.
+
+        This differs from the version of this theme in 'res/values-v14', as not all of the
+        necessary attributes are available in the android: namespace on older versions of Android.
+        This means that for certain attributes we must set the attributes provided in
+        ActionBarCompat's namespace instead.
+    -->
+
+    <style name="Theme.Styled" parent="@style/Theme.AppCompat.Light">
+        <item name="actionBarItemBackground">@drawable/selectable_background</item>
+        <item name="actionBarTabStyle">@style/Widget.Styled.ActionBar.TabView</item>
+        <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item>
+        <item name="actionDropDownStyle">@style/Widget.Styled.Spinner.DropDown.ActionBar</item>
+        <item name="dropDownListViewStyle">@style/Widget.Styled.ListView.DropDown</item>
+        <item name="popupMenuStyle">@style/Widget.Styled.PopupMenu</item>
+    </style>
+
+    <style name="Widget.Styled.ActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
+        <item name="background">@drawable/ab_solid_styled</item>
+        <item name="backgroundStacked">@drawable/ab_stacked_solid_styled</item>
+        <item name="backgroundSplit">@drawable/ab_bottom_solid_styled</item>
+        <item name="progressBarStyle">@style/Widget.Styled.ProgressBar.Horizontal</item>
+    </style>
+
+
+    <!--
+        For the following styles, the attributes are available in the android namespace which
+        means that we can set them here for all platforms (v7 through to the latest).
+    -->
+
+    <style name="Widget.Styled.ActionBar.TabView"
+           parent="@style/Widget.AppCompat.Light.ActionBar.TabView">
+        <item name="android:background">@drawable/tab_indicator_ab</item>
+    </style>
+
+    <style name="Widget.Styled.Spinner.DropDown.ActionBar"
+           parent="@style/Widget.AppCompat.Light.Spinner.DropDown.ActionBar">
+        <item name="android:background">@drawable/spinner_background_ab</item>
+        <item name="android:popupBackground">@drawable/menu_dropdown_panel_styled</item>
+        <item name="android:dropDownSelector">@drawable/selectable_background</item>
+    </style>
+
+    <style name="Widget.Styled.ProgressBar.Horizontal"
+           parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
+        <item name="android:progressDrawable">@drawable/progress_horizontal</item>
+    </style>
+
+    <style name="Widget.Styled.PopupMenu" parent="@style/Widget.AppCompat.Light.PopupMenu">
+        <item name="android:popupBackground">@drawable/menu_dropdown_panel_styled</item>
+    </style>
+
+    <style name="Widget.Styled.ListView.DropDown"
+           parent="@style/Widget.AppCompat.Light.ListView.DropDown">
+        <item name="android:listSelector">@drawable/selectable_background</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/Styled/res/values/dimens.xml b/samples/browseable/ActionBarCompat-Styled/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/Styled/res/values/dimens.xml
rename to samples/browseable/ActionBarCompat-Styled/res/values/template-dimens.xml
diff --git a/samples/browseable/ActionBarCompat-Styled/res/values/template-styles.xml b/samples/browseable/ActionBarCompat-Styled/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/ActionBarCompat-Styled/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java b/samples/browseable/ActionBarCompat-Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java
similarity index 100%
rename from samples/browseable/Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java
rename to samples/browseable/ActionBarCompat-Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml b/samples/browseable/ActivityInstrumentation/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml
rename to samples/browseable/ActivityInstrumentation/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml b/samples/browseable/ActivityInstrumentation/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml
rename to samples/browseable/ActivityInstrumentation/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/ActivityInstrumentation/res/values-v11/template-styles.xml b/samples/browseable/ActivityInstrumentation/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/ActivityInstrumentation/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/ActivityInstrumentation/res/values/dimens.xml b/samples/browseable/ActivityInstrumentation/res/values/dimens.xml
index 39e710b..4afc9dd 100644
--- a/samples/browseable/ActivityInstrumentation/res/values/dimens.xml
+++ b/samples/browseable/ActivityInstrumentation/res/values/dimens.xml
@@ -1,32 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright 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.
+  ~ Copyright 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>
-
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
-
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 </resources>
diff --git a/samples/browseable/ActivityInstrumentation/res/values/styles.xml b/samples/browseable/ActivityInstrumentation/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/ActivityInstrumentation/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml b/samples/browseable/ActivityInstrumentation/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml
copy to samples/browseable/ActivityInstrumentation/res/values/template-dimens.xml
diff --git a/samples/browseable/ActivityInstrumentation/res/values/template-styles.xml b/samples/browseable/ActivityInstrumentation/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/ActivityInstrumentation/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/AdapterTransition/AndroidManifest.xml b/samples/browseable/AdapterTransition/AndroidManifest.xml
new file mode 100644
index 0000000..01b414d
--- /dev/null
+++ b/samples/browseable/AdapterTransition/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.adaptertransition"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk
+        android:minSdkVersion="19"
+        android:targetSdkVersion="19"/>
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme">
+        <activity
+            android:name="com.example.android.adaptertransition.MainActivity"
+            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/browseable/AdapterTransition/_index.jd b/samples/browseable/AdapterTransition/_index.jd
new file mode 100644
index 0000000..d5c48a3
--- /dev/null
+++ b/samples/browseable/AdapterTransition/_index.jd
@@ -0,0 +1,12 @@
+
+
+
+page.tags="AdapterTransition"
+sample.group=UI
+@jd:body
+
+<p>
+{@link android.transition.Transition Transition} cannot be directly applied to 
+{@link android.widget.AdapterView AdapterView}s. This sample demonstrates how to 
+create a overlay layout and how to run a transition on it.
+</p>
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_grid.png
new file mode 100644
index 0000000..e04f4a7
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_grid.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_list.png
new file mode 100644
index 0000000..4131dba
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_list.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b7a67c0
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_grid.png
new file mode 100644
index 0000000..f2a83e3
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_grid.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_list.png
new file mode 100644
index 0000000..e248a48
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_list.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..1c9fc09
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p1.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p1.jpg
new file mode 100644
index 0000000..10f07ac
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p1.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p10.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p10.jpg
new file mode 100644
index 0000000..4272f4c
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p10.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p11.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p11.jpg
new file mode 100644
index 0000000..c5722b2
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p11.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p2.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p2.jpg
new file mode 100644
index 0000000..ca380ae
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p2.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p3.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p3.jpg
new file mode 100644
index 0000000..6fc71e7
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p3.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p4.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p4.jpg
new file mode 100644
index 0000000..153c1ff
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p4.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p5.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p5.jpg
new file mode 100644
index 0000000..46d6a13
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p5.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p6.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p6.jpg
new file mode 100644
index 0000000..89ccb83
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p6.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p7.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p7.jpg
new file mode 100644
index 0000000..7e9546d
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p7.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p8.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p8.jpg
new file mode 100644
index 0000000..21e25ba
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p8.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p9.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p9.jpg
new file mode 100644
index 0000000..79854cb
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-nodpi/p9.jpg
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_grid.png
new file mode 100644
index 0000000..ecd39b5
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_grid.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_list.png
new file mode 100644
index 0000000..e7e510d
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_list.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..11b9928
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_grid.png
new file mode 100644
index 0000000..3ba98fc
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_grid.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_list.png
new file mode 100644
index 0000000..d187732
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_list.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f136c9f
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/AdapterTransition/res/layout/activity_main.xml b/samples/browseable/AdapterTransition/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/AdapterTransition/res/layout/fragment_adapter_transition.xml b/samples/browseable/AdapterTransition/res/layout/fragment_adapter_transition.xml
new file mode 100644
index 0000000..22ec090
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout/fragment_adapter_transition.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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.
+-->
+<FrameLayout 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"
+    tools:context="com.example.android.adaptertransition.AdapterTransitionFragment">
+
+    <FrameLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <FrameLayout
+        android:id="@+id/cover"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#f3f3f3"
+        android:visibility="invisible"/>
+
+</FrameLayout>
diff --git a/samples/browseable/AdapterTransition/res/layout/fragment_meat_grid.xml b/samples/browseable/AdapterTransition/res/layout/fragment_meat_grid.xml
new file mode 100644
index 0000000..9a4f7a1
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout/fragment_meat_grid.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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.
+-->
+<GridView
+    android:id="@+id/abs_list_view"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipToPadding="false"
+    android:columnWidth="150dp"
+    android:horizontalSpacing="1dp"
+    android:numColumns="auto_fit"
+    android:padding="1dp"
+    android:scrollbars="none"
+    android:stretchMode="columnWidth"
+    android:verticalSpacing="1dp"/>
diff --git a/samples/browseable/AdapterTransition/res/layout/fragment_meat_list.xml b/samples/browseable/AdapterTransition/res/layout/fragment_meat_list.xml
new file mode 100644
index 0000000..4523b26
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout/fragment_meat_list.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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.
+-->
+<ListView
+    android:id="@+id/abs_list_view"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
diff --git a/samples/browseable/AdapterTransition/res/layout/item_meat_grid.xml b/samples/browseable/AdapterTransition/res/layout/item_meat_grid.xml
new file mode 100644
index 0000000..d7fb77a
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout/item_meat_grid.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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
+    android:id="@+id/meat_container"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="150dp">
+
+    <ImageView
+        android:id="@+id/meat_image"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="centerCrop"
+        tools:src="@drawable/p1"/>
+
+    <TextView
+        android:id="@+id/meat_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_gravity="bottom|end"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:gravity="center_horizontal"
+        android:shadowColor="#000000"
+        android:shadowDx="0"
+        android:shadowDy="0"
+        android:shadowRadius="10"
+        android:textColor="#ffffff"
+        android:textSize="24sp"
+        android:textStyle="bold"
+        tools:text="Hello"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/layout/item_meat_list.xml b/samples/browseable/AdapterTransition/res/layout/item_meat_list.xml
new file mode 100644
index 0000000..8d75b90
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/layout/item_meat_list.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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
+    android:id="@+id/meat_container"
+    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="horizontal"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+
+    <ImageView
+        android:id="@+id/meat_image"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:scaleType="centerCrop"
+        tools:src="@drawable/p1"/>
+
+    <TextView
+        android:id="@+id/meat_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
+        android:layout_toEndOf="@id/meat_image"
+        android:layout_centerInParent="true"
+        android:gravity="center_vertical"
+        android:textSize="24sp"
+        tools:text="Title"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/menu/fragment_adapter_transition.xml b/samples/browseable/AdapterTransition/res/menu/fragment_adapter_transition.xml
new file mode 100644
index 0000000..10057b8
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/menu/fragment_adapter_transition.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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/action_toggle"
+        android:icon="@drawable/ic_action_grid"
+        android:showAsAction="always|withText"
+        android:title="@string/show_as_grid"/>
+</menu>
diff --git a/samples/browseable/AdapterTransition/res/menu/main.xml b/samples/browseable/AdapterTransition/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml b/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml
copy to samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml b/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml
copy to samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/base-strings.xml b/samples/browseable/AdapterTransition/res/values/base-strings.xml
new file mode 100644
index 0000000..ee0c4bd
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/values/base-strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">AdapterTransition</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+	    Transition cannot be directly applied to AdapterViews. In this sample, we demonstrate how to create an overlay layout and run a Transition on it. Press the action bar button to toggle between ListView and GridView.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/strings.xml b/samples/browseable/AdapterTransition/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/AdapterTransition/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/AdapterTransition/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/AdapterTransition/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/AdapterTransition/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/AdapterTransitionFragment.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/AdapterTransitionFragment.java
new file mode 100644
index 0000000..a949818
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/AdapterTransitionFragment.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2014 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.adaptertransition;
+
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.Fragment;
+import android.transition.AutoTransition;
+import android.transition.Scene;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.FrameLayout;
+import android.widget.GridView;
+import android.widget.ListView;
+import android.widget.Toast;
+
+/**
+ * Main screen for AdapterTransition sample.
+ */
+public class AdapterTransitionFragment extends Fragment implements Transition.TransitionListener {
+
+    /**
+     * Since the transition framework requires all relevant views in a view hierarchy to be marked
+     * with IDs, we use this ID to mark the root view.
+     */
+    private static final int ROOT_ID = 1;
+
+    /**
+     * A tag for saving state whether the mAbsListView is ListView or GridView.
+     */
+    private static final String STATE_IS_LISTVIEW = "is_listview";
+
+    /**
+     * This is where we place our AdapterView (ListView / GridView).
+     */
+    private FrameLayout mContent;
+
+    /**
+     * This is where we carry out the transition.
+     */
+    private FrameLayout mCover;
+
+    /**
+     * This list shows our contents. It can be ListView or GridView, and we toggle between them
+     * using the transition framework.
+     */
+    private AbsListView mAbsListView;
+
+    /**
+     * This is our contents.
+     */
+    private MeatAdapter mAdapter;
+
+    public static AdapterTransitionFragment newInstance() {
+        return new AdapterTransitionFragment();
+    }
+
+    public AdapterTransitionFragment() {
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // If savedInstanceState is available, we restore the state whether the list is a ListView
+        // or a GridView.
+        boolean isListView;
+        if (null == savedInstanceState) {
+            isListView = true;
+        } else {
+            isListView = savedInstanceState.getBoolean(STATE_IS_LISTVIEW, true);
+        }
+        inflateAbsList(inflater, container, isListView);
+        return inflater.inflate(R.layout.fragment_adapter_transition, container, false);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(STATE_IS_LISTVIEW, mAbsListView instanceof ListView);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        // Retaining references for FrameLayouts that we use later.
+        mContent = (FrameLayout) view.findViewById(R.id.content);
+        mCover = (FrameLayout) view.findViewById(R.id.cover);
+        // We are attaching the list to the screen here.
+        mContent.addView(mAbsListView);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.fragment_adapter_transition, menu);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        // We change the look of the icon every time the user toggles between list and grid.
+        MenuItem item = menu.findItem(R.id.action_toggle);
+        if (null != item) {
+            if (mAbsListView instanceof ListView) {
+                item.setIcon(R.drawable.ic_action_grid);
+                item.setTitle(R.string.show_as_grid);
+            } else {
+                item.setIcon(R.drawable.ic_action_list);
+                item.setTitle(R.string.show_as_list);
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_toggle: {
+                toggle();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void onTransitionStart(Transition transition) {
+    }
+
+    // BEGIN_INCLUDE(on_transition_end)
+    @Override
+    public void onTransitionEnd(Transition transition) {
+        // When the transition ends, we remove all the views from the overlay and hide it.
+        mCover.removeAllViews();
+        mCover.setVisibility(View.INVISIBLE);
+    }
+    // END_INCLUDE(on_transition_end)
+
+    @Override
+    public void onTransitionCancel(Transition transition) {
+    }
+
+    @Override
+    public void onTransitionPause(Transition transition) {
+    }
+
+    @Override
+    public void onTransitionResume(Transition transition) {
+    }
+
+    /**
+     * Inflate a ListView or a GridView with a corresponding ListAdapter.
+     *
+     * @param inflater The LayoutInflater.
+     * @param container The ViewGroup that contains this AbsListView. The AbsListView won't be
+     *                  attached to it.
+     * @param inflateListView Pass true to inflate a ListView, or false to inflate a GridView.
+     */
+    private void inflateAbsList(LayoutInflater inflater, ViewGroup container,
+                                boolean inflateListView) {
+        if (inflateListView) {
+            mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_list,
+                    container, false);
+            mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list);
+        } else {
+            mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_grid,
+                    container, false);
+            mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
+        }
+        mAbsListView.setAdapter(mAdapter);
+        mAbsListView.setOnItemClickListener(mAdapter);
+    }
+
+    /**
+     * Toggle the UI between ListView and GridView.
+     */
+    private void toggle() {
+        // We use mCover as the overlay on which we carry out the transition.
+        mCover.setVisibility(View.VISIBLE);
+        // This FrameLayout holds all the visible views in the current list or grid. We use this as
+        // the starting Scene of the Transition later.
+        FrameLayout before = copyVisibleViews();
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
+        mCover.addView(before, params);
+        // Swap the actual list.
+        swapAbsListView();
+        // We also swap the icon for the toggle button.
+        ActivityCompat.invalidateOptionsMenu(getActivity());
+        // It is now ready to start the transition.
+        mAbsListView.post(new Runnable() {
+            @Override
+            public void run() {
+                // BEGIN_INCLUDE(transition_with_listener)
+                Scene scene = new Scene(mCover, copyVisibleViews());
+                Transition transition = new AutoTransition();
+                transition.addListener(AdapterTransitionFragment.this);
+                TransitionManager.go(scene, transition);
+                // END_INCLUDE(transition_with_listener)
+            }
+        });
+    }
+
+    /**
+     * Swap ListView with GridView, or GridView with ListView.
+     */
+    private void swapAbsListView() {
+        // We save the current scrolling position before removing the current list.
+        int first = mAbsListView.getFirstVisiblePosition();
+        // If the current list is a GridView, we replace it with a ListView. If it is a ListView,
+        // a GridView.
+        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        inflateAbsList(inflater, (ViewGroup) mAbsListView.getParent(),
+                mAbsListView instanceof GridView);
+        mAbsListView.setAdapter(mAdapter);
+        // We restore the scrolling position here.
+        mAbsListView.setSelection(first);
+        // The new list is ready, and we replace the existing one with it.
+        mContent.removeAllViews();
+        mContent.addView(mAbsListView);
+    }
+
+    /**
+     * Copy all the visible views in the mAbsListView into a new FrameLayout and return it.
+     *
+     * @return a FrameLayout with all the visible views inside.
+     */
+    private FrameLayout copyVisibleViews() {
+        // This is the FrameLayout we return afterwards.
+        FrameLayout layout = new FrameLayout(getActivity());
+        // The transition framework requires to set ID for all views to be animated.
+        layout.setId(ROOT_ID);
+        // We only copy visible views.
+        int first = mAbsListView.getFirstVisiblePosition();
+        int index = 0;
+        while (true) {
+            // This is one of the views that we copy. Note that the argument for getChildAt is a
+            // zero-oriented index, and it doesn't usually match with its position in the list.
+            View source = mAbsListView.getChildAt(index);
+            if (null == source) {
+                break;
+            }
+            // This is the copy of the original view.
+            View destination = mAdapter.getView(first + index, null, layout);
+            assert destination != null;
+            destination.setId(ROOT_ID + first + index);
+            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                    source.getWidth(), source.getHeight());
+            params.leftMargin = (int) source.getX();
+            params.topMargin = (int) source.getY();
+            layout.addView(destination, params);
+            ++index;
+        }
+        return layout;
+    }
+
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
new file mode 100644
index 0000000..a45632c
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.adaptertransition;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        AdapterTransitionFragment fragment = new AdapterTransitionFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java
new file mode 100644
index 0000000..bca1c5f
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 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.adaptertransition;
+
+/**
+ * Sample data.
+ */
+public class Meat {
+
+    public int resourceId;
+    public String title;
+
+    public Meat(int resourceId, String title) {
+        this.resourceId = resourceId;
+        this.title = title;
+    }
+
+    public static final Meat[] MEATS = {
+            new Meat(R.drawable.p1, "First"),
+            new Meat(R.drawable.p2, "Second"),
+            new Meat(R.drawable.p3, "Third"),
+            new Meat(R.drawable.p4, "Fourth"),
+            new Meat(R.drawable.p5, "Fifth"),
+            new Meat(R.drawable.p6, "Sixth"),
+            new Meat(R.drawable.p7, "Seventh"),
+            new Meat(R.drawable.p8, "Eighth"),
+            new Meat(R.drawable.p9, "Ninth"),
+            new Meat(R.drawable.p10, "Tenth"),
+            new Meat(R.drawable.p11, "Eleventh"),
+    };
+
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java
new file mode 100644
index 0000000..dea4435
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 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.adaptertransition;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * This class provides data as Views. It is designed to support both ListView and GridView by
+ * changing a layout resource file to inflate.
+ */
+public class MeatAdapter extends BaseAdapter implements AbsListView.OnItemClickListener {
+
+    private final LayoutInflater mLayoutInflater;
+    private final int mResourceId;
+
+    /**
+     * Create a new instance of {@link MeatAdapter}.
+     *
+     * @param inflater   The layout inflater.
+     * @param resourceId The resource ID for the layout to be used. The layout should contain an
+     *                   ImageView with ID of "meat_image" and a TextView with ID of "meat_title".
+     */
+    public MeatAdapter(LayoutInflater inflater, int resourceId) {
+        mLayoutInflater = inflater;
+        mResourceId = resourceId;
+    }
+
+    @Override
+    public int getCount() {
+        return Meat.MEATS.length;
+    }
+
+    @Override
+    public Meat getItem(int position) {
+        return Meat.MEATS[position];
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return Meat.MEATS[position].resourceId;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        final View view;
+        final ViewHolder holder;
+        if (null == convertView) {
+            view = mLayoutInflater.inflate(mResourceId, parent, false);
+            holder = new ViewHolder();
+            assert view != null;
+            holder.image = (ImageView) view.findViewById(R.id.meat_image);
+            holder.title = (TextView) view.findViewById(R.id.meat_title);
+            view.setTag(holder);
+        } else {
+            view = convertView;
+            holder = (ViewHolder) view.getTag();
+        }
+        Meat meat = getItem(position);
+        holder.image.setImageResource(meat.resourceId);
+        holder.title.setText(meat.title);
+        return view;
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        ViewHolder holder = (ViewHolder) view.getTag();
+        Context context = view.getContext();
+        if (null != holder && null != holder.title && null != context) {
+            Toast.makeText(context, context.getString(R.string.item_clicked,
+                    holder.title.getText()), Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private static class ViewHolder {
+        public ImageView image;
+        public TextView title;
+    }
+
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/AdvancedImmersiveMode/_index.jd b/samples/browseable/AdvancedImmersiveMode/_index.jd
index 2b19c9c..7048fe8 100644
--- a/samples/browseable/AdvancedImmersiveMode/_index.jd
+++ b/samples/browseable/AdvancedImmersiveMode/_index.jd
@@ -5,10 +5,15 @@
 sample.group=UI
 @jd:body
 
-<p>Android 4.4 introduces a way for you to provide a more immersive screen
-experience in your app, by letting users show or hide the status bar and
-navigation bar with a swipe.</p>
-<p>This sample demonstrates how this features interacts with some of the other
+<p>
+Android 4.4 introduces a way for you to provide a more immersive screen
+experience in your app by letting users show or hide the status bar and
+the navigation bar with a swipe.
+</p>
+
+<p>
+This sample demonstrates how this feature interacts with some of the other
 UI flags related to full-screen apps. The sample also shows how to implement a
 "sticky" mode, which re-hides the bars a few seconds after the user swipes
-them back in.</p>
+them back in.
+</p>
\ No newline at end of file
diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png
index b1efaf4..b96e6a5 100644
--- a/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png
index f5f9244..1294d5b 100644
--- a/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..9f101ff 100644
--- a/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..7a195a1 100644
--- a/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdvancedImmersiveMode/res/layout-w720dp/activity_main.xml b/samples/browseable/AdvancedImmersiveMode/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/AdvancedImmersiveMode/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml b/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml
index bc5a575..1ae4f98 100755
--- a/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml
+++ b/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml
@@ -14,25 +14,52 @@
   limitations under the License.
   -->
 <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:orientation="vertical"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:id="@+id/sample_main_layout">
-    <TextView android:id="@+id/sample_output"
-              style="@style/Widget.SampleMessage"
-              android:layout_weight="1"
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
               android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
     <View
-            android:layout_width="fill_parent"
-            android:layout_height="1dp"
-            android:background="@android:color/darker_gray"/>
-    <fragment
-            android:name="com.example.android.common.logger.LogFragment"
-            android:id="@+id/log_fragment"
-            android:layout_weight="1"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" />
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
 </LinearLayout>
+
diff --git a/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml b/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml
new file mode 100644
index 0000000..2c74e83
--- /dev/null
+++ b/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/flag_enable_lowprof"
+        android:text="Enable Low Profile Mode" />
+
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/flag_hide_navbar"
+        android:text="Hide Navigation bar" />
+
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/flag_hide_statbar"
+        android:text="Hide Status Bar" />
+
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/flag_enable_immersive"
+        android:text="Enable Immersive Mode" />
+
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/flag_enable_immersive_sticky"
+        android:text="Enable Immersive Mode (Sticky)" />
+
+    <Button
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Do things!"
+        android:id="@+id/btn_changeFlags" />
+
+
+    <TextView
+        android:layout_marginTop="@dimen/margin_large"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Common flag presets"/>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal" android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Immersive Mode"
+            android:id="@+id/btn_immersive" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Leanback Mode"
+            android:id="@+id/btn_leanback" />
+
+    </LinearLayout>
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml b/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml
index 2c3515d..b49c2c5 100644
--- a/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml
+++ b/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml
@@ -15,7 +15,7 @@
   -->
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/sample_action"
-          android:showAsAction="ifRoom|withText"
-          android:title="@string/sample_action" />
+    <item android:id="@+id/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
 </menu>
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/dimens.xml
rename to samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/styles.xml
rename to samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values-v11/template-styles.xml b/samples/browseable/AdvancedImmersiveMode/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/AdvancedImmersiveMode/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml b/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml
index 305e12a..8e4c710 100644
--- a/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml
+++ b/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml
@@ -23,13 +23,10 @@
         <![CDATA[
         
             
-            \"Immersive Mode\" is a new UI mode which improves \"hide full screen\" and
+            \n\n\n\"Immersive Mode\", added in Android 4.4, improves the \"hide full screen\" and
             \"hide nav bar\" modes, by letting users swipe the bars in and out.  This sample
-            lets the user experiment with immersive mode by enabling it and seeing how it interacts
+            lets the user experiment with immersive mode by seeing how it interacts
             with some of the other UI flags related to full-screen apps.
-            \n\nThis sample also lets the user choose between normal immersive mode and "sticky"
-            immersive mode, which removes the status bar and nav bar
-            a few seconds after the user has swiped them back in.
             
         
         ]]>
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml b/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml
old mode 100644
new mode 100755
index a65b891..7b9d9ec
--- a/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml
+++ b/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml
@@ -1,22 +1,19 @@
-<?xml version="1.0" encoding="UTF-8"?>
 <!--
- Copyright 2013 The Android Open Source Project
+  Copyright 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
+  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
+      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.
+  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="sample_action">Try these settings!</string>
+    <string name="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
 </resources>
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/styles.xml b/samples/browseable/AdvancedImmersiveMode/res/values/styles.xml
deleted file mode 100644
index d3f82ff..0000000
--- a/samples/browseable/AdvancedImmersiveMode/res/values/styles.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="AppTheme" parent="Theme.Base" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-
-    <style name="Widget.SampleOutput">
-        <item name="android:padding">@dimen/margin_medium</item>
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Log" parent="Widget.SampleOutput">
-        <item name="android:typeface">monospace</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml b/samples/browseable/AdvancedImmersiveMode/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml
rename to samples/browseable/AdvancedImmersiveMode/res/values/template-dimens.xml
diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/template-styles.xml b/samples/browseable/AdvancedImmersiveMode/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/AdvancedImmersiveMode/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java
index fe11ecb..d8fb0d4 100644
--- a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java
+++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java
@@ -17,9 +17,10 @@
 
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
-import android.view.MenuItem;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 import android.widget.CheckBox;
 
 import com.example.android.common.logger.Log;
@@ -46,49 +47,136 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
+        final View flagsView = inflater.inflate(R.layout.fragment_flags, container, false);
+        mLowProfileCheckBox = (CheckBox) flagsView.findViewById(R.id.flag_enable_lowprof);
+        mHideNavCheckbox = (CheckBox) flagsView.findViewById(R.id.flag_hide_navbar);
+        mHideStatusBarCheckBox = (CheckBox) flagsView.findViewById(R.id.flag_hide_statbar);
+        mImmersiveModeCheckBox = (CheckBox) flagsView.findViewById(R.id.flag_enable_immersive);
+        mImmersiveModeStickyCheckBox =
+                (CheckBox) flagsView.findViewById(R.id.flag_enable_immersive_sticky);
 
-        final View decorView = getActivity().getWindow().getDecorView();
-        ViewGroup parentView = (ViewGroup) getActivity().getWindow().getDecorView()
-                .findViewById(R.id.sample_main_layout);
+        Button toggleFlagsButton = (Button) flagsView.findViewById(R.id.btn_changeFlags);
+        toggleFlagsButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                toggleUiFlags();
+            }
+        });
 
-        mLowProfileCheckBox = new CheckBox(getActivity());
-        mLowProfileCheckBox.setText("Enable Low Profile mode.");
-        parentView.addView(mLowProfileCheckBox);
+        Button presetsImmersiveModeButton = (Button) flagsView.findViewById(R.id.btn_immersive);
+        presetsImmersiveModeButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
 
-        mHideNavCheckbox = new CheckBox(getActivity());
-        mHideNavCheckbox.setChecked(true);
-        mHideNavCheckbox.setText("Hide Navigation bar");
-        parentView.addView(mHideNavCheckbox);
+                // BEGIN_INCLUDE(immersive_presets)
+                // For immersive mode, the FULLSCREEN, HIDE_HAVIGATION and IMMERSIVE
+                // flags should be set (you can use IMMERSIVE_STICKY instead of IMMERSIVE
+                // as appropriate for your app).  The LOW_PROFILE flag should be cleared.
 
-        mHideStatusBarCheckBox = new CheckBox(getActivity());
-        mHideStatusBarCheckBox.setChecked(true);
-        mHideStatusBarCheckBox.setText("Hide Status Bar");
-        parentView.addView(mHideStatusBarCheckBox);
+                // Immersive mode is primarily for situations where the user will be
+                // interacting with the screen, like games or reading books.
+                int uiOptions = flagsView.getSystemUiVisibility();
+                uiOptions &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
+                uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE;
+                uiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+                flagsView.setSystemUiVisibility(uiOptions);
+                // END_INCLUDE(immersive_presets)
 
-        mImmersiveModeCheckBox = new CheckBox(getActivity());
-        mImmersiveModeCheckBox.setText("Enable Immersive Mode.");
-        parentView.addView(mImmersiveModeCheckBox);
+                dumpFlagStateToLog(uiOptions);
 
-        mImmersiveModeStickyCheckBox = new CheckBox(getActivity());
-        mImmersiveModeStickyCheckBox.setText("Enable Immersive Mode (Sticky)");
-        parentView.addView(mImmersiveModeStickyCheckBox);
+                // The below code just updates the checkboxes to reflect which flags have been set.
+                mLowProfileCheckBox.setChecked(false);
+                mHideNavCheckbox.setChecked(true);
+                mHideStatusBarCheckBox.setChecked(true);
+                mImmersiveModeCheckBox.setChecked(true);
+                mImmersiveModeStickyCheckBox.setChecked(false);
+            }
+        });
 
+
+        Button presetsLeanbackModeButton = (Button) flagsView.findViewById(R.id.btn_leanback);
+        presetsLeanbackModeButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                // BEGIN_INCLUDE(leanback_presets)
+                // For leanback mode, only the HIDE_NAVE and HIDE_STATUSBAR flags
+                // should be checked.  In this case IMMERSIVE should *not* be set,
+                // since this mode is left as soon as the user touches the screen.
+                int uiOptions = flagsView.getSystemUiVisibility();
+                uiOptions &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
+                uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                uiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE;
+                uiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+                flagsView.setSystemUiVisibility(uiOptions);
+                // END_INCLUDE(leanback_presets)
+
+                dumpFlagStateToLog(uiOptions);
+
+                // The below code just updates the checkboxes to reflect which flags have been set.
+                mLowProfileCheckBox.setChecked(false);
+                mHideNavCheckbox.setChecked(true);
+                mHideStatusBarCheckBox.setChecked(true);
+                mImmersiveModeCheckBox.setChecked(false);
+                mImmersiveModeStickyCheckBox.setChecked(false);
+            }
+        });
+
+        // Setting these flags makes the content appear under the navigation
+        // bars, so that showing/hiding the nav bars doesn't resize the content
+        // window, which can be jarring.
+        int uiOptions = flagsView.getSystemUiVisibility();
+        uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+        uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+        uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        flagsView.setSystemUiVisibility(uiOptions);
+
+        return flagsView;
     }
 
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item.getItemId() == R.id.sample_action) {
-            toggleImmersiveMode();
+    /**
+     * Helper method to dump flag state to the log.
+     * @param uiFlags Set of UI flags to inspect
+     */
+    public void dumpFlagStateToLog(int uiFlags) {
+        if ((uiFlags & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+            Log.i(TAG, "SYSTEM_UI_FLAG_LOW_PROFILE is set");
+        } else {
+            Log.i(TAG, "SYSTEM_UI_FLAG_LOW_PROFILE is unset");
         }
-        return true;
+
+        if ((uiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
+            Log.i(TAG, "SYSTEM_UI_FLAG_FULLSCREEN is set");
+        } else {
+            Log.i(TAG, "SYSTEM_UI_FLAG_FULLSCREEN is unset");
+        }
+
+        if ((uiFlags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
+            Log.i(TAG, "SYSTEM_UI_FLAG_HIDE_NAVIGATION is set");
+        } else {
+            Log.i(TAG, "SYSTEM_UI_FLAG_HIDE_NAVIGATION is unset");
+        }
+
+        if ((uiFlags & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0) {
+            Log.i(TAG, "SYSTEM_UI_FLAG_IMMERSIVE is set");
+        } else {
+            Log.i(TAG, "SYSTEM_UI_FLAG_IMMERSIVE is unset");
+        }
+
+        if ((uiFlags & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0) {
+            Log.i(TAG, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY is set");
+        } else {
+            Log.i(TAG, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY is unset");
+        }
     }
 
     /**
      * Detects and toggles immersive mode (also known as "hidey bar" mode).
      */
-    public void toggleImmersiveMode() {
+    public void toggleUiFlags() {
 
         // BEGIN_INCLUDE (get_current_ui_flags)
         // The "Decor View" is the parent view of the Activity.  It's also conveniently the easiest
@@ -168,7 +256,8 @@
         // BEGIN_INCLUDE (set_ui_flags)
         //Set the new UI flags.
         decorView.setSystemUiVisibility(newUiOptions);
-        Log.i(TAG, "Current height: " + decorView.getHeight() + ", width: " + decorView.getWidth());
         // END_INCLUDE (set_ui_flags)
+
+        dumpFlagStateToLog(uiOptions);
     }
 }
diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java
index 0ebe878..e323557 100644
--- a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java
+++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java
@@ -22,6 +22,8 @@
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
 
 import com.example.android.common.activities.SampleActivityBase;
 import com.example.android.common.logger.Log;
@@ -30,26 +32,28 @@
 import com.example.android.common.logger.MessageOnlyLogFilter;
 
 /**
- * A simple launcher activity containing a summary sample description
- * and a few action bar buttons.
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
  */
 public class MainActivity extends SampleActivityBase {
 
     public static final String TAG = "MainActivity";
 
-    public static final String FRAGTAG = "AdvancedImmersiveModeFragment";
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
-        if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
-            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-            AdvancedImmersiveModeFragment fragment = new AdvancedImmersiveModeFragment();
-            transaction.add(fragment, FRAGTAG);
-            transaction.commit();
-        }
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        AdvancedImmersiveModeFragment fragment = new AdvancedImmersiveModeFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
     }
 
     @Override
@@ -58,6 +62,32 @@
         return true;
     }
 
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
     /** Create a chain of targets that will receive log data */
     @Override
     public void initializeLogging() {
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml b/samples/browseable/AppRestrictions/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml
rename to samples/browseable/AppRestrictions/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml b/samples/browseable/AppRestrictions/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml
rename to samples/browseable/AppRestrictions/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AppRestrictions/res/values-v11/template-styles.xml b/samples/browseable/AppRestrictions/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/AppRestrictions/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/styles.xml b/samples/browseable/AppRestrictions/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/AppRestrictions/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/AppRestrictions/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/AppRestrictions/res/values/dimens.xml
rename to samples/browseable/AppRestrictions/res/values/template-dimens.xml
diff --git a/samples/browseable/AppRestrictions/res/values/template-styles.xml b/samples/browseable/AppRestrictions/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/AppRestrictions/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/Basic/_index.jd b/samples/browseable/Basic/_index.jd
deleted file mode 100644
index 72cffd9..0000000
--- a/samples/browseable/Basic/_index.jd
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-page.tags="Basic ActionBarCompat"
-sample.group=UI
-@jd:body
-
-<p>This sample demonstrates how to create a basic action bar that displays
-action items. The sample shows how to inflate items from a menu resource, and
-how to add items programatically. To reduce clutter, rarely used actions are
-displayed in an action bar overflow.</p>
-<p>The activity in this sample extends from
-{@link android.support.v7.app.ActionBarActivity}, which provides the
-functionality necessary to display a compatible action bar on devices
-running Android 2.1 and higher.</p>
diff --git a/samples/browseable/Basic/res/values/base-strings.xml b/samples/browseable/Basic/res/values/base-strings.xml
deleted file mode 100644
index ff084ef..0000000
--- a/samples/browseable/Basic/res/values/base-strings.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 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">Basic</string>
-    <string name="intro_message">
-        <![CDATA[
-        
-            
-            This sample shows you how to use ActionBarCompat to create a basic Activity which
-            displays action items. It covers inflating items from a menu resource, as well as adding
-            an item in code. Items that are not shown as action items on the Action Bar are
-            displayed in the action bar overflow.
-            
-        
-        ]]>
-    </string>
-</resources>
diff --git a/samples/browseable/Basic/res/values/styles.xml b/samples/browseable/Basic/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/Basic/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicAccessibility/res/values-sw600dp/dimens.xml b/samples/browseable/BasicAccessibility/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicAccessibility/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicAccessibility/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicAccessibility/res/values-sw600dp/styles.xml b/samples/browseable/BasicAccessibility/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicAccessibility/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicAccessibility/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicAccessibility/res/values-v11/template-styles.xml b/samples/browseable/BasicAccessibility/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicAccessibility/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicAccessibility/res/values/dimens.xml b/samples/browseable/BasicAccessibility/res/values/dimens.xml
index 39e710b..a5e4ebe 100644
--- a/samples/browseable/BasicAccessibility/res/values/dimens.xml
+++ b/samples/browseable/BasicAccessibility/res/values/dimens.xml
@@ -1,32 +1,20 @@
 <!--
-  Copyright 2013 The Android Open Source Project
+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
+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
+     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.
-  -->
-
+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>
-
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
-
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 </resources>
diff --git a/samples/browseable/BasicAccessibility/res/values/styles.xml b/samples/browseable/BasicAccessibility/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/BasicAccessibility/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/BasicAccessibility/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/BasicAccessibility/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicAccessibility/res/values/template-styles.xml b/samples/browseable/BasicAccessibility/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicAccessibility/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicAndroidKeyStore/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/BasicAndroidKeyStore/res/layout-sw600dp-land/activity_main.xml
new file mode 100644
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/BasicAndroidKeyStore/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/BasicAndroidKeyStore/res/layout-sw600dp/activity_main.xml b/samples/browseable/BasicAndroidKeyStore/res/layout-sw600dp/activity_main.xml
new file mode 100644
index 0000000..11cd71b
--- /dev/null
+++ b/samples/browseable/BasicAndroidKeyStore/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,36 @@
+<!--
+  Copyright 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:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/intro_message"
+              android:padding="16dp"
+              android:layout_margin="16dp"/>
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml b/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml
index bc5a575..6f41369 100644
--- a/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml
+++ b/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml
@@ -24,7 +24,8 @@
               android:layout_weight="1"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:text="@string/intro_message"
+              android:padding="16dp" />
     <View
             android:layout_width="fill_parent"
             android:layout_height="1dp"
diff --git a/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/dimens.xml b/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/styles.xml b/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicAndroidKeyStore/res/values-v11/template-styles.xml b/samples/browseable/BasicAndroidKeyStore/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicAndroidKeyStore/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/styles.xml b/samples/browseable/BasicAndroidKeyStore/res/values/styles.xml
deleted file mode 100644
index d3f82ff..0000000
--- a/samples/browseable/BasicAndroidKeyStore/res/values/styles.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="AppTheme" parent="Theme.Base" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-
-    <style name="Widget.SampleOutput">
-        <item name="android:padding">@dimen/margin_medium</item>
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Log" parent="Widget.SampleOutput">
-        <item name="android:typeface">monospace</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/dimens.xml b/samples/browseable/BasicAndroidKeyStore/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicAndroidKeyStore/res/values/dimens.xml
rename to samples/browseable/BasicAndroidKeyStore/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/template-styles.xml b/samples/browseable/BasicAndroidKeyStore/res/values/template-styles.xml
new file mode 100644
index 0000000..cfffcbd
--- /dev/null
+++ b/samples/browseable/BasicAndroidKeyStore/res/values/template-styles.xml
@@ -0,0 +1,54 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+    <style name="AppTheme" parent="Theme.Base" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+        <item name="android:layout_margin">16dp</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+
+    <style name="Widget.SampleOutput">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java
index 9f16565..7711f35 100644
--- a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java
+++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java
@@ -19,6 +19,7 @@
 
 package com.example.android.basicandroidkeystore;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
@@ -72,6 +73,8 @@
         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
                 .findFragmentById(R.id.log_fragment);
         msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
 
         Log.i(TAG, "Ready");
     }
diff --git a/samples/browseable/BasicContactables/res/values-sw600dp/dimens.xml b/samples/browseable/BasicContactables/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicContactables/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicContactables/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicContactables/res/values-sw600dp/styles.xml b/samples/browseable/BasicContactables/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicContactables/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicContactables/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicContactables/res/values-v11/template-styles.xml b/samples/browseable/BasicContactables/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicContactables/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicContactables/res/values/styles.xml b/samples/browseable/BasicContactables/res/values/styles.xml
index 404623e..c3a400d 100644
--- a/samples/browseable/BasicContactables/res/values/styles.xml
+++ b/samples/browseable/BasicContactables/res/values/styles.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   Copyright 2013 The Android Open Source Project
 
@@ -15,28 +16,12 @@
   -->
 
 <resources>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
     <!-- Widget styling -->
 
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
+    <style name="Widget.SampleOutput">
+        <item name="android:padding">@dimen/margin_medium</item>
         <item name="android:textAppearance">?android:textAppearanceMedium</item>
         <item name="android:lineSpacingMultiplier">1.1</item>
     </style>
 
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
 </resources>
diff --git a/samples/browseable/BasicContactables/res/values/dimens.xml b/samples/browseable/BasicContactables/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicContactables/res/values/dimens.xml
rename to samples/browseable/BasicContactables/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicContactables/res/values/template-styles.xml b/samples/browseable/BasicContactables/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicContactables/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicGestureDetect/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/BasicGestureDetect/res/layout-sw600dp-land/activity_main.xml
new file mode 100755
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/BasicGestureDetect/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/BasicGestureDetect/res/layout-sw600dp/activity_main.xml b/samples/browseable/BasicGestureDetect/res/layout-sw600dp/activity_main.xml
new file mode 100755
index 0000000..f6f4157
--- /dev/null
+++ b/samples/browseable/BasicGestureDetect/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:id="@+id/sample_main_layout" >
+
+    <TextView android:id="@+id/sample_output"
+        style="@style/Widget.SampleMessage"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/intro_message"
+        android:padding="16dp"
+        android:layout_margin="16dp"/>
+    <fragment
+        android:name="com.example.android.common.logger.LogFragment"
+        android:id="@+id/log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml b/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml
index bc5a575..6f41369 100755
--- a/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml
+++ b/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml
@@ -24,7 +24,8 @@
               android:layout_weight="1"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:text="@string/intro_message"
+              android:padding="16dp" />
     <View
             android:layout_width="fill_parent"
             android:layout_height="1dp"
diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml b/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  Copyright 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>
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceLarge</item>
-        <item name="android:lineSpacingMultiplier">1.2</item>
-        <item name="android:shadowDy">-6.5</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml b/samples/browseable/BasicGestureDetect/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicGestureDetect/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/template-styles.xml b/samples/browseable/BasicGestureDetect/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..b6ea1a0
--- /dev/null
+++ b/samples/browseable/BasicGestureDetect/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright 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>
+
+    <style name="Widget.SampleMessage">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+
+        <item name="android:layout_margin">16dp</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+
+</resources>
diff --git a/samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml b/samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicGestureDetect/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicGestureDetect/res/values/dimens.xml b/samples/browseable/BasicGestureDetect/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicGestureDetect/res/values/dimens.xml
rename to samples/browseable/BasicGestureDetect/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicGestureDetect/res/values/styles.xml b/samples/browseable/BasicGestureDetect/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicGestureDetect/res/values/styles.xml
rename to samples/browseable/BasicGestureDetect/res/values/template-styles.xml
diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java
index c2d2b6f..2e2921d 100644
--- a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java
+++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java
@@ -42,7 +42,7 @@
 
     @Override
     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
-                            float distanceY) {
+    float distanceY) {
         // User attempted to scroll
         Log.i(TAG, "Scroll");
         return false;
@@ -50,7 +50,7 @@
 
     @Override
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
-                           float velocityY) {
+    float velocityY) {
         // Fling event occurred.  Notification of this one happens after an "up" event.
         Log.i(TAG, "Fling");
         return false;
diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java
index 1547807..de4a39f 100644
--- a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java
+++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java
@@ -19,6 +19,7 @@
 
 package com.example.android.basicgesturedetect;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
@@ -74,6 +75,9 @@
         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
                 .findFragmentById(R.id.log_fragment);
         msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
 
         Log.i(TAG, "Ready");
     }
diff --git a/samples/browseable/BasicImmersiveMode/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/BasicImmersiveMode/res/layout-sw600dp-land/activity_main.xml
new file mode 100755
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/BasicImmersiveMode/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/BasicImmersiveMode/res/layout-sw600dp/activity_main.xml b/samples/browseable/BasicImmersiveMode/res/layout-sw600dp/activity_main.xml
new file mode 100755
index 0000000..f6f4157
--- /dev/null
+++ b/samples/browseable/BasicImmersiveMode/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:id="@+id/sample_main_layout" >
+
+    <TextView android:id="@+id/sample_output"
+        style="@style/Widget.SampleMessage"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/intro_message"
+        android:padding="16dp"
+        android:layout_margin="16dp"/>
+    <fragment
+        android:name="com.example.android.common.logger.LogFragment"
+        android:id="@+id/log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml b/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml
index bc5a575..6f41369 100755
--- a/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml
+++ b/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml
@@ -24,7 +24,8 @@
               android:layout_weight="1"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:text="@string/intro_message"
+              android:padding="16dp" />
     <View
             android:layout_width="fill_parent"
             android:layout_height="1dp"
diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  Copyright 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>
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceLarge</item>
-        <item name="android:lineSpacingMultiplier">1.2</item>
-        <item name="android:shadowDy">-6.5</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicImmersiveMode/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/template-styles.xml b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..b6ea1a0
--- /dev/null
+++ b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright 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>
+
+    <style name="Widget.SampleMessage">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+
+        <item name="android:layout_margin">16dp</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+
+</resources>
diff --git a/samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml b/samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicImmersiveMode/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicImmersiveMode/res/values/dimens.xml b/samples/browseable/BasicImmersiveMode/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicImmersiveMode/res/values/dimens.xml
rename to samples/browseable/BasicImmersiveMode/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicImmersiveMode/res/values/styles.xml b/samples/browseable/BasicImmersiveMode/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicImmersiveMode/res/values/styles.xml
rename to samples/browseable/BasicImmersiveMode/res/values/template-styles.xml
diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java
index 83e8f0e..0c1bcef 100644
--- a/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java
+++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java
@@ -19,6 +19,7 @@
 
 package com.example.android.basicimmersivemode;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
@@ -74,6 +75,9 @@
         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
                 .findFragmentById(R.id.log_fragment);
         msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
 
         Log.i(TAG, "Ready");
     }
diff --git a/samples/browseable/BasicMediaDecoder/res/values-sw600dp/dimens.xml b/samples/browseable/BasicMediaDecoder/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMediaDecoder/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicMediaDecoder/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicMediaDecoder/res/values-sw600dp/styles.xml b/samples/browseable/BasicMediaDecoder/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicMediaDecoder/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicMediaDecoder/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicMediaDecoder/res/values-v11/template-styles.xml b/samples/browseable/BasicMediaDecoder/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicMediaDecoder/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicMediaDecoder/res/values/styles.xml b/samples/browseable/BasicMediaDecoder/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/BasicMediaDecoder/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicMediaDecoder/res/values/dimens.xml b/samples/browseable/BasicMediaDecoder/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMediaDecoder/res/values/dimens.xml
rename to samples/browseable/BasicMediaDecoder/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicMediaDecoder/res/values/template-styles.xml b/samples/browseable/BasicMediaDecoder/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicMediaDecoder/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml b/samples/browseable/BasicMediaRouter/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicMediaRouter/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml b/samples/browseable/BasicMediaRouter/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicMediaRouter/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicMediaRouter/res/values-v11/template-styles.xml b/samples/browseable/BasicMediaRouter/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicMediaRouter/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicMediaRouter/res/values/styles.xml b/samples/browseable/BasicMediaRouter/res/values/styles.xml
index 404623e..4f64054 100644
--- a/samples/browseable/BasicMediaRouter/res/values/styles.xml
+++ b/samples/browseable/BasicMediaRouter/res/values/styles.xml
@@ -1,42 +1,22 @@
-<!--
-  Copyright 2013 The Android Open Source Project
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-  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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
     </style>
 
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
+
+    <style name="DisplayLargeText">
+        <item name="android:textSize">30sp</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">#FFFFFF</item>
     </style>
 
-</resources>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BasicMediaRouter/res/values/dimens.xml b/samples/browseable/BasicMediaRouter/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMediaRouter/res/values/dimens.xml
rename to samples/browseable/BasicMediaRouter/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicMediaRouter/res/values/template-styles.xml b/samples/browseable/BasicMediaRouter/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicMediaRouter/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicMultitouch/res/values-sw600dp/dimens.xml b/samples/browseable/BasicMultitouch/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMultitouch/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicMultitouch/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicMultitouch/res/values-sw600dp/styles.xml b/samples/browseable/BasicMultitouch/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicMultitouch/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicMultitouch/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicMultitouch/res/values-v11/template-styles.xml b/samples/browseable/BasicMultitouch/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicMultitouch/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicMultitouch/res/values/styles.xml b/samples/browseable/BasicMultitouch/res/values/styles.xml
index 404623e..e56d4f8 100644
--- a/samples/browseable/BasicMultitouch/res/values/styles.xml
+++ b/samples/browseable/BasicMultitouch/res/values/styles.xml
@@ -13,30 +13,18 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-
 <resources>
 
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light.NoTitleBar">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
     </style>
 
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BasicMultitouch/res/values/dimens.xml b/samples/browseable/BasicMultitouch/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMultitouch/res/values/dimens.xml
rename to samples/browseable/BasicMultitouch/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicMultitouch/res/values/template-styles.xml b/samples/browseable/BasicMultitouch/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicMultitouch/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicNetworking/res/values-sw600dp/dimens.xml b/samples/browseable/BasicNetworking/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicNetworking/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicNetworking/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicNetworking/res/values-sw600dp/styles.xml b/samples/browseable/BasicNetworking/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicNetworking/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicNetworking/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicNetworking/res/values-v11/template-styles.xml b/samples/browseable/BasicNetworking/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicNetworking/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicNetworking/res/values/styles.xml b/samples/browseable/BasicNetworking/res/values/styles.xml
index 404623e..3450a54 100644
--- a/samples/browseable/BasicNetworking/res/values/styles.xml
+++ b/samples/browseable/BasicNetworking/res/values/styles.xml
@@ -8,7 +8,7 @@
       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,
+  distributed under thegi 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.
@@ -16,27 +16,17 @@
 
 <resources>
 
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
     <!-- Widget styling -->
 
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
+    <style name="Widget.SampleOutput">
+        <item name="android:padding">@dimen/margin_medium</item>
         <item name="android:textAppearance">?android:textAppearanceMedium</item>
         <item name="android:lineSpacingMultiplier">1.1</item>
     </style>
 
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
     </style>
 
+
 </resources>
diff --git a/samples/browseable/BasicNetworking/res/values/dimens.xml b/samples/browseable/BasicNetworking/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicNetworking/res/values/dimens.xml
rename to samples/browseable/BasicNetworking/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicNetworking/res/values/template-styles.xml b/samples/browseable/BasicNetworking/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicNetworking/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicNotifications/res/values-sw600dp/dimens.xml b/samples/browseable/BasicNotifications/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicNotifications/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicNotifications/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicNotifications/res/values-sw600dp/styles.xml b/samples/browseable/BasicNotifications/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicNotifications/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicNotifications/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicNotifications/res/values-v11/template-styles.xml b/samples/browseable/BasicNotifications/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicNotifications/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicNotifications/res/values/styles.xml b/samples/browseable/BasicNotifications/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/BasicNotifications/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicNotifications/res/values/dimens.xml b/samples/browseable/BasicNotifications/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicNotifications/res/values/dimens.xml
rename to samples/browseable/BasicNotifications/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicNotifications/res/values/template-styles.xml b/samples/browseable/BasicNotifications/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicNotifications/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/AndroidManifest.xml b/samples/browseable/BasicRenderScript/AndroidManifest.xml
new file mode 100644
index 0000000..e1e2dfc
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?><!--
+ Copyright 2014 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.
+-->
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.basicrenderscript"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk
+        android:minSdkVersion="8"
+        android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name"
+            android:theme="@style/FullscreenTheme">
+            <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/browseable/BasicRenderScript/_index.jd b/samples/browseable/BasicRenderScript/_index.jd
new file mode 100644
index 0000000..a40a264
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/_index.jd
@@ -0,0 +1,13 @@
+
+
+
+page.tags="BasicRenderScript"
+sample.group=RenderScript
+@jd:body
+
+<p>
+  This sample demonstrates the basic steps for using RenderScript. In this
+  example, the app uses <a href=
+  "{@docRoot}guide/topics/renderscript/compute.html">RenderScript</a> to
+  perform graphical filter operations on a image.
+</p>
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-hdpi/ic_launcher.png
old mode 100644
new mode 100755
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/BasicRenderScript/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicRenderScript/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/BasicRenderScript/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/BasicRenderScript/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..4ccd98e
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicRenderScript/res/drawable-nodpi/data.jpg b/samples/browseable/BasicRenderScript/res/drawable-nodpi/data.jpg
new file mode 100644
index 0000000..81a87b1
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/drawable-nodpi/data.jpg
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-xhdpi/ic_launcher.png
old mode 100644
new mode 100755
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/BasicRenderScript/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicRenderScript/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..3c45f51
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/BasicRenderScript/res/layout/activity_main.xml
similarity index 100%
rename from samples/browseable/Basic/res/layout/activity_main.xml
rename to samples/browseable/BasicRenderScript/res/layout/activity_main.xml
diff --git a/samples/browseable/BasicRenderScript/res/layout/main_layout.xml b/samples/browseable/BasicRenderScript/res/layout/main_layout.xml
new file mode 100644
index 0000000..69695f0
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/layout/main_layout.xml
@@ -0,0 +1,22 @@
+<FrameLayout 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:background="#0099cc"
+    tools:context=".MainActivity">
+
+    <ImageView
+        android:id="@+id/imageView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="fitCenter"
+        android:src="@drawable/data" />
+
+    <SeekBar
+        android:id="@+id/seekBar1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="16dp" />
+
+</FrameLayout>
diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/BasicRenderScript/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/dimens.xml
copy to samples/browseable/BasicRenderScript/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/BasicRenderScript/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/styles.xml
copy to samples/browseable/BasicRenderScript/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicRenderScript/res/values-v11/styles.xml b/samples/browseable/BasicRenderScript/res/values-v11/styles.xml
new file mode 100644
index 0000000..f3a90c6
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values-v11/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+    <style name="FullscreenTheme" parent="android:Theme.Holo">
+        <item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="android:windowBackground">@null</item>
+        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
+        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
+    </style>
+
+    <style name="FullscreenActionBarStyle" parent="android:Widget.Holo.ActionBar">
+        <item name="android:background">@color/black_overlay</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/res/values-v11/template-styles.xml b/samples/browseable/BasicRenderScript/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/res/values-v14/styles.xml b/samples/browseable/BasicRenderScript/res/values-v14/styles.xml
new file mode 100644
index 0000000..a91fd03
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/res/values/attrs.xml b/samples/browseable/BasicRenderScript/res/values/attrs.xml
new file mode 100644
index 0000000..e67df0a
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values/attrs.xml
@@ -0,0 +1,14 @@
+<resources>
+
+    <!--
+         Declare custom theme attributes that allow changing which styles are
+         used for button bars depending on the API level.
+         ?android:attr/buttonBarStyle is new as of API 11 so this is
+         necessary to support previous API levels.
+    -->
+    <declare-styleable name="ButtonBarContainerTheme">
+        <attr name="buttonBarStyle" format="reference" />
+        <attr name="buttonBarButtonStyle" format="reference" />
+    </declare-styleable>
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/res/values/base-strings.xml b/samples/browseable/BasicRenderScript/res/values/base-strings.xml
new file mode 100644
index 0000000..a35b320
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">BasicRenderScript</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            BasicRenderScript sample demonstrates basic steps how to use renderScript.
+			In the sample, it performs graphical filter operation on a image with renderScript.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/BasicRenderScript/res/values/colors.xml b/samples/browseable/BasicRenderScript/res/values/colors.xml
new file mode 100644
index 0000000..327c060
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values/colors.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <color name="black_overlay">#66000000</color>
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/res/values/styles.xml b/samples/browseable/BasicRenderScript/res/values/styles.xml
new file mode 100644
index 0000000..49299b9
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+
+    <style name="FullscreenTheme" parent="android:Theme.NoTitleBar">
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowBackground">@null</item>
+        <item name="buttonBarStyle">@style/ButtonBar</item>
+        <item name="buttonBarButtonStyle">@style/ButtonBarButton</item>
+    </style>
+
+    <style name="ButtonBar">
+        <item name="android:paddingLeft">2dp</item>
+        <item name="android:paddingTop">5dp</item>
+        <item name="android:paddingRight">2dp</item>
+        <item name="android:paddingBottom">0dp</item>
+        <item name="android:background">@android:drawable/bottom_bar</item>
+    </style>
+
+    <style name="ButtonBarButton" />
+
+</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/BasicRenderScript/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/BasicRenderScript/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicRenderScript/res/values/template-styles.xml b/samples/browseable/BasicRenderScript/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicRenderScript/src/com.example.android.basicrenderscript/MainActivity.java b/samples/browseable/BasicRenderScript/src/com.example.android.basicrenderscript/MainActivity.java
new file mode 100644
index 0000000..ed6d5ad
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/src/com.example.android.basicrenderscript/MainActivity.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 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.basicrenderscript;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.support.v8.renderscript.*;
+
+public class MainActivity extends Activity {
+    /* Number of bitmaps that is used for renderScript thread and UI thread synchronization.
+       Ideally, this can be reduced to 2, however in some devices, 2 buffers still showing tierings on UI.
+       Investigating a root cause.
+     */
+    private final int NUM_BITMAPS = 3;
+    private int mCurrentBitmap = 0;
+    private Bitmap mBitmapIn;
+    private Bitmap[] mBitmapsOut;
+    private ImageView mImageView;
+
+    private RenderScript mRS;
+    private Allocation mInAllocation;
+    private Allocation[] mOutAllocations;
+    private ScriptC_saturation mScript;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main_layout);
+
+        /*
+         * Initialize UI
+         */
+        mBitmapIn = loadBitmap(R.drawable.data);
+        mBitmapsOut = new Bitmap[NUM_BITMAPS];
+        for (int i = 0; i < NUM_BITMAPS; ++i) {
+            mBitmapsOut[i] = Bitmap.createBitmap(mBitmapIn.getWidth(),
+                    mBitmapIn.getHeight(), mBitmapIn.getConfig());
+        }
+
+        mImageView = (ImageView) findViewById(R.id.imageView);
+        mImageView.setImageBitmap(mBitmapsOut[mCurrentBitmap]);
+        mCurrentBitmap += (mCurrentBitmap + 1) % NUM_BITMAPS;
+
+        SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar1);
+        seekbar.setProgress(50);
+        seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+            public void onProgressChanged(SeekBar seekBar, int progress,
+                                          boolean fromUser) {
+                float max = 2.0f;
+                float min = 0.0f;
+                float f = (float) ((max - min) * (progress / 100.0) + min);
+                updateImage(f);
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+            }
+        });
+
+        /*
+         * Create renderScript
+         */
+        createScript();
+
+        /*
+         * Invoke renderScript kernel and update imageView
+         */
+        updateImage(1.0f);
+    }
+
+    /*
+     * Initialize RenderScript
+     * In the sample, it creates RenderScript kernel that performs saturation manipulation.
+     */
+    private void createScript() {
+        //Initialize RS
+        mRS = RenderScript.create(this);
+
+        //Allocate buffers
+        mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
+        mOutAllocations = new Allocation[NUM_BITMAPS];
+        for (int i = 0; i < NUM_BITMAPS; ++i) {
+            mOutAllocations[i] = Allocation.createFromBitmap(mRS, mBitmapsOut[i]);
+        }
+
+        //Load script
+        mScript = new ScriptC_saturation(mRS);
+    }
+
+    /*
+     * In the AsyncTask, it invokes RenderScript intrinsics to do a filtering.
+     * After the filtering is done, an operation blocks at Allication.copyTo() in AsyncTask thread.
+     * Once all operation is finished at onPostExecute() in UI thread, it can invalidate and update ImageView UI.
+     */
+    private class RenderScriptTask extends AsyncTask<Float, Integer, Integer> {
+        Boolean issued = false;
+
+        protected Integer doInBackground(Float... values) {
+            int index = -1;
+            if (isCancelled() == false) {
+                issued = true;
+                index = mCurrentBitmap;
+
+                /*
+                 * Set global variable in RS
+                 */
+                mScript.set_saturationValue(values[0]);
+
+                /*
+                 * Invoke saturation filter kernel
+                 */
+                mScript.forEach_saturation(mInAllocation, mOutAllocations[index]);
+
+                /*
+                 * Copy to bitmap and invalidate image view
+                 */
+                mOutAllocations[index].copyTo(mBitmapsOut[index]);
+                mCurrentBitmap = (mCurrentBitmap + 1) % NUM_BITMAPS;
+            }
+            return index;
+        }
+
+        void updateView(Integer result) {
+            if (result != -1) {
+                // Request UI update
+                mImageView.setImageBitmap(mBitmapsOut[result]);
+                mImageView.invalidate();
+            }
+        }
+
+        protected void onPostExecute(Integer result) {
+            updateView(result);
+        }
+
+        protected void onCancelled(Integer result) {
+            if (issued) {
+                updateView(result);
+            }
+        }
+    }
+
+    RenderScriptTask currentTask = null;
+
+    /*
+    Invoke AsynchTask and cancel previous task.
+    When AsyncTasks are piled up (typically in slow device with heavy kernel),
+    Only the latest (and already started) task invokes RenderScript operation.
+     */
+    private void updateImage(final float f) {
+        if (currentTask != null)
+            currentTask.cancel(false);
+        currentTask = new RenderScriptTask();
+        currentTask.execute(f);
+    }
+
+    /*
+    Helper to load Bitmap from resource
+     */
+    private Bitmap loadBitmap(int resource) {
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        return BitmapFactory.decodeResource(getResources(), resource, options);
+    }
+
+}
diff --git a/samples/browseable/BasicRenderScript/src/com.example.android.common.media/CameraHelper.java b/samples/browseable/BasicRenderScript/src/com.example.android.common.media/CameraHelper.java
new file mode 100644
index 0000000..1fa8416
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/src/com.example.android.common.media/CameraHelper.java
@@ -0,0 +1,182 @@
+/*
+ * 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.common.media;
+
+import android.annotation.TargetApi;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Camera related utilities.
+ */
+public class CameraHelper {
+
+    public static final int MEDIA_TYPE_IMAGE = 1;
+    public static final int MEDIA_TYPE_VIDEO = 2;
+
+    /**
+     * Iterate over supported camera preview sizes to see which one best fits the
+     * dimensions of the given view while maintaining the aspect ratio. If none can,
+     * be lenient with the aspect ratio.
+     *
+     * @param sizes Supported camera preview sizes.
+     * @param w The width of the view.
+     * @param h The height of the view.
+     * @return Best match camera preview size to fit in the view.
+     */
+    public static  Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
+        // Use a very small tolerance because we want an exact match.
+        final double ASPECT_TOLERANCE = 0.1;
+        double targetRatio = (double) w / h;
+        if (sizes == null)
+            return null;
+
+        Camera.Size optimalSize = null;
+
+        // Start with max value and refine as we iterate over available preview sizes. This is the
+        // minimum difference between view and camera height.
+        double minDiff = Double.MAX_VALUE;
+
+        // Target view height
+        int targetHeight = h;
+
+        // Try to find a preview size that matches aspect ratio and the target view size.
+        // Iterate over all available sizes and pick the largest size that can fit in the view and
+        // still maintain the aspect ratio.
+        for (Camera.Size size : sizes) {
+            double ratio = (double) size.width / size.height;
+            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
+                continue;
+            if (Math.abs(size.height - targetHeight) < minDiff) {
+                optimalSize = size;
+                minDiff = Math.abs(size.height - targetHeight);
+            }
+        }
+
+        // Cannot find preview size that matches the aspect ratio, ignore the requirement
+        if (optimalSize == null) {
+            minDiff = Double.MAX_VALUE;
+            for (Camera.Size size : sizes) {
+                if (Math.abs(size.height - targetHeight) < minDiff) {
+                    optimalSize = size;
+                    minDiff = Math.abs(size.height - targetHeight);
+                }
+            }
+        }
+        return optimalSize;
+    }
+
+    /**
+     * @return the default camera on the device. Return null if there is no camera on the device.
+     */
+    public static Camera getDefaultCameraInstance() {
+        return Camera.open();
+    }
+
+
+    /**
+     * @return the default rear/back facing camera on the device. Returns null if camera is not
+     * available.
+     */
+    public static Camera getDefaultBackFacingCameraInstance() {
+        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
+    }
+
+    /**
+     * @return the default front facing camera on the device. Returns null if camera is not
+     * available.
+     */
+    public static Camera getDefaultFrontFacingCameraInstance() {
+        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
+    }
+
+
+    /**
+     *
+     * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT
+     *                 or Camera.CameraInfo.CAMERA_FACING_BACK.
+     * @return the default camera on the device. Returns null if camera is not available.
+     */
+    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+    private static Camera getDefaultCamera(int position) {
+        // Find the total number of cameras available
+        int  mNumberOfCameras = Camera.getNumberOfCameras();
+
+        // Find the ID of the back-facing ("default") camera
+        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+        for (int i = 0; i < mNumberOfCameras; i++) {
+            Camera.getCameraInfo(i, cameraInfo);
+            if (cameraInfo.facing == position) {
+                return Camera.open(i);
+
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory
+     * is persistent and available to other applications like gallery.
+     *
+     * @param type Media type. Can be video or image.
+     * @return A file object pointing to the newly created file.
+     */
+    public  static File getOutputMediaFile(int type){
+        // To be safe, you should check that the SDCard is mounted
+        // using Environment.getExternalStorageState() before doing this.
+        if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
+            return  null;
+        }
+
+        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_PICTURES), "CameraSample");
+        // This location works best if you want the created images to be shared
+        // between applications and persist after your app has been uninstalled.
+
+        // Create the storage directory if it does not exist
+        if (! mediaStorageDir.exists()){
+            if (! mediaStorageDir.mkdirs()) {
+                Log.d("CameraSample", "failed to create directory");
+                return null;
+            }
+        }
+
+        // Create a media file name
+        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+        File mediaFile;
+        if (type == MEDIA_TYPE_IMAGE){
+            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+                    "IMG_"+ timeStamp + ".jpg");
+        } else if(type == MEDIA_TYPE_VIDEO) {
+            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+                    "VID_"+ timeStamp + ".mp4");
+        } else {
+            return null;
+        }
+
+        return mediaFile;
+    }
+
+}
diff --git a/samples/browseable/BasicRenderScript/src/com.example.android.common.media/MediaCodecWrapper.java b/samples/browseable/BasicRenderScript/src/com.example.android.common.media/MediaCodecWrapper.java
new file mode 100644
index 0000000..a511221
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/src/com.example.android.common.media/MediaCodecWrapper.java
@@ -0,0 +1,386 @@
+/*
+ * 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.common.media;
+
+import android.media.*;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * Simplifies the MediaCodec interface by wrapping around the buffer processing operations.
+ */
+public class MediaCodecWrapper {
+
+    // Handler to use for {@code OutputSampleListener} and {code OutputFormatChangedListener}
+    // callbacks
+    private Handler mHandler;
+
+
+    // Callback when media output format changes.
+    public interface OutputFormatChangedListener {
+        void outputFormatChanged(MediaCodecWrapper sender, MediaFormat newFormat);
+    }
+
+    private OutputFormatChangedListener mOutputFormatChangedListener = null;
+
+    /**
+     * Callback for decodes frames. Observers can register a listener for optional stream
+     * of decoded data
+     */
+    public interface OutputSampleListener {
+        void outputSample(MediaCodecWrapper sender, MediaCodec.BufferInfo info, ByteBuffer buffer);
+    }
+
+    /**
+     * The {@link MediaCodec} that is managed by this class.
+     */
+    private MediaCodec mDecoder;
+
+    // References to the internal buffers managed by the codec. The codec
+    // refers to these buffers by index, never by reference so it's up to us
+    // to keep track of which buffer is which.
+    private ByteBuffer[] mInputBuffers;
+    private ByteBuffer[] mOutputBuffers;
+
+    // Indices of the input buffers that are currently available for writing. We'll
+    // consume these in the order they were dequeued from the codec.
+    private Queue<Integer> mAvailableInputBuffers;
+
+    // Indices of the output buffers that currently hold valid data, in the order
+    // they were produced by the codec.
+    private Queue<Integer> mAvailableOutputBuffers;
+
+    // Information about each output buffer, by index. Each entry in this array
+    // is valid if and only if its index is currently contained in mAvailableOutputBuffers.
+    private MediaCodec.BufferInfo[] mOutputBufferInfo;
+
+    // An (optional) stream that will receive decoded data.
+    private OutputSampleListener mOutputSampleListener;
+
+    private MediaCodecWrapper(MediaCodec codec) {
+        mDecoder = codec;
+        codec.start();
+        mInputBuffers = codec.getInputBuffers();
+        mOutputBuffers = codec.getOutputBuffers();
+        mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length];
+        mAvailableInputBuffers = new ArrayDeque<Integer>(mOutputBuffers.length);
+        mAvailableOutputBuffers = new ArrayDeque<Integer>(mInputBuffers.length);
+    }
+
+    /**
+     * Releases resources and ends the encoding/decoding session.
+     */
+    public void stopAndRelease() {
+        mDecoder.stop();
+        mDecoder.release();
+        mDecoder = null;
+        mHandler = null;
+    }
+
+    /**
+     * Getter for the registered {@link OutputFormatChangedListener}
+     */
+    public OutputFormatChangedListener getOutputFormatChangedListener() {
+        return mOutputFormatChangedListener;
+    }
+
+    /**
+     *
+     * @param outputFormatChangedListener the listener for callback.
+     * @param handler message handler for posting the callback.
+     */
+    public void setOutputFormatChangedListener(final OutputFormatChangedListener
+            outputFormatChangedListener, Handler handler) {
+        mOutputFormatChangedListener = outputFormatChangedListener;
+
+        // Making sure we don't block ourselves due to a bad implementation of the callback by
+        // using a handler provided by client.
+        Looper looper;
+        mHandler = handler;
+        if (outputFormatChangedListener != null && mHandler == null) {
+            if ((looper = Looper.myLooper()) != null) {
+                mHandler = new Handler();
+            } else {
+                throw new IllegalArgumentException(
+                        "Looper doesn't exist in the calling thread");
+            }
+        }
+    }
+
+    /**
+     * Constructs the {@link MediaCodecWrapper} wrapper object around the video codec.
+     * The codec is created using the encapsulated information in the
+     * {@link MediaFormat} object.
+     *
+     * @param trackFormat The format of the media object to be decoded.
+     * @param surface Surface to render the decoded frames.
+     * @return
+     */
+    public static MediaCodecWrapper fromVideoFormat(final MediaFormat trackFormat,
+            Surface surface) {
+        MediaCodecWrapper result = null;
+        MediaCodec videoCodec = null;
+
+        // BEGIN_INCLUDE(create_codec)
+        final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
+
+        // Check to see if this is actually a video mime type. If it is, then create
+        // a codec that can decode this mime type.
+        if (mimeType.contains("video/")) {
+            videoCodec = MediaCodec.createDecoderByType(mimeType);
+            videoCodec.configure(trackFormat, surface, null,  0);
+
+        }
+
+        // If codec creation was successful, then create a wrapper object around the
+        // newly created codec.
+        if (videoCodec != null) {
+            result = new MediaCodecWrapper(videoCodec);
+        }
+        // END_INCLUDE(create_codec)
+
+        return result;
+    }
+
+
+    /**
+     * Write a media sample to the decoder.
+     *
+     * A "sample" here refers to a single atomic access unit in the media stream. The definition
+     * of "access unit" is dependent on the type of encoding used, but it typically refers to
+     * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor}
+     * extracts data from a stream one sample at a time.
+     *
+     * @param input A ByteBuffer containing the input data for one sample. The buffer must be set
+     * up for reading, with its position set to the beginning of the sample data and its limit
+     * set to the end of the sample data.
+     *
+     * @param presentationTimeUs  The time, relative to the beginning of the media stream,
+     * at which this buffer should be rendered.
+     *
+     * @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
+     * int, int, long, int)}
+     *
+     * @throws MediaCodec.CryptoException
+     */
+    public boolean writeSample(final ByteBuffer input,
+            final MediaCodec.CryptoInfo crypto,
+            final long presentationTimeUs,
+            final int flags) throws MediaCodec.CryptoException, WriteException {
+        boolean result = false;
+        int size = input.remaining();
+
+        // check if we have dequed input buffers available from the codec
+        if (size > 0 &&  !mAvailableInputBuffers.isEmpty()) {
+            int index = mAvailableInputBuffers.remove();
+            ByteBuffer buffer = mInputBuffers[index];
+
+            // we can't write our sample to a lesser capacity input buffer.
+            if (size > buffer.capacity()) {
+                throw new MediaCodecWrapper.WriteException(String.format(
+                        "Insufficient capacity in MediaCodec buffer: "
+                            + "tried to write %d, buffer capacity is %d.",
+                        input.remaining(),
+                        buffer.capacity()));
+            }
+
+            buffer.clear();
+            buffer.put(input);
+
+            // Submit the buffer to the codec for decoding. The presentationTimeUs
+            // indicates the position (play time) for the current sample.
+            if (crypto == null) {
+                mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
+            } else {
+                mDecoder.queueSecureInputBuffer(index, 0, crypto, presentationTimeUs, flags);
+            }
+            result = true;
+        }
+        return result;
+    }
+
+    static MediaCodec.CryptoInfo cryptoInfo= new MediaCodec.CryptoInfo();
+
+    /**
+     * Write a media sample to the decoder.
+     *
+     * A "sample" here refers to a single atomic access unit in the media stream. The definition
+     * of "access unit" is dependent on the type of encoding used, but it typically refers to
+     * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor}
+     * extracts data from a stream one sample at a time.
+     *
+     * @param extractor  Instance of {@link android.media.MediaExtractor} wrapping the media.
+     *
+     * @param presentationTimeUs The time, relative to the beginning of the media stream,
+     * at which this buffer should be rendered.
+     *
+     * @param flags  Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
+     * int, int, long, int)}
+     *
+     * @throws MediaCodec.CryptoException
+     */
+    public boolean writeSample(final MediaExtractor extractor,
+            final boolean isSecure,
+            final long presentationTimeUs,
+            int flags) {
+        boolean result = false;
+        boolean isEos = false;
+
+        if (!mAvailableInputBuffers.isEmpty()) {
+            int index = mAvailableInputBuffers.remove();
+            ByteBuffer buffer = mInputBuffers[index];
+
+            // reads the sample from the file using extractor into the buffer
+            int size = extractor.readSampleData(buffer, 0);
+            if (size <= 0) {
+                flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+            }
+
+            // Submit the buffer to the codec for decoding. The presentationTimeUs
+            // indicates the position (play time) for the current sample.
+            if (!isSecure) {
+                mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
+            } else {
+                extractor.getSampleCryptoInfo(cryptoInfo);
+                mDecoder.queueSecureInputBuffer(index, 0, cryptoInfo, presentationTimeUs, flags);
+            }
+
+            result = true;
+        }
+        return result;
+    }
+
+    /**
+     * Performs a peek() operation in the queue to extract media info for the buffer ready to be
+     * released i.e. the head element of the queue.
+     *
+     * @param out_bufferInfo An output var to hold the buffer info.
+     *
+     * @return True, if the peek was successful.
+     */
+    public boolean peekSample(MediaCodec.BufferInfo out_bufferInfo) {
+        // dequeue available buffers and synchronize our data structures with the codec.
+        update();
+        boolean result = false;
+        if (!mAvailableOutputBuffers.isEmpty()) {
+            int index = mAvailableOutputBuffers.peek();
+            MediaCodec.BufferInfo info = mOutputBufferInfo[index];
+            // metadata of the sample
+            out_bufferInfo.set(
+                    info.offset,
+                    info.size,
+                    info.presentationTimeUs,
+                    info.flags);
+            result = true;
+        }
+        return result;
+    }
+
+    /**
+     * Processes, releases and optionally renders the output buffer available at the head of the
+     * queue. All observers are notified with a callback. See {@link
+     * OutputSampleListener#outputSample(MediaCodecWrapper, android.media.MediaCodec.BufferInfo,
+     * java.nio.ByteBuffer)}
+     *
+     * @param render True, if the buffer is to be rendered on the {@link Surface} configured
+     *
+     */
+    public void popSample(boolean render) {
+        // dequeue available buffers and synchronize our data structures with the codec.
+        update();
+        if (!mAvailableOutputBuffers.isEmpty()) {
+            int index = mAvailableOutputBuffers.remove();
+
+            if (render && mOutputSampleListener != null) {
+                ByteBuffer buffer = mOutputBuffers[index];
+                MediaCodec.BufferInfo info = mOutputBufferInfo[index];
+                mOutputSampleListener.outputSample(this, info, buffer);
+            }
+
+            // releases the buffer back to the codec
+            mDecoder.releaseOutputBuffer(index, render);
+        }
+    }
+
+    /**
+     * Synchronize this object's state with the internal state of the wrapped
+     * MediaCodec.
+     */
+    private void update() {
+        // BEGIN_INCLUDE(update_codec_state)
+        int index;
+
+        // Get valid input buffers from the codec to fill later in the same order they were
+        // made available by the codec.
+        while ((index = mDecoder.dequeueInputBuffer(0)) != MediaCodec.INFO_TRY_AGAIN_LATER) {
+            mAvailableInputBuffers.add(index);
+        }
+
+
+        // Likewise with output buffers. If the output buffers have changed, start using the
+        // new set of output buffers. If the output format has changed, notify listeners.
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        while ((index = mDecoder.dequeueOutputBuffer(info, 0)) !=  MediaCodec.INFO_TRY_AGAIN_LATER) {
+            switch (index) {
+                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
+                    mOutputBuffers = mDecoder.getOutputBuffers();
+                    mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length];
+                    mAvailableOutputBuffers.clear();
+                    break;
+                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
+                    if (mOutputFormatChangedListener != null) {
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mOutputFormatChangedListener
+                                        .outputFormatChanged(MediaCodecWrapper.this,
+                                                mDecoder.getOutputFormat());
+
+                            }
+                        });
+                    }
+                    break;
+                default:
+                    // Making sure the index is valid before adding to output buffers. We've already
+                    // handled INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED &
+                    // INFO_OUTPUT_BUFFERS_CHANGED i.e all the other possible return codes but
+                    // asserting index value anyways for future-proofing the code.
+                    if(index >= 0) {
+                        mOutputBufferInfo[index] = info;
+                        mAvailableOutputBuffers.add(index);
+                    } else {
+                        throw new IllegalStateException("Unknown status from dequeueOutputBuffer");
+                    }
+                    break;
+            }
+
+        }
+        // END_INCLUDE(update_codec_state)
+
+    }
+
+    private class WriteException extends Throwable {
+        private WriteException(final String detailMessage) {
+            super(detailMessage);
+        }
+    }
+}
diff --git a/samples/browseable/BasicRenderScript/src/rs/saturation.rs b/samples/browseable/BasicRenderScript/src/rs/saturation.rs
new file mode 100644
index 0000000..cf043d1
--- /dev/null
+++ b/samples/browseable/BasicRenderScript/src/rs/saturation.rs
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.basicrenderscript)
+#pragma rs_fp_relaxed
+
+const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+
+float saturationValue = 0.f;
+
+/*
+RenderScript kernel that performs saturation manipulation.
+*/
+uchar4 __attribute__((kernel)) saturation(uchar4 in)
+{
+    float4 f4 = rsUnpackColor8888(in);
+    float3 result = dot(f4.rgb, gMonoMult);
+    result = mix( result, f4.rgb, saturationValue );
+
+    return rsPackColorTo8888(result);
+}
+
diff --git a/samples/browseable/BasicSyncAdapter/res/values-sw600dp/dimens.xml b/samples/browseable/BasicSyncAdapter/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicSyncAdapter/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicSyncAdapter/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicSyncAdapter/res/values-sw600dp/styles.xml b/samples/browseable/BasicSyncAdapter/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicSyncAdapter/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicSyncAdapter/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicSyncAdapter/res/values-v11/template-styles.xml b/samples/browseable/BasicSyncAdapter/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicSyncAdapter/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicSyncAdapter/res/values/styles.xml b/samples/browseable/BasicSyncAdapter/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/BasicSyncAdapter/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicSyncAdapter/res/values/dimens.xml b/samples/browseable/BasicSyncAdapter/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicSyncAdapter/res/values/dimens.xml
rename to samples/browseable/BasicSyncAdapter/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicSyncAdapter/res/values/template-styles.xml b/samples/browseable/BasicSyncAdapter/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicSyncAdapter/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicTransition/AndroidManifest.xml b/samples/browseable/BasicTransition/AndroidManifest.xml
new file mode 100644
index 0000000..b4698d6
--- /dev/null
+++ b/samples/browseable/BasicTransition/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.basictransition"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name=".MainActivity"
+            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/browseable/BasicTransition/_index.jd b/samples/browseable/BasicTransition/_index.jd
new file mode 100644
index 0000000..af2a6e7
--- /dev/null
+++ b/samples/browseable/BasicTransition/_index.jd
@@ -0,0 +1,12 @@
+
+
+
+page.tags="BasicTransition"
+sample.group=UI
+@jd:body
+
+<p>
+This sample demonstrates the basic use of the transition framework introduced in KitKat.
+Select each of the radio buttons to switch between the 
+{@link android.transition.Scene Scene}s.
+</p>
diff --git a/samples/browseable/BasicTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0f5d360
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicTransition/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
rename to samples/browseable/BasicTransition/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/BasicTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..72d85c9
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..cf93e69
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..149a984
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicTransition/res/drawable/oval.xml b/samples/browseable/BasicTransition/res/drawable/oval.xml
new file mode 100644
index 0000000..07f3abd
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/drawable/oval.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="#0000ff"/>
+</shape>
diff --git a/samples/browseable/BasicTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/BasicTransition/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/BasicTransition/res/layout/activity_basic_transition.xml b/samples/browseable/BasicTransition/res/layout/activity_basic_transition.xml
new file mode 100644
index 0000000..f9a4cd2
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout/activity_basic_transition.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.example.android.basictransition.BasicTransitionActivity"/>
diff --git a/samples/browseable/BasicTransition/res/layout/activity_main.xml b/samples/browseable/BasicTransition/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/BasicTransition/res/layout/fragment_basic_transition.xml b/samples/browseable/BasicTransition/res/layout/fragment_basic_transition.xml
new file mode 100644
index 0000000..98999c8
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout/fragment_basic_transition.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.example.android.basictransition.BasicTransitionFragment">
+
+    <RadioGroup
+        android:id="@+id/select_scene"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:orientation="horizontal">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/scene"/>
+
+        <RadioButton
+            android:id="@+id/select_scene_1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:checked="true"
+            android:text="@string/scene_1"/>
+
+        <RadioButton
+            android:id="@+id/select_scene_2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/scene_2"/>
+
+        <RadioButton
+            android:id="@+id/select_scene_3"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/scene_3"/>
+
+        <RadioButton
+            android:id="@+id/select_scene_4"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/scene_4"/>
+
+    </RadioGroup>
+
+    <FrameLayout
+        android:id="@+id/scene_root"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+
+        <include layout="@layout/scene1"/>
+
+    </FrameLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/BasicTransition/res/layout/scene1.xml b/samples/browseable/BasicTransition/res/layout/scene1.xml
new file mode 100644
index 0000000..005bf3b
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout/scene1.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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
+    android:id="@+id/container"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/transition_square"
+        android:layout_width="@dimen/square_size_normal"
+        android:layout_height="@dimen/square_size_normal"
+        android:background="#990000"
+        android:gravity="center"/>
+
+    <ImageView
+        android:id="@+id/transition_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/transition_square"
+        android:src="@drawable/ic_launcher"/>
+
+    <ImageView
+        android:id="@+id/transition_oval"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_below="@id/transition_image"
+        android:src="@drawable/oval"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/BasicTransition/res/layout/scene2.xml b/samples/browseable/BasicTransition/res/layout/scene2.xml
new file mode 100644
index 0000000..38a809a
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout/scene2.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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
+    android:id="@+id/container"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/transition_square"
+        android:layout_width="@dimen/square_size_normal"
+        android:layout_height="@dimen/square_size_normal"
+        android:layout_alignParentBottom="true"
+        android:background="#990000"
+        android:gravity="center"/>
+
+    <ImageView
+        android:id="@+id/transition_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:src="@drawable/ic_launcher"/>
+
+    <ImageView
+        android:id="@+id/transition_oval"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_centerHorizontal="true"
+        android:src="@drawable/oval"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/BasicTransition/res/layout/scene3.xml b/samples/browseable/BasicTransition/res/layout/scene3.xml
new file mode 100644
index 0000000..a4cac0f
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/layout/scene3.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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
+    android:id="@+id/container"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/transition_square"
+        android:layout_width="@dimen/square_size_normal"
+        android:layout_height="@dimen/square_size_normal"
+        android:layout_centerHorizontal="true"
+        android:background="#990000"
+        android:gravity="center"/>
+
+    <ImageView
+        android:id="@+id/transition_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:src="@drawable/ic_launcher"/>
+
+    <ImageView
+        android:id="@+id/transition_oval"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:src="@drawable/oval"/>
+
+    <TextView
+        android:id="@+id/transition_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:text="@string/additional_message"
+        android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/BasicTransition/res/menu/main.xml b/samples/browseable/BasicTransition/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/BasicTransition/res/transition/changebounds_fadein_together.xml b/samples/browseable/BasicTransition/res/transition/changebounds_fadein_together.xml
new file mode 100644
index 0000000..062e012
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/transition/changebounds_fadein_together.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+    <changeBounds/>
+    <fade android:fadingMode="fade_in">
+        <targets>
+            <target android:targetId="@id/transition_title" />
+        </targets>
+    </fade>
+</transitionSet>
diff --git a/samples/browseable/BasicTransition/res/transition/scene3_transition_manager.xml b/samples/browseable/BasicTransition/res/transition/scene3_transition_manager.xml
new file mode 100644
index 0000000..6189d61
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/transition/scene3_transition_manager.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+    <transition
+        android:toScene="@layout/scene3"
+        android:transition="@transition/changebounds_fadein_together"/>
+</transitionManager>
diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/BasicTransition/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/Basic/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicTransition/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/BasicTransition/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/Basic/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicTransition/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicTransition/res/values-v11/template-styles.xml b/samples/browseable/BasicTransition/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BasicTransition/res/values-w820dp/dimens.xml b/samples/browseable/BasicTransition/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..21e2968
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/samples/browseable/BasicTransition/res/values/base-strings.xml b/samples/browseable/BasicTransition/res/values/base-strings.xml
new file mode 100644
index 0000000..466e590
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">BasicTransition</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+	    This sample demonstrates the basic use of the transition framework introduced in KitKat.
+	    Select each of the RadioButtons to switch between the Scenes.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/BasicTransition/res/values/dimens.xml b/samples/browseable/BasicTransition/res/values/dimens.xml
new file mode 100644
index 0000000..45ccdbc
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+    <dimen name="square_size_normal">50dp</dimen>
+    <dimen name="square_size_expanded">100dp</dimen>
+</resources>
diff --git a/samples/browseable/BasicTransition/res/values/strings.xml b/samples/browseable/BasicTransition/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/BasicTransition/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/Basic/res/values/dimens.xml
rename to samples/browseable/BasicTransition/res/values/template-dimens.xml
diff --git a/samples/browseable/BasicTransition/res/values/template-styles.xml b/samples/browseable/BasicTransition/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BasicTransition/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BasicTransition/src/com.example.android.basictransition/BasicTransitionFragment.java b/samples/browseable/BasicTransition/src/com.example.android.basictransition/BasicTransitionFragment.java
new file mode 100644
index 0000000..e67603d
--- /dev/null
+++ b/samples/browseable/BasicTransition/src/com.example.android.basictransition/BasicTransitionFragment.java
@@ -0,0 +1,121 @@
+/*
+ * 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.basictransition;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.transition.Scene;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioGroup;
+
+public class BasicTransitionFragment extends Fragment
+        implements RadioGroup.OnCheckedChangeListener {
+
+    // We transition between these Scenes
+    private Scene mScene1;
+    private Scene mScene2;
+    private Scene mScene3;
+
+    /** A custom TransitionManager */
+    private TransitionManager mTransitionManagerForScene3;
+
+    /** Transitions take place in this ViewGroup. We retain this for the dynamic transition on scene 4. */
+    private ViewGroup mSceneRoot;
+
+    public static BasicTransitionFragment newInstance() {
+        return new BasicTransitionFragment();
+    }
+
+    public BasicTransitionFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_basic_transition, container, false);
+        assert view != null;
+        RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.select_scene);
+        radioGroup.setOnCheckedChangeListener(this);
+        mSceneRoot = (ViewGroup) view.findViewById(R.id.scene_root);
+
+        // BEGIN_INCLUDE(instantiation_from_view)
+        // A Scene can be instantiated from a live view hierarchy.
+        mScene1 = new Scene(mSceneRoot, (ViewGroup) mSceneRoot.findViewById(R.id.container));
+        // END_INCLUDE(instantiation_from_view)
+
+        // BEGIN_INCLUDE(instantiation_from_resource)
+        // You can also inflate a generate a Scene from a layout resource file.
+        mScene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, getActivity());
+        // END_INCLUDE(instantiation_from_resource)
+
+        // Another scene from a layout resource file.
+        mScene3 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene3, getActivity());
+
+        // BEGIN_INCLUDE(custom_transition_manager)
+        // We create a custom TransitionManager for Scene 3, in which ChangeBounds and Fade
+        // take place at the same time.
+        mTransitionManagerForScene3 = TransitionInflater.from(getActivity())
+                .inflateTransitionManager(R.transition.scene3_transition_manager, mSceneRoot);
+        // END_INCLUDE(custom_transition_manager)
+
+        return view;
+    }
+
+    @Override
+    public void onCheckedChanged(RadioGroup group, int checkedId) {
+        switch (checkedId) {
+            case R.id.select_scene_1: {
+                // BEGIN_INCLUDE(transition_simple)
+                // You can start an automatic transition with TransitionManager.go().
+                TransitionManager.go(mScene1);
+                // END_INCLUDE(transition_simple)
+                break;
+            }
+            case R.id.select_scene_2: {
+                TransitionManager.go(mScene2);
+                break;
+            }
+            case R.id.select_scene_3: {
+                // BEGIN_INCLUDE(transition_custom)
+                // You can also start a transition with a custom TransitionManager.
+                mTransitionManagerForScene3.transitionTo(mScene3);
+                // END_INCLUDE(transition_custom)
+                break;
+            }
+            case R.id.select_scene_4: {
+                // BEGIN_INCLUDE(transition_dynamic)
+                // Alternatively, transition can be invoked dynamically without a Scene.
+                // For this, we first call TransitionManager.beginDelayedTransition().
+                TransitionManager.beginDelayedTransition(mSceneRoot);
+                // Then, we can just change view properties as usual.
+                View square = mSceneRoot.findViewById(R.id.transition_square);
+                ViewGroup.LayoutParams params = square.getLayoutParams();
+                int newSize = getResources().getDimensionPixelSize(R.dimen.square_size_expanded);
+                params.width = newSize;
+                params.height = newSize;
+                square.setLayoutParams(params);
+                // END_INCLUDE(transition_dynamic)
+                break;
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/BasicTransition/src/com.example.android.basictransition/MainActivity.java b/samples/browseable/BasicTransition/src/com.example.android.basictransition/MainActivity.java
new file mode 100644
index 0000000..1e7c301
--- /dev/null
+++ b/samples/browseable/BasicTransition/src/com.example.android.basictransition/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.basictransition;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        BasicTransitionFragment fragment = new BasicTransitionFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BasicTransition/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/BasicTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/AndroidManifest.xml b/samples/browseable/BatchStepSensor/AndroidManifest.xml
new file mode 100644
index 0000000..2c4e4f2
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.batchstepsensor"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <!-- This sample requires at least Android KitKat for sensor batching support -->
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+
+    <!-- Require the step counter and step detector sensors.
+    See the method BatchStepSensorFragment#isKitkatWithStepSensor() for a programmatic check if
+    support is optional and the application supports a case where these sensors are not available.
+    -->
+    <uses-feature android:name="android.hardware.sensor.stepcounter" />
+    <uses-feature android:name="android.hardware.sensor.stepdetector" />
+
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/BatchStepSensor/_index.jd b/samples/browseable/BatchStepSensor/_index.jd
new file mode 100644
index 0000000..a361294
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/_index.jd
@@ -0,0 +1,27 @@
+
+
+
+page.tags="BatchStepSensor"
+sample.group=Sensors
+@jd:body
+
+<p>
+  This sample demonstrates the use of the two step sensors (step detector and
+  counter) and sensor batching. It shows how to register a {@link
+  android.hardware.SensorEventListener} with and without batching and shows how
+  these events are received.
+</p>
+
+<p>
+  The Step Detector sensor fires an event when a step is detected, while the
+  step counter returns the total number of steps since a listener was first
+  registered for this sensor. Both sensors only count steps while a listener is
+  registered.
+</p>
+
+<p>
+  This sample only covers the basic case, where a listener is only registered
+  while the app is running. Likewise, batched sensors can be used in the
+  background (when the CPU is suspended), which requires manually flushing the
+  sensor event queue before it overflows, which is not covered in this sample.
+</p>
diff --git a/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_action_cancel.png b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_action_cancel.png
new file mode 100644
index 0000000..f889617
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_action_cancel.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..564742c
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/BatchStepSensor/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/BatchStepSensor/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_action_cancel.png b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_action_cancel.png
new file mode 100644
index 0000000..d5a9384
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_action_cancel.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..08abe57
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/card_bg.9.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/card_bg.9.png
new file mode 100644
index 0000000..23b30c1
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/card_bg.9.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative.png
new file mode 100644
index 0000000..1e6a64e
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative_pressed.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative_pressed.png
new file mode 100644
index 0000000..13c105e
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative_pressed.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral.png
new file mode 100644
index 0000000..f8874cd
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png
new file mode 100644
index 0000000..a8f5e67
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive.png
new file mode 100644
index 0000000..b0a68c3
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive_pressed.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive_pressed.png
new file mode 100644
index 0000000..528b5aa
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive_pressed.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..15bafae
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_action_cancel.png b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_action_cancel.png
new file mode 100644
index 0000000..331c545
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_action_cancel.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..40bdd35
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_bg.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg.xml
new file mode 100644
index 0000000..f86cf2f
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@color/card_action" android:state_pressed="true" />
+    <item android:drawable="@color/card_action_focused" android:state_focused="true" />
+    <item android:drawable="@color/card_action_item_bg" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_bg_negative.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg_negative.xml
new file mode 100644
index 0000000..05d38ac
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg_negative.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@color/card_action_negative" android:state_pressed="true" />
+    <item android:drawable="@color/card_action_negative_focused" android:state_focused="true" />
+    <item android:drawable="@color/card_action_item_bg" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_bg_positive.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg_positive.xml
new file mode 100644
index 0000000..4e92f24
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg_positive.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@color/card_action_positive" android:state_pressed="true" />
+    <item android:drawable="@color/card_action_positive_focused" android:state_focused="true" />
+    <item android:drawable="@color/card_action_item_bg" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_negative.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_negative.xml
new file mode 100644
index 0000000..6a797dc
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_negative.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/ic_cardaction_negative_pressed" android:state_pressed="true" />
+    <item android:drawable="@drawable/ic_cardaction_negative" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_neutral.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_neutral.xml
new file mode 100644
index 0000000..73e9214
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_neutral.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/ic_cardaction_neutral_pressed" android:state_pressed="true" />
+    <item android:drawable="@drawable/ic_cardaction_neutral" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_positive.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_positive.xml
new file mode 100644
index 0000000..63f82d6
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_icon_positive.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/ic_cardaction_positive_pressed" android:state_pressed="true" />
+    <item android:drawable="@drawable/ic_cardaction_positive" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_text.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_text.xml
new file mode 100644
index 0000000..cddf5cd
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_text.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@color/card_action_inverted" />
+    <item android:state_focused="true" android:color="@color/card_action_inverted" />
+    <item android:color="@color/card_action" />
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_text_negative.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_text_negative.xml
new file mode 100644
index 0000000..90f2c05
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_text_negative.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@color/card_action_inverted"/>
+    <item android:state_focused="true" android:color="@color/card_action_inverted"/>
+    <item android:color="@color/card_action_negative"/>
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_text_positive.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_text_positive.xml
new file mode 100644
index 0000000..c3ff402
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_text_positive.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@color/card_action_inverted"/>
+    <item android:state_focused="true" android:color="@color/card_action_inverted"/>
+    <item android:color="@color/card_action_positive"/>
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_overlay_focused.xml b/samples/browseable/BatchStepSensor/res/drawable/card_overlay_focused.xml
new file mode 100644
index 0000000..787167d
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_overlay_focused.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <stroke
+        android:width="5dp"
+        android:color="#aa99CC00"/>
+</shape>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_separator.xml b/samples/browseable/BatchStepSensor/res/drawable/card_separator.xml
new file mode 100644
index 0000000..6bb5630
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/drawable/card_separator.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="line">
+    <stroke android:color="#CCC" />
+    <size android:height="1dp" />
+</shape>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/layout/activity_main.xml b/samples/browseable/BatchStepSensor/res/layout/activity_main.xml
new file mode 100755
index 0000000..63f1297
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/activity_main.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<fragment
+    android:id="@+id/fragment_cardstream"
+    android:name="com.example.android.batchstepsensor.cardstream.CardStreamFragment"
+    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"
+    tools:context=".MainActivity"
+    tools:layout="@layout/cardstream"/>
diff --git a/samples/browseable/BatchStepSensor/res/layout/card.xml b/samples/browseable/BatchStepSensor/res/layout/card.xml
new file mode 100644
index 0000000..5deb6f5
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/card.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<com.example.android.batchstepsensor.cardstream.CardLayout
+    android:id="@+id/card_layout"
+    style="@style/Card"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:id="@+id/card_actionarea"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/card_contentarea"
+        android:background="@color/card_action_bg"
+        android:orientation="vertical"
+        android:paddingBottom="@dimen/card_action_margin"
+        android:visibility="gone"
+        >
+        <include layout="@layout/card_button_seperator"/>
+    </LinearLayout>
+
+
+    <LinearLayout
+        android:id="@id/card_contentarea"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        style="@style/CardContentArea">
+
+        <TextView
+            android:id="@+id/card_title"
+            style="@style/CardTitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+
+        <TextView
+            android:id="@+id/card_content"
+            style="@style/CardContent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/card_title"/>
+
+    </LinearLayout>
+
+    <View
+        android:id="@+id/card_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignBottom="@id/card_contentarea"
+        android:layout_alignLeft="@id/card_contentarea"
+        android:layout_alignRight="@id/card_contentarea"
+        android:layout_alignTop="@id/card_contentarea"
+        android:layout_alignWithParentIfMissing="false"
+        android:visibility="invisible"/>
+
+</com.example.android.batchstepsensor.cardstream.CardLayout>
diff --git a/samples/browseable/BatchStepSensor/res/layout/card_button_negative.xml b/samples/browseable/BatchStepSensor/res/layout/card_button_negative.xml
new file mode 100644
index 0000000..7ca9677
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/card_button_negative.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<com.example.android.batchstepsensor.cardstream.CardActionButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/card_button"
+    style="@style/CardActionNegative"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical" />
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/layout/card_button_neutral.xml b/samples/browseable/BatchStepSensor/res/layout/card_button_neutral.xml
new file mode 100644
index 0000000..da74448
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/card_button_neutral.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<com.example.android.batchstepsensor.cardstream.CardActionButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/card_button"
+    style="@style/CardActionNeutral"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical" />
diff --git a/samples/browseable/BatchStepSensor/res/layout/card_button_positive.xml b/samples/browseable/BatchStepSensor/res/layout/card_button_positive.xml
new file mode 100644
index 0000000..afbda8a
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/card_button_positive.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<com.example.android.batchstepsensor.cardstream.CardActionButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/card_button"
+    style="@style/CardActionPositive"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical" />
diff --git a/samples/browseable/BatchStepSensor/res/layout/card_button_seperator.xml b/samples/browseable/BatchStepSensor/res/layout/card_button_seperator.xml
new file mode 100644
index 0000000..6731242
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/card_button_seperator.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <ImageView
+        android:id="@+id/card_separator"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@drawable/card_separator" />
+
+</merge>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/layout/card_progress.xml b/samples/browseable/BatchStepSensor/res/layout/card_progress.xml
new file mode 100644
index 0000000..1bf349f
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/card_progress.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    style="@style/CardProgressLayout">
+
+    <TextView
+        android:id="@+id/card_progress_text"
+        style="@style/CardProgressText"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <ProgressBar
+        android:id="@+id/card_progress"
+        style="@android:style/Widget.Holo.ProgressBar.Horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/layout/cardstream.xml b/samples/browseable/BatchStepSensor/res/layout/cardstream.xml
new file mode 100644
index 0000000..a6b917b
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/layout/cardstream.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fillViewport="true">
+
+    <com.example.android.batchstepsensor.cardstream.CardStreamLinearLayout
+        style="@style/CardStream"
+        android:id="@+id/card_stream"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</ScrollView>
diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml b/samples/browseable/BatchStepSensor/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml
copy to samples/browseable/BatchStepSensor/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml b/samples/browseable/BatchStepSensor/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml
copy to samples/browseable/BatchStepSensor/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BatchStepSensor/res/values-sw720dp-land/dimens.xml b/samples/browseable/BatchStepSensor/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 0000000..00059fc
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+    <!-- Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. -->
+    <dimen name="activity_horizontal_margin">128dp</dimen>
+</resources>
diff --git a/samples/browseable/BatchStepSensor/res/values-v11/styles.xml b/samples/browseable/BatchStepSensor/res/values-v11/styles.xml
new file mode 100644
index 0000000..3c02242
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/samples/browseable/BatchStepSensor/res/values-v11/template-styles.xml b/samples/browseable/BatchStepSensor/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BatchStepSensor/res/values-v14/styles.xml b/samples/browseable/BatchStepSensor/res/values-v14/styles.xml
new file mode 100644
index 0000000..a91fd03
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/samples/browseable/BatchStepSensor/res/values-v16/styles.xml b/samples/browseable/BatchStepSensor/res/values-v16/styles.xml
new file mode 100644
index 0000000..2b380aa
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values-v16/styles.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="CardTitle" parent="@style/CardTitleBase">
+        <item name="android:fontFamily">sans-serif-condensed</item>
+    </style>
+
+    <style name="CardContent" parent="@style/CardContentBase">
+        <item name="android:fontFamily">sans-serif-light</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/values/attrs.xml b/samples/browseable/BatchStepSensor/res/values/attrs.xml
new file mode 100644
index 0000000..16b5260
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/attrs.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <declare-styleable name="CardStream">
+        <attr name="animationDuration" format="enum">
+            <enum name="slow" value="1001"/>
+            <enum name="normal" value="1002"/>
+            <enum name="fast" value="1003"/>
+        </attr>
+        <attr name="animators" format="string"/>
+    </declare-styleable>
+
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/values/base-strings.xml b/samples/browseable/BatchStepSensor/res/values/base-strings.xml
new file mode 100644
index 0000000..341b6bc
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/base-strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">BatchStepSensor</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample demonstrates the use of the two step sensors (step detector and counter) and
+            sensor batching.\n\n It shows how to register a SensorEventListener with and without
+            batching and shows how these events are received.\n\nThe Step Detector sensor fires an
+            event when a step is detected, while the step counter returns the total number of
+            steps since a listener was first registered for this sensor.
+            Both sensors only count steps while a listener is registered. This sample only covers the
+            basic case, where a listener is only registered while the app is running. Likewise,
+            batched sensors can be used in the background (when the CPU is suspended), which
+            requires manually flushing the sensor event queue before it overflows, which is not
+            covered in this sample.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/BatchStepSensor/res/values/color.xml b/samples/browseable/BatchStepSensor/res/values/color.xml
new file mode 100644
index 0000000..98717d8
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <color name="card_action_inverted">@android:color/white</color>
+
+    <color name="card_content_textcolor">#444</color>
+
+    <color name="card_action_bg">#DDD</color>
+    <color name="card_action_item_bg">#F4F4F4</color>
+
+    <!-- Neutral Actions -->
+    <color name="card_action_focused">#FFE3F4FC</color>
+    <color name="card_action">#FF47B4EA</color>
+
+    <!-- Negative Actions -->
+    <color name="card_action_negative_focused">#FFFBCBCA</color>
+    <color name="card_action_negative">#FFF64940</color>
+
+    <!-- Positive Actions -->
+    <color name="card_action_positive_focused">#FFE4F0AF</color>
+    <color name="card_action_positive">#FFA0CC00</color>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/values/dimens.xml b/samples/browseable/BatchStepSensor/res/values/dimens.xml
new file mode 100644
index 0000000..b025f1f
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/dimens.xml
@@ -0,0 +1,14 @@
+<resources>
+
+    <dimen name="card_content_text">14sp</dimen>
+    <dimen name="card_content_title">24sp</dimen>
+
+    <dimen name="card_padding">15dp</dimen>
+    <dimen name="card_margin">10dp</dimen>
+
+    <dimen name="card_action_margin">3dp</dimen>
+    <dimen name="card_action_padding">8dp</dimen>
+
+    <dimen name="card_stream_bottom_padding">90dp</dimen>
+
+</resources>
diff --git a/samples/browseable/BatchStepSensor/res/values/ids.xml b/samples/browseable/BatchStepSensor/res/values/ids.xml
new file mode 100644
index 0000000..c5d1b78
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/ids.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item name="card_layout" type="id"/>
+    <item name="card_actionarea" type="id"/>
+    <item name="card_contentarea" type="id"/>
+    <item name="card_title" type="id"/>
+    <item name="card_content" type="id"/>
+    <item name="card_overlay" type="id"/>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/values/strings.xml b/samples/browseable/BatchStepSensor/res/values/strings.xml
new file mode 100644
index 0000000..9c0a8ad
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/strings.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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="intro_title">Introduction</string>
+
+    <string name="batching_queue_title">Background sensor batching</string>
+    <string name="batching_queue_description">Batching allows the sensor to report sensor events at
+        a specified frequency.\n\nThe system delays calls to the SensorEventListener and deliver
+        them in intervals, based on the maximum report latency specified when the listener is
+        registered. Note that this only means that the call to onSensorChanged() is delayed, the
+        total number of calls is identical as if no batching was used. Sensors only deliver events
+        while the CPU is awake. If the CPU is asleep and a batched sensor event listener is still
+        registered, the sensor will continue to collect events until it runs out of memory and
+        overwrites old values. This use case is not covered by this sample. (The sensor event queue
+        should be flushed using a scheduled background thread.) \n\nIn this sample app data is only
+        collected while the app is running and the CPU is awake. In this case the sensor will
+        deliver events before the queue fills up.
+    </string>
+
+    <string name="explanation_description">The age of a sensor event describes the delay between
+        when it was recorded by the sensor until it was delivered to the SensorEventListener.
+    </string>
+
+    <string name="register_detector_title">Register step detector sensor</string>
+    <string name="register_detector_description">Register a listener for the STEP DETECTOR
+        sensor.\n\nThis sensor delivers an event when the user takes a step. One event is received
+        per step.
+    </string>
+
+    <string name="register_counter_title">Register step counter sensor</string>
+    <string name="register_counter_description">Register a listener for the STEP COUNTER
+        sensor.\n\nThis sensor triggers events when a step is detected, but applies algorithms to
+        filter out false positives. Events from this sensor have higher latency than the step
+        detector and contain the total number of steps taken since the sensor was first registered.
+    </string>
+
+    <string name="register_0">No batching (delay=0)</string>
+    <string name="register_5">5s batching (delay=5000ms)</string>
+    <string name="register_10">10s batching (delay=10000ms)</string>
+
+    <string name="counting_title">Total Steps: %1$d</string>
+    <string name="sensor_counter">Step Counter</string>
+    <string name="sensor_detector">Step Detector</string>
+    <string name="counting_description">Sensor: %1$s\nMax sensor event delay: %2$,d \u00B5s\nAge of
+        events in s:\n%3$s
+    </string>
+
+    <string name="error_title">Error</string>
+    <string name="error_nosensor">This sample requires at least Android KitKat (4.4) and a device
+        with the step sensor.\n\nThis device does not appear to meet these requirements, as an
+        alternative you may want to consider using the gyro sensor and implement your own step
+        recognition as a fallback.
+    </string>
+    <string name="warning_nobatching">The listener has been registered, but batch mode could not be
+        enabled.\n\nIt is likely that it is not supported by this device.\n\nSensor events will be
+        delivered in continuous mode.
+    </string>
+
+    <string name="action_notagain">Do not show again</string>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/res/values/styles.xml b/samples/browseable/BatchStepSensor/res/values/styles.xml
new file mode 100644
index 0000000..23e8181
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/styles.xml
@@ -0,0 +1,92 @@
+<resources>
+
+    <!-- Card Stream -->
+    <style name="CardStream">
+        <item name="android:paddingBottom">@dimen/card_stream_bottom_padding</item>
+        <item name="android:divider">@null</item>
+        <item name="android:orientation">vertical</item>
+    </style>
+
+    <!-- Main card -->
+    <style name="Card">
+        <item name="android:background">@drawable/card_bg</item>
+        <item name="android:layout_margin">@dimen/card_margin</item>
+    </style>
+
+    <style name="CardContentArea">
+        <item name="android:paddingBottom">@dimen/card_padding</item>
+    </style>
+
+    <style name="CardActionArea">
+        <item name="android:background">@color/card_action_bg</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:paddingBottom">@dimen/card_action_margin</item>
+    </style>
+
+    <style name="CardElement">
+        <item name="android:paddingLeft">@dimen/card_padding</item>
+        <item name="android:paddingRight">@dimen/card_padding</item>
+    </style>
+
+    <!-- Content of main card -->
+    <style name="CardTitleBase" parent="@style/CardElement">
+        <item name="android:paddingTop">@dimen/card_padding</item>
+        <item name="android:textSize">@dimen/card_content_title</item>
+    </style>
+
+    <style name="CardTitle" parent="@style/CardTitleBase">
+    </style>
+
+    <style name="CardContentBase" parent="@style/CardElement">
+        <item name="android:paddingTop">@dimen/card_padding</item>
+        <item name="android:textSize">@dimen/card_content_text</item>
+        <item name="android:textColor">@color/card_content_textcolor</item>
+    </style>
+
+    <style name="CardContent" parent="@style/CardContentBase">
+    </style>
+
+    <!-- Action Area Items -->
+    <style name="CardAction">
+        <item name="android:textSize">17sp</item>
+        <item name="android:layout_marginTop">@dimen/card_action_margin</item>
+        <item name="android:layout_marginLeft">@dimen/card_action_margin</item>
+        <item name="android:layout_marginRight">@dimen/card_action_margin</item>
+        <item name="android:paddingLeft">@dimen/card_action_padding</item>
+        <item name="android:drawablePadding">@dimen/card_action_padding</item>
+    </style>
+
+    <style name="CardActionNeutral" parent="@style/CardAction">
+        <item name="android:background">@drawable/card_action_bg</item>
+        <item name="android:drawableStart">@drawable/card_action_icon_neutral</item>
+        <item name="android:textColor">@drawable/card_action_text</item>
+    </style>
+
+    <style name="CardActionNegative" parent="@style/CardAction">
+        <item name="android:background">@drawable/card_action_bg_negative</item>
+        <item name="android:drawableStart">@drawable/card_action_icon_negative</item>
+        <item name="android:textColor">@drawable/card_action_text_negative</item>
+    </style>
+
+    <style name="CardActionPositive" parent="@style/CardAction">
+        <item name="android:background">@drawable/card_action_bg_positive</item>
+        <item name="android:drawableStart">@drawable/card_action_icon_positive</item>
+        <item name="android:textColor">@drawable/card_action_text_positive</item>
+    </style>
+
+    <!-- Card Action Progress -->
+    <style name="CardProgressLayout" parent="@style/CardAction">
+        <item name="android:layout_marginLeft">@dimen/card_action_margin</item>
+        <item name="android:layout_marginRight">@dimen/card_action_margin</item>
+        <item name="android:paddingLeft">@dimen/card_action_padding</item>
+        <item name="android:paddingRight">@dimen/card_action_padding</item>
+        <item name="android:background">#EEE</item>
+    </style>
+
+    <style name="CardProgressText" parent="@style/CardAction">
+        <item name="android:textColor">#77000000</item>
+        <item name="android:textSize">12sp</item>
+        <item name="android:paddingLeft">0dp</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/BatchStepSensor/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/BatchStepSensor/res/values/template-dimens.xml
diff --git a/samples/browseable/BatchStepSensor/res/values/template-styles.xml b/samples/browseable/BatchStepSensor/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/BatchStepSensorFragment.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/BatchStepSensorFragment.java
new file mode 100644
index 0000000..467c427
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/BatchStepSensorFragment.java
@@ -0,0 +1,592 @@
+/*
+* Copyright 2014 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.batchstepsensor;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+
+import com.example.android.common.logger.Log;
+import com.example.android.batchstepsensor.cardstream.Card;
+import com.example.android.batchstepsensor.cardstream.CardStream;
+import com.example.android.batchstepsensor.cardstream.CardStreamFragment;
+import com.example.android.batchstepsensor.cardstream.OnCardClickListener;
+
+public class BatchStepSensorFragment extends Fragment implements OnCardClickListener {
+
+    public static final String TAG = "StepSensorSample";
+    // Cards
+    private CardStreamFragment mCards = null;
+
+    // Card tags
+    public static final String CARD_INTRO = "intro";
+    public static final String CARD_REGISTER_DETECTOR = "register_detector";
+    public static final String CARD_REGISTER_COUNTER = "register_counter";
+    public static final String CARD_BATCHING_DESCRIPTION = "register_batching_description";
+    public static final String CARD_COUNTING = "counting";
+    public static final String CARD_EXPLANATION = "explanation";
+    public static final String CARD_NOBATCHSUPPORT = "error";
+
+    // Actions from REGISTER cards
+    public static final int ACTION_REGISTER_DETECT_NOBATCHING = 10;
+    public static final int ACTION_REGISTER_DETECT_BATCHING_5s = 11;
+    public static final int ACTION_REGISTER_DETECT_BATCHING_10s = 12;
+    public static final int ACTION_REGISTER_COUNT_NOBATCHING = 21;
+    public static final int ACTION_REGISTER_COUNT_BATCHING_5s = 22;
+    public static final int ACTION_REGISTER_COUNT_BATCHING_10s = 23;
+    // Action from COUNTING card
+    public static final int ACTION_UNREGISTER = 1;
+    // Actions from description cards
+    private static final int ACTION_BATCHING_DESCRIPTION_DISMISS = 2;
+    private static final int ACTION_EXPLANATION_DISMISS = 3;
+
+    // State of application, used to register for sensors when app is restored
+    public static final int STATE_OTHER = 0;
+    public static final int STATE_COUNTER = 1;
+    public static final int STATE_DETECTOR = 2;
+
+    // Bundle tags used to store data when restoring application state
+    private static final String BUNDLE_STATE = "state";
+    private static final String BUNDLE_LATENCY = "latency";
+    private static final String BUNDLE_STEPS = "steps";
+
+    // max batch latency is specified in microseconds
+    private static final int BATCH_LATENCY_0 = 0; // no batching
+    private static final int BATCH_LATENCY_10s = 10000000;
+    private static final int BATCH_LATENCY_5s = 5000000;
+
+    /*
+    For illustration we keep track of the last few events and show their delay from when the
+    event occurred until it was received by the event listener.
+    These variables keep track of the list of timestamps and the number of events.
+     */
+    // Number of events to keep in queue and display on card
+    private static final int EVENT_QUEUE_LENGTH = 10;
+    // List of timestamps when sensor events occurred
+    private float[] mEventDelays = new float[EVENT_QUEUE_LENGTH];
+
+    // number of events in event list
+    private int mEventLength = 0;
+    // pointer to next entry in sensor event list
+    private int mEventData = 0;
+
+    // Steps counted in current session
+    private int mSteps = 0;
+    // Value of the step counter sensor when the listener was registered.
+    // (Total steps are calculated from this value.)
+    private int mCounterSteps = 0;
+    // Steps counted by the step counter previously. Used to keep counter consistent across rotation
+    // changes
+    private int mPreviousCounterSteps = 0;
+    // State of the app (STATE_OTHER, STATE_COUNTER or STATE_DETECTOR)
+    private int mState = STATE_OTHER;
+    // When a listener is registered, the batch sensor delay in microseconds
+    private int mMaxDelay = 0;
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        CardStreamFragment stream = getCardStream();
+        if (stream.getVisibleCardCount() < 1) {
+            // No cards are visible, started for the first time
+            // Prepare all cards and show the intro card.
+            initialiseCards();
+            showIntroCard();
+            // Show the registration card if the hardware is supported, show an error otherwise
+            if (isKitkatWithStepSensor()) {
+                showRegisterCard();
+            } else {
+                showErrorCard();
+            }
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // BEGIN_INCLUDE(onpause)
+        // Unregister the listener when the application is paused
+        unregisterListeners();
+        // END_INCLUDE(onpause)
+    }
+
+    /**
+     * Returns true if this device is supported. It needs to be running Android KitKat (4.4) or
+     * higher and has a step counter and step detector sensor.
+     * This check is useful when an app provides an alternative implementation or different
+     * functionality if the step sensors are not available or this code runs on a platform version
+     * below Android KitKat. If this functionality is required, then the minSDK parameter should
+     * be specified appropriately in the AndroidManifest.
+     *
+     * @return True iff the device can run this sample
+     */
+    private boolean isKitkatWithStepSensor() {
+        // BEGIN_INCLUDE(iskitkatsensor)
+        // Require at least Android KitKat
+        int currentApiVersion = android.os.Build.VERSION.SDK_INT;
+        // Check that the device supports the step counter and detector sensors
+        PackageManager packageManager = getActivity().getPackageManager();
+        return currentApiVersion >= android.os.Build.VERSION_CODES.KITKAT
+                && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER)
+                && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
+        // END_INCLUDE(iskitkatsensor)
+    }
+
+    /**
+     * Handles a click on a card action.
+     * Registers a SensorEventListener (see {@link #registerEventListener(int, int)}) with the
+     * selected delay, dismisses cards and unregisters the listener
+     * (see {@link #unregisterListeners()}).
+     * Actions are defined when a card is created.
+     *
+     * @param cardActionId
+     * @param cardTag
+     */
+    @Override
+    public void onCardClick(int cardActionId, String cardTag) {
+
+        switch (cardActionId) {
+            // BEGIN_INCLUDE(onclick)
+            // Register Step Counter card
+            case ACTION_REGISTER_COUNT_NOBATCHING:
+                registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_COUNTER);
+                break;
+            case ACTION_REGISTER_COUNT_BATCHING_5s:
+                registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_COUNTER);
+                break;
+            case ACTION_REGISTER_COUNT_BATCHING_10s:
+                registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_COUNTER);
+                break;
+
+            // Register Step Detector card
+            case ACTION_REGISTER_DETECT_NOBATCHING:
+                registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_DETECTOR);
+                break;
+            case ACTION_REGISTER_DETECT_BATCHING_5s:
+                registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_DETECTOR);
+                break;
+            case ACTION_REGISTER_DETECT_BATCHING_10s:
+                registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_DETECTOR);
+                break;
+
+            // Unregister card
+            case ACTION_UNREGISTER:
+                showRegisterCard();
+                unregisterListeners();
+                // reset the application state when explicitly unregistered
+                mState = STATE_OTHER;
+                break;
+            // END_INCLUDE(onclick)
+            // Explanation cards
+            case ACTION_BATCHING_DESCRIPTION_DISMISS:
+                // permanently remove the batch description card, it will not be shown again
+                getCardStream().removeCard(CARD_BATCHING_DESCRIPTION);
+                break;
+            case ACTION_EXPLANATION_DISMISS:
+                // permanently remove the explanation card, it will not be shown again
+                getCardStream().removeCard(CARD_EXPLANATION);
+        }
+
+        // For register cards, display the counting card
+        if (cardTag.equals(CARD_REGISTER_COUNTER) || cardTag.equals(CARD_REGISTER_DETECTOR)) {
+            showCountingCards();
+        }
+    }
+
+    /**
+     * Register a {@link android.hardware.SensorEventListener} for the sensor and max batch delay.
+     * The maximum batch delay specifies the maximum duration in microseconds for which subsequent
+     * sensor events can be temporarily stored by the sensor before they are delivered to the
+     * registered SensorEventListener. A larger delay allows the system to handle sensor events more
+     * efficiently, allowing the system to switch to a lower power state while the sensor is
+     * capturing events. Once the max delay is reached, all stored events are delivered to the
+     * registered listener. Note that this value only specifies the maximum delay, the listener may
+     * receive events quicker. A delay of 0 disables batch mode and registers the listener in
+     * continuous mode.
+     * The optimium batch delay depends on the application. For example, a delay of 5 seconds or
+     * higher may be appropriate for an  application that does not update the UI in real time.
+     *
+     * @param maxdelay
+     * @param sensorType
+     */
+    private void registerEventListener(int maxdelay, int sensorType) {
+        // BEGIN_INCLUDE(register)
+
+        // Keep track of state so that the correct sensor type and batch delay can be set up when
+        // the app is restored (for example on screen rotation).
+        mMaxDelay = maxdelay;
+        if (sensorType == Sensor.TYPE_STEP_COUNTER) {
+            mState = STATE_COUNTER;
+            /*
+            Reset the initial step counter value, the first event received by the event listener is
+            stored in mCounterSteps and used to calculate the total number of steps taken.
+             */
+            mCounterSteps = 0;
+            Log.i(TAG, "Event listener for step counter sensor registered with a max delay of "
+                    + mMaxDelay);
+        } else {
+            mState = STATE_DETECTOR;
+            Log.i(TAG, "Event listener for step detector sensor registered with a max delay of "
+                    + mMaxDelay);
+        }
+
+        // Get the default sensor for the sensor type from the SenorManager
+        SensorManager sensorManager =
+                (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE);
+        // sensorType is either Sensor.TYPE_STEP_COUNTER or Sensor.TYPE_STEP_DETECTOR
+        Sensor sensor = sensorManager.getDefaultSensor(sensorType);
+
+        // Register the listener for this sensor in batch mode.
+        // If the max delay is 0, events will be delivered in continuous mode without batching.
+        final boolean batchMode = sensorManager.registerListener(
+                mListener, sensor, SensorManager.SENSOR_DELAY_NORMAL, maxdelay);
+
+        if (!batchMode) {
+            // Batch mode could not be enabled, show a warning message and switch to continuous mode
+            getCardStream().getCard(CARD_NOBATCHSUPPORT)
+                    .setDescription(getString(R.string.warning_nobatching));
+            getCardStream().showCard(CARD_NOBATCHSUPPORT);
+            Log.w(TAG, "Could not register sensor listener in batch mode, " +
+                    "falling back to continuous mode.");
+        }
+
+        if (maxdelay > 0 && batchMode) {
+            // Batch mode was enabled successfully, show a description card
+            getCardStream().showCard(CARD_BATCHING_DESCRIPTION);
+        }
+
+        // Show the explanation card
+        getCardStream().showCard(CARD_EXPLANATION);
+
+        // END_INCLUDE(register)
+
+    }
+
+    /**
+     * Unregisters the sensor listener if it is registered.
+     */
+    private void unregisterListeners() {
+        // BEGIN_INCLUDE(unregister)
+        SensorManager sensorManager =
+                (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE);
+        sensorManager.unregisterListener(mListener);
+        Log.i(TAG, "Sensor listener unregistered.");
+
+        // END_INCLUDE(unregister)
+    }
+
+    /**
+     * Resets the step counter by clearing all counting variables and lists.
+     */
+    private void resetCounter() {
+        // BEGIN_INCLUDE(reset)
+        mSteps = 0;
+        mCounterSteps = 0;
+        mEventLength = 0;
+        mEventDelays = new float[EVENT_QUEUE_LENGTH];
+        mPreviousCounterSteps = 0;
+        // END_INCLUDE(reset)
+    }
+
+
+    /**
+     * Listener that handles step sensor events for step detector and step counter sensors.
+     */
+    private final SensorEventListener mListener = new SensorEventListener() {
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            // BEGIN_INCLUDE(sensorevent)
+            // store the delay of this event
+            recordDelay(event);
+            final String delayString = getDelayString();
+
+            if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) {
+                // A step detector event is received for each step.
+                // This means we need to count steps ourselves
+
+                mSteps += event.values.length;
+
+                // Update the card with the latest step count
+                getCardStream().getCard(CARD_COUNTING)
+                        .setTitle(getString(R.string.counting_title, mSteps))
+                        .setDescription(getString(R.string.counting_description,
+                                getString(R.string.sensor_detector), mMaxDelay, delayString));
+
+                Log.i(TAG,
+                        "New step detected by STEP_DETECTOR sensor. Total step count: " + mSteps);
+
+            } else if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
+
+                /*
+                A step counter event contains the total number of steps since the listener
+                was first registered. We need to keep track of this initial value to calculate the
+                number of steps taken, as the first value a listener receives is undefined.
+                 */
+                if (mCounterSteps < 1) {
+                    // initial value
+                    mCounterSteps = (int) event.values[0];
+                }
+
+                // Calculate steps taken based on first counter value received.
+                mSteps = (int) event.values[0] - mCounterSteps;
+
+                // Add the number of steps previously taken, otherwise the counter would start at 0.
+                // This is needed to keep the counter consistent across rotation changes.
+                mSteps = mSteps + mPreviousCounterSteps;
+
+                // Update the card with the latest step count
+                getCardStream().getCard(CARD_COUNTING)
+                        .setTitle(getString(R.string.counting_title, mSteps))
+                        .setDescription(getString(R.string.counting_description,
+                                getString(R.string.sensor_counter), mMaxDelay, delayString));
+                Log.i(TAG, "New step detected by STEP_COUNTER sensor. Total step count: " + mSteps);
+                // END_INCLUDE(sensorevent)
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+        }
+    };
+
+    /**
+     * Records the delay for the event.
+     *
+     * @param event
+     */
+    private void recordDelay(SensorEvent event) {
+        // Calculate the delay from when event was recorded until it was received here in ms
+        // Event timestamp is recorded in us accuracy, but ms accuracy is sufficient here
+        mEventDelays[mEventData] = System.currentTimeMillis() - (event.timestamp / 1000000L);
+
+        // Increment length counter
+        mEventLength = Math.min(EVENT_QUEUE_LENGTH, mEventLength + 1);
+        // Move pointer to the next (oldest) location
+        mEventData = (mEventData + 1) % EVENT_QUEUE_LENGTH;
+    }
+
+    private final StringBuffer mDelayStringBuffer = new StringBuffer();
+
+    /**
+     * Returns a string describing the sensor delays recorded in
+     * {@link #recordDelay(android.hardware.SensorEvent)}.
+     *
+     * @return
+     */
+    private String getDelayString() {
+        // Empty the StringBuffer
+        mDelayStringBuffer.setLength(0);
+
+        // Loop over all recorded delays and append them to the buffer as a decimal
+        for (int i = 0; i < mEventLength; i++) {
+            if (i > 0) {
+                mDelayStringBuffer.append(", ");
+            }
+            final int index = (mEventData + i) % EVENT_QUEUE_LENGTH;
+            final float delay = mEventDelays[index] / 1000f; // convert delay from ms into s
+            mDelayStringBuffer.append(String.format("%1.1f", delay));
+        }
+
+        return mDelayStringBuffer.toString();
+    }
+
+    /**
+     * Records the state of the application into the {@link android.os.Bundle}.
+     *
+     * @param outState
+     */
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        // BEGIN_INCLUDE(saveinstance)
+        super.onSaveInstanceState(outState);
+        // Store all variables required to restore the state of the application
+        outState.putInt(BUNDLE_LATENCY, mMaxDelay);
+        outState.putInt(BUNDLE_STATE, mState);
+        outState.putInt(BUNDLE_STEPS, mSteps);
+        // END_INCLUDE(saveinstance)
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        // BEGIN_INCLUDE(restore)
+        // Fragment is being restored, reinitialise its state with data from the bundle
+        if (savedInstanceState != null) {
+            resetCounter();
+            mSteps = savedInstanceState.getInt(BUNDLE_STEPS);
+            mState = savedInstanceState.getInt(BUNDLE_STATE);
+            mMaxDelay = savedInstanceState.getInt(BUNDLE_LATENCY);
+
+            // Register listeners again if in detector or counter states with restored delay
+            if (mState == STATE_DETECTOR) {
+                registerEventListener(mMaxDelay, Sensor.TYPE_STEP_DETECTOR);
+            } else if (mState == STATE_COUNTER) {
+                // store the previous number of steps to keep  step counter count consistent
+                mPreviousCounterSteps = mSteps;
+                registerEventListener(mMaxDelay, Sensor.TYPE_STEP_COUNTER);
+            }
+        }
+        // END_INCLUDE(restore)
+    }
+
+    /**
+     * Hides the registration cards, reset the counter and show the step counting card.
+     */
+    private void showCountingCards() {
+        // Hide the registration cards
+        getCardStream().hideCard(CARD_REGISTER_DETECTOR);
+        getCardStream().hideCard(CARD_REGISTER_COUNTER);
+
+        // Show the explanation card if it has not been dismissed
+        getCardStream().showCard(CARD_EXPLANATION);
+
+        // Reset the step counter, then show the step counting card
+        resetCounter();
+
+        // Set the inital text for the step counting card before a step is recorded
+        String sensor = "-";
+        if (mState == STATE_COUNTER) {
+            sensor = getString(R.string.sensor_counter);
+        } else if (mState == STATE_DETECTOR) {
+            sensor = getString(R.string.sensor_detector);
+        }
+        // Set initial text
+        getCardStream().getCard(CARD_COUNTING)
+                .setTitle(getString(R.string.counting_title, 0))
+                .setDescription(getString(R.string.counting_description, sensor, mMaxDelay, "-"));
+
+        // Show the counting card and make it undismissable
+        getCardStream().showCard(CARD_COUNTING, false);
+
+    }
+
+    /**
+     * Show the introduction card
+     */
+    private void showIntroCard() {
+        Card c = new Card.Builder(this, CARD_INTRO)
+                .setTitle(getString(R.string.intro_title))
+                .setDescription(getString(R.string.intro_message))
+                .build(getActivity());
+        getCardStream().addCard(c, true);
+    }
+
+    /**
+     * Show two registration cards, one for the step detector and counter sensors.
+     */
+    private void showRegisterCard() {
+        // Hide the counting and explanation cards
+        getCardStream().hideCard(CARD_BATCHING_DESCRIPTION);
+        getCardStream().hideCard(CARD_EXPLANATION);
+        getCardStream().hideCard(CARD_COUNTING);
+
+        // Show two undismissable registration cards, one for each step sensor
+        getCardStream().showCard(CARD_REGISTER_DETECTOR, false);
+        getCardStream().showCard(CARD_REGISTER_COUNTER, false);
+    }
+
+    /**
+     * Show the error card.
+     */
+    private void showErrorCard() {
+        getCardStream().showCard(CARD_NOBATCHSUPPORT, false);
+    }
+
+    /**
+     * Initialise Cards.
+     */
+    private void initialiseCards() {
+        // Step counting
+        Card c = new Card.Builder(this, CARD_COUNTING)
+                .setTitle("Steps")
+                .setDescription("")
+                .addAction("Unregister Listener", ACTION_UNREGISTER, Card.ACTION_NEGATIVE)
+                .build(getActivity());
+        getCardStream().addCard(c);
+
+        // Register step detector listener
+        c = new Card.Builder(this, CARD_REGISTER_DETECTOR)
+                .setTitle(getString(R.string.register_detector_title))
+                .setDescription(getString(R.string.register_detector_description))
+                .addAction(getString(R.string.register_0),
+                        ACTION_REGISTER_DETECT_NOBATCHING, Card.ACTION_NEUTRAL)
+                .addAction(getString(R.string.register_5),
+                        ACTION_REGISTER_DETECT_BATCHING_5s, Card.ACTION_NEUTRAL)
+                .addAction(getString(R.string.register_10),
+                        ACTION_REGISTER_DETECT_BATCHING_10s, Card.ACTION_NEUTRAL)
+                .build(getActivity());
+        getCardStream().addCard(c);
+
+        // Register step counter listener
+        c = new Card.Builder(this, CARD_REGISTER_COUNTER)
+                .setTitle(getString(R.string.register_counter_title))
+                .setDescription(getString(R.string.register_counter_description))
+                .addAction(getString(R.string.register_0),
+                        ACTION_REGISTER_COUNT_NOBATCHING, Card.ACTION_NEUTRAL)
+                .addAction(getString(R.string.register_5),
+                        ACTION_REGISTER_COUNT_BATCHING_5s, Card.ACTION_NEUTRAL)
+                .addAction(getString(R.string.register_10),
+                        ACTION_REGISTER_COUNT_BATCHING_10s, Card.ACTION_NEUTRAL)
+                .build(getActivity());
+        getCardStream().addCard(c);
+
+
+        // Batching description
+        c = new Card.Builder(this, CARD_BATCHING_DESCRIPTION)
+                .setTitle(getString(R.string.batching_queue_title))
+                .setDescription(getString(R.string.batching_queue_description))
+                .addAction(getString(R.string.action_notagain),
+                        ACTION_BATCHING_DESCRIPTION_DISMISS, Card.ACTION_POSITIVE)
+                .build(getActivity());
+        getCardStream().addCard(c);
+
+        // Explanation
+        c = new Card.Builder(this, CARD_EXPLANATION)
+                .setDescription(getString(R.string.explanation_description))
+                .addAction(getString(R.string.action_notagain),
+                        ACTION_EXPLANATION_DISMISS, Card.ACTION_POSITIVE)
+                .build(getActivity());
+        getCardStream().addCard(c);
+
+        // Error
+        c = new Card.Builder(this, CARD_NOBATCHSUPPORT)
+                .setTitle(getString(R.string.error_title))
+                .setDescription(getString(R.string.error_nosensor))
+                .build(getActivity());
+        getCardStream().addCard(c);
+    }
+
+    /**
+     * Returns the cached CardStreamFragment used to show cards.
+     *
+     * @return
+     */
+    private CardStreamFragment getCardStream() {
+        if (mCards == null) {
+            mCards = ((CardStream) getActivity()).getCardStream();
+        }
+        return mCards;
+    }
+
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/MainActivity.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/MainActivity.java
new file mode 100644
index 0000000..5e4f7e3
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/MainActivity.java
@@ -0,0 +1,97 @@
+/*
+* Copyright 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.batchstepsensor;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+
+import com.example.android.batchstepsensor.cardstream.CardStream;
+import com.example.android.batchstepsensor.cardstream.CardStreamFragment;
+import com.example.android.batchstepsensor.cardstream.CardStreamState;
+import com.example.android.batchstepsensor.cardstream.OnCardClickListener;
+import com.example.android.batchstepsensor.cardstream.StreamRetentionFragment;
+
+public class MainActivity extends SampleActivityBase implements CardStream {
+    public static final String TAG = "MainActivity";
+    public static final String FRAGTAG = "BatchStepSensorFragment";
+
+    private CardStreamFragment mCardStreamFragment;
+
+    private StreamRetentionFragment mRetentionFragment;
+    private static final String RETENTION_TAG = "retention";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentManager fm = getSupportFragmentManager();
+        BatchStepSensorFragment fragment =
+                (BatchStepSensorFragment) fm.findFragmentByTag(FRAGTAG);
+
+        if (fragment == null) {
+            FragmentTransaction transaction = fm.beginTransaction();
+            fragment = new BatchStepSensorFragment();
+            transaction.add(fragment, FRAGTAG);
+            transaction.commit();
+        }
+
+        // Use fragment as click listener for cards, but must implement correct interface
+        if(!(fragment instanceof OnCardClickListener)){
+            throw new ClassCastException("BatchStepSensorFragment must " +
+                    "implement OnCardClickListener interface.");
+        }
+        OnCardClickListener clickListener = (OnCardClickListener) fm.findFragmentByTag(FRAGTAG);
+
+        mRetentionFragment = (StreamRetentionFragment) fm.findFragmentByTag(RETENTION_TAG);
+        if (mRetentionFragment == null) {
+            mRetentionFragment = new StreamRetentionFragment();
+            fm.beginTransaction().add(mRetentionFragment, RETENTION_TAG).commit();
+        } else {
+            // If the retention fragment already existed, we need to pull some state.
+            // pull state out
+            CardStreamState state = mRetentionFragment.getCardStream();
+
+            // dump it in CardStreamFragment.
+            mCardStreamFragment =
+                    (CardStreamFragment) fm.findFragmentById(R.id.fragment_cardstream);
+            mCardStreamFragment.restoreState(state, clickListener);
+        }
+    }
+
+    public CardStreamFragment getCardStream() {
+        if (mCardStreamFragment == null) {
+            mCardStreamFragment = (CardStreamFragment)
+                    getSupportFragmentManager().findFragmentById(R.id.fragment_cardstream);
+        }
+        return mCardStreamFragment;
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        CardStreamState state = getCardStream().dumpState();
+        mRetentionFragment.storeCardStream(state);
+    }
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/Card.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/Card.java
new file mode 100644
index 0000000..a3723e8
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/Card.java
@@ -0,0 +1,752 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.example.android.batchstepsensor.R;
+
+import java.util.ArrayList;
+
+/**
+ * A Card contains a description and has a visual state. Optionally a card also contains a title,
+ * progress indicator and zero or more actions. It is constructed through the {@link Builder}.
+ */
+public class Card {
+
+    public static final int ACTION_POSITIVE = 1;
+    public static final int ACTION_NEGATIVE = 2;
+    public static final int ACTION_NEUTRAL = 3;
+
+    public static final int PROGRESS_TYPE_NO_PROGRESS = 0;
+    public static final int PROGRESS_TYPE_NORMAL = 1;
+    public static final int PROGRESS_TYPE_INDETERMINATE = 2;
+    public static final int PROGRESS_TYPE_LABEL = 3;
+
+    private OnCardClickListener mClickListener;
+
+
+    // The card model contains a reference to its desired layout (for extensibility), title,
+    // description, zero to many action buttons, and zero or 1 progress indicators.
+    private int mLayoutId = R.layout.card;
+
+    /**
+     * Tag that uniquely identifies this card.
+     */
+    private String mTag = null;
+
+    private String mTitle = null;
+    private String mDescription = null;
+
+    private View mCardView = null;
+    private View mOverlayView = null;
+    private TextView mTitleView = null;
+    private TextView mDescView = null;
+    private View mActionAreaView = null;
+
+    private Animator mOngoingAnimator = null;
+
+    /**
+     * Visual state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or
+     * {@link #CARD_STATE_INACTIVE}.
+     */
+    private int mCardState = CARD_STATE_NORMAL;
+    public static final int CARD_STATE_NORMAL = 1;
+    public static final int CARD_STATE_FOCUSED = 2;
+    public static final int CARD_STATE_INACTIVE = 3;
+
+    /**
+     * Represent actions that can be taken from the card.  Stylistically the developer can
+     * designate the action as positive, negative (ok/cancel, for instance), or neutral.
+     * This "type" can be used as a UI hint.
+     * @see com.example.android.sensors.batchstepsensor.Card.CardAction
+     */
+    private ArrayList<CardAction> mCardActions = new ArrayList<CardAction>();
+
+    /**
+     * Some cards will have a sense of "progress" which should be associated with, but separated
+     * from its "parent" card.  To push for simplicity in samples, Cards are designed to have
+     * a maximum of one progress indicator per Card.
+     */
+    private CardProgress mCardProgress = null;
+
+    public Card() {
+    }
+
+    public String getTag() {
+        return mTag;
+    }
+
+    public View getView() {
+        return mCardView;
+    }
+
+
+    public Card setDescription(String desc) {
+        if (mDescView != null) {
+            mDescription = desc;
+            mDescView.setText(desc);
+        }
+        return this;
+    }
+
+    public Card setTitle(String title) {
+        if (mTitleView != null) {
+            mTitle = title;
+            mTitleView.setText(title);
+        }
+        return this;
+    }
+
+
+    /**
+     * Return the UI state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED}
+     * or {@link #CARD_STATE_INACTIVE}.
+     */
+    public int getState() {
+        return mCardState;
+    }
+
+    /**
+     * Set the UI state. The parameter describes the state and must be either
+     * {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or {@link #CARD_STATE_INACTIVE}.
+     * Note: This method must be called from the UI Thread.
+     * @param state
+     * @return The card itself, allows for chaining of calls
+     */
+    public Card setState(int state) {
+        mCardState = state;
+        if (null != mOverlayView) {
+            if (null != mOngoingAnimator) {
+                mOngoingAnimator.end();
+                mOngoingAnimator = null;
+            }
+            switch (state) {
+                case CARD_STATE_NORMAL: {
+                    mOverlayView.setVisibility(View.GONE);
+                    mOverlayView.setAlpha(1.f);
+                    break;
+                }
+                case CARD_STATE_FOCUSED: {
+                    mOverlayView.setVisibility(View.VISIBLE);
+                    mOverlayView.setBackgroundResource(R.drawable.card_overlay_focused);
+                    ObjectAnimator animator = ObjectAnimator.ofFloat(mOverlayView, "alpha", 0.f);
+                    animator.setRepeatMode(ObjectAnimator.REVERSE);
+                    animator.setRepeatCount(ObjectAnimator.INFINITE);
+                    animator.setDuration(1000);
+                    animator.start();
+                    mOngoingAnimator = animator;
+                    break;
+                }
+                case CARD_STATE_INACTIVE: {
+                    mOverlayView.setVisibility(View.VISIBLE);
+                    mOverlayView.setAlpha(1.f);
+                    mOverlayView.setBackgroundColor(Color.argb(0xaa, 0xcc, 0xcc, 0xcc));
+                    break;
+                }
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Set the type of progress indicator.
+     * The progress type can only be changed if the Card was initially build with a progress
+     * indicator.
+     * See {@link Builder#setProgressType(int)}.
+     * Must be a value of either {@link #PROGRESS_TYPE_NORMAL},
+     * {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL} or
+     * {@link #PROGRESS_TYPE_NO_PROGRESS}.
+     * @param progressType
+     * @return The card itself, allows for chaining of calls
+     */
+    public Card setProgressType(int progressType) {
+        if (mCardProgress == null) {
+            mCardProgress = new CardProgress();
+        }
+        mCardProgress.setProgressType(progressType);
+        return this;
+    }
+
+    /**
+     * Return the progress indicator type. A value of either {@link #PROGRESS_TYPE_NORMAL},
+     * {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL}. Otherwise if no progress
+     * indicator is enabled, {@link #PROGRESS_TYPE_NO_PROGRESS} is returned.
+     * @return
+     */
+    public int getProgressType() {
+        if (mCardProgress == null) {
+            return PROGRESS_TYPE_NO_PROGRESS;
+        }
+        return mCardProgress.progressType;
+    }
+
+    /**
+     * Set the progress to the specified value. Only applicable if the card has a
+     * {@link #PROGRESS_TYPE_NORMAL} progress type.
+     * @param progress
+     * @return
+     * @see #setMaxProgress(int)
+     */
+    public Card setProgress(int progress) {
+        if (mCardProgress != null) {
+            mCardProgress.setProgress(progress);
+        }
+        return this;
+    }
+
+    /**
+     * Set the range of the progress to 0...max. Only applicable if the card has a
+     * {@link #PROGRESS_TYPE_NORMAL} progress type.
+     * @return
+     */
+    public Card setMaxProgress(int max){
+        if (mCardProgress != null) {
+            mCardProgress.setMax(max);
+        }
+        return this;
+    }
+
+    /**
+     * Set the label text for the progress if the card has a progress type of
+     * {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or
+     * {@link #PROGRESS_TYPE_LABEL}
+     * @param text
+     * @return
+     */
+    public Card setProgressLabel(String text) {
+        if (mCardProgress != null) {
+            mCardProgress.setProgressLabel(text);
+        }
+        return this;
+    }
+
+    /**
+     * Toggle the visibility of the progress section of the card. Only applicable if
+     * the card has a progress type of
+     * {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or
+     * {@link #PROGRESS_TYPE_LABEL}.
+     * @param isVisible
+     * @return
+     */
+    public Card setProgressVisibility(boolean isVisible) {
+        if (mCardProgress.progressView == null) {
+            return this; // Card does not have progress
+        }
+        mCardProgress.progressView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+
+        return this;
+    }
+
+    /**
+     * Adds an action to this card during build time.
+     *
+     * @param label
+     * @param id
+     * @param type
+     */
+    private void addAction(String label, int id, int type) {
+        CardAction cardAction = new CardAction();
+        cardAction.label = label;
+        cardAction.id = id;
+        cardAction.type = type;
+        mCardActions.add(cardAction);
+    }
+
+    /**
+     * Toggles the visibility of a card action.
+     * @param actionId
+     * @param isVisible
+     * @return
+     */
+    public Card setActionVisibility(int actionId, boolean isVisible) {
+        int visibilityFlag = isVisible ? View.VISIBLE : View.GONE;
+        for (CardAction action : mCardActions) {
+            if (action.id == actionId && action.actionView != null) {
+                action.actionView.setVisibility(visibilityFlag);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Toggles visibility of the action area of this Card through an animation.
+     * @param isVisible
+     * @return
+     */
+    public Card setActionAreaVisibility(boolean isVisible) {
+        if (mActionAreaView == null) {
+            return this; // Card does not have an action area
+        }
+
+        if (isVisible) {
+            // Show the action area
+            mActionAreaView.setVisibility(View.VISIBLE);
+            mActionAreaView.setPivotY(0.f);
+            mActionAreaView.setPivotX(mCardView.getWidth() / 2.f);
+            mActionAreaView.setAlpha(0.5f);
+            mActionAreaView.setRotationX(-90.f);
+            mActionAreaView.animate().rotationX(0.f).alpha(1.f).setDuration(400);
+        } else {
+            // Hide the action area
+            mActionAreaView.setPivotY(0.f);
+            mActionAreaView.setPivotX(mCardView.getWidth() / 2.f);
+            mActionAreaView.animate().rotationX(-90.f).alpha(0.f).setDuration(400).setListener(
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mActionAreaView.setVisibility(View.GONE);
+                        }
+                    });
+        }
+        return this;
+    }
+
+
+    /**
+     * Creates a shallow clone of the card.  Shallow means all values are present, but no views.
+     * This is useful for saving/restoring in the case of configuration changes, like screen
+     * rotation.
+     *
+     * @return A shallow clone of the card instance
+     */
+    public Card createShallowClone() {
+        Card cloneCard = new Card();
+
+        // Outer card values
+        cloneCard.mTitle = mTitle;
+        cloneCard.mDescription = mDescription;
+        cloneCard.mTag = mTag;
+        cloneCard.mLayoutId = mLayoutId;
+        cloneCard.mCardState = mCardState;
+
+        // Progress
+        if (mCardProgress != null) {
+            cloneCard.mCardProgress = mCardProgress.createShallowClone();
+        }
+
+        // Actions
+        for (CardAction action : mCardActions) {
+            cloneCard.mCardActions.add(action.createShallowClone());
+        }
+
+        return cloneCard;
+    }
+
+
+    /**
+     * Prepare the card to be stored for configuration change.
+     */
+    public void prepareForConfigurationChange() {
+        // Null out views.
+        mCardView = null;
+        for (CardAction action : mCardActions) {
+            action.actionView = null;
+        }
+        mCardProgress.progressView = null;
+    }
+
+    /**
+     * Creates a new {@link #Card}.
+     */
+    public static class Builder {
+        private Card mCard;
+
+        /**
+         * Instantiate the builder with data from a shallow clone.
+         * @param listener
+         * @param card
+         * @see Card#createShallowClone()
+         */
+        protected Builder(OnCardClickListener listener, Card card) {
+            mCard = card;
+            mCard.mClickListener = listener;
+        }
+
+        /**
+         * Instantiate the builder with the tag of the card.
+         * @param listener
+         * @param tag
+         */
+        public Builder(OnCardClickListener listener, String tag) {
+            mCard = new Card();
+            mCard.mTag = tag;
+            mCard.mClickListener = listener;
+        }
+
+        public Builder setTitle(String title) {
+            mCard.mTitle = title;
+            return this;
+        }
+
+        public Builder setDescription(String desc) {
+            mCard.mDescription = desc;
+            return this;
+        }
+
+        /**
+         * Add an action.
+         * The type describes how this action will be displayed. Accepted values are
+         * {@link #ACTION_NEUTRAL}, {@link #ACTION_POSITIVE} or {@link #ACTION_NEGATIVE}.
+         *
+         * @param label The text to display for this action
+         * @param id Identifier for this action, supplied in the click listener
+         * @param type UI style of action
+         * @return
+         */
+        public Builder addAction(String label, int id, int type) {
+            mCard.addAction(label, id, type);
+            return this;
+        }
+
+        /**
+         * Override the default layout.
+         * The referenced layout file has to contain the same identifiers as defined in the default
+         * layout configuration.
+         * @param layout
+         * @return
+         * @see R.layout.card
+         */
+        public Builder setLayout(int layout) {
+            mCard.mLayoutId = layout;
+            return this;
+        }
+
+        /**
+         * Set the type of progress bar to display.
+         * Accepted values are:
+         * <ul>
+         *     <li>{@link #PROGRESS_TYPE_NO_PROGRESS} disables the progress indicator</li>
+         *     <li>{@link #PROGRESS_TYPE_NORMAL} 
+         *     displays a standard, linear progress indicator.</li>
+         *     <li>{@link #PROGRESS_TYPE_INDETERMINATE} displays an indeterminate (infite) progress
+         *     indicator.</li>
+         *     <li>{@link #PROGRESS_TYPE_LABEL} only displays a label text in the progress area
+         *     of the card.</li>
+         * </ul>
+         *
+         * @param progressType
+         * @return
+         */
+        public Builder setProgressType(int progressType) {
+            mCard.setProgressType(progressType);
+            return this;
+        }
+
+        public Builder setProgressLabel(String label) {
+            // ensure the progress layout has been initialized, use 'no progress' by default
+            if (mCard.mCardProgress == null) {
+                mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS);
+            }
+            mCard.mCardProgress.label = label;
+            return this;
+        }
+
+        public Builder setProgressMaxValue(int maxValue) {
+            // ensure the progress layout has been initialized, use 'no progress' by default
+            if (mCard.mCardProgress == null) {
+                mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS);
+            }
+            mCard.mCardProgress.maxValue = maxValue;
+            return this;
+        }
+
+        public Builder setStatus(int status) {
+            mCard.setState(status);
+            return this;
+        }
+
+        public Card build(Activity activity) {
+            LayoutInflater inflater = activity.getLayoutInflater();
+            // Inflating the card.
+            ViewGroup cardView = (ViewGroup) inflater.inflate(mCard.mLayoutId,
+                    (ViewGroup) activity.findViewById(R.id.card_stream), false);
+
+            // Check that the layout contains a TextView with the card_title id
+            View viewTitle = cardView.findViewById(R.id.card_title);
+            if (mCard.mTitle != null && viewTitle != null) {
+                mCard.mTitleView = (TextView) viewTitle;
+                mCard.mTitleView.setText(mCard.mTitle);
+            } else if (viewTitle != null) {
+                viewTitle.setVisibility(View.GONE);
+            }
+
+            // Check that the layout contains a TextView with the card_content id
+            View viewDesc = cardView.findViewById(R.id.card_content);
+            if (mCard.mDescription != null && viewDesc != null) {
+                mCard.mDescView = (TextView) viewDesc;
+                mCard.mDescView.setText(mCard.mDescription);
+            } else if (viewDesc != null) {
+                cardView.findViewById(R.id.card_content).setVisibility(View.GONE);
+            }
+
+
+            ViewGroup actionArea = (ViewGroup) cardView.findViewById(R.id.card_actionarea);
+
+            // Inflate Progress
+            initializeProgressView(inflater, actionArea);
+
+            // Inflate all action views.
+            initializeActionViews(inflater, cardView, actionArea);
+
+            mCard.mCardView = cardView;
+            mCard.mOverlayView = cardView.findViewById(R.id.card_overlay);
+
+            return mCard;
+        }
+
+        /**
+         * Initialize data from the given card.
+         * @param card
+         * @return
+         * @see Card#createShallowClone()
+         */
+        public Builder cloneFromCard(Card card) {
+            mCard = card.createShallowClone();
+            return this;
+        }
+
+        /**
+         * Build the action views by inflating the appropriate layouts and setting the text and 
+         * values.
+         * @param inflater
+         * @param cardView
+         * @param actionArea
+         */
+        private void initializeActionViews(LayoutInflater inflater, ViewGroup cardView,
+                                           ViewGroup actionArea) {
+            if (!mCard.mCardActions.isEmpty()) {
+                // Set action area to visible only when actions are visible
+                actionArea.setVisibility(View.VISIBLE);
+                mCard.mActionAreaView = actionArea;
+            }
+
+            // Inflate all card actions
+            for (final CardAction action : mCard.mCardActions) {
+
+                int useActionLayout = 0;
+                switch (action.type) {
+                    case Card.ACTION_POSITIVE:
+                        useActionLayout = R.layout.card_button_positive;
+                        break;
+                    case Card.ACTION_NEGATIVE:
+                        useActionLayout = R.layout.card_button_negative;
+                        break;
+                    case Card.ACTION_NEUTRAL:
+                    default:
+                        useActionLayout = R.layout.card_button_neutral;
+                        break;
+                }
+
+                action.actionView = inflater.inflate(useActionLayout, actionArea, false);
+                Button actionButton = (Button) action.actionView.findViewById(R.id.card_button);
+
+                actionButton.setText(action.label);
+                actionButton.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mCard.mClickListener.onCardClick(action.id, mCard.mTag);
+                    }
+                });
+                actionArea.addView(action.actionView);
+            }
+        }
+
+        /**
+         * Build the progress view into the given ViewGroup.
+         *
+         * @param inflater
+         * @param actionArea
+         */
+        private void initializeProgressView(LayoutInflater inflater, ViewGroup actionArea) {
+
+            // Only inflate progress layout if a progress type other than NO_PROGRESS was set.
+            if (mCard.mCardProgress != null) {
+                //Setup progress card.
+                View progressView = inflater.inflate(R.layout.card_progress, actionArea, false);
+                ProgressBar progressBar = 
+                        (ProgressBar) progressView.findViewById(R.id.card_progress);
+                ((TextView) progressView.findViewById(R.id.card_progress_text))
+                        .setText(mCard.mCardProgress.label);
+                progressBar.setMax(mCard.mCardProgress.maxValue);
+                progressBar.setProgress(0);
+                mCard.mCardProgress.progressView = progressView;
+                mCard.mCardProgress.setProgressType(mCard.getProgressType());
+                actionArea.addView(progressView);
+            }
+        }
+    }
+
+    /**
+     * Represents a clickable action, accessible from the bottom of the card.
+     * Fields include the label, an ID to specify the action that was performed in the callback,
+     * an action type (positive, negative, neutral), and the callback.
+     */
+    public class CardAction {
+
+        public String label;
+        public int id;
+        public int type;
+        public View actionView;
+
+        public CardAction createShallowClone() {
+            CardAction actionClone = new CardAction();
+            actionClone.label = label;
+            actionClone.id = id;
+            actionClone.type = type;
+            return actionClone;
+            // Not the view.  Never the view (don't want to hold view references for
+            // onConfigurationChange.
+        }
+
+    }
+
+    /**
+     * Describes the progress of a {@link Card}.
+     * Three types of progress are supported:
+     * <ul><li>{@link Card#PROGRESS_TYPE_NORMAL: Standard progress bar with label text</li>
+     * <li>{@link Card#PROGRESS_TYPE_INDETERMINATE}: Indeterminate progress bar with label txt</li>
+     * <li>{@link Card#PROGRESS_TYPE_LABEL}: Label only, no progresss bar</li>
+     * </ul>
+     */
+    public class CardProgress {
+        private int progressType = Card.PROGRESS_TYPE_NO_PROGRESS;
+        private String label = "";
+        private int currProgress = 0;
+        private int maxValue = 100;
+
+        public View progressView = null;
+        private ProgressBar progressBar = null;
+        private TextView progressLabel = null;
+
+        public CardProgress createShallowClone() {
+            CardProgress progressClone = new CardProgress();
+            progressClone.label = label;
+            progressClone.currProgress = currProgress;
+            progressClone.maxValue = maxValue;
+            progressClone.progressType = progressType;
+            return progressClone;
+        }
+
+        /**
+         * Set the progress. Only useful for the type {@link #PROGRESS_TYPE_NORMAL}.
+         * @param progress
+         * @see android.widget.ProgressBar#setProgress(int)
+         */
+        public void setProgress(int progress) {
+            currProgress = progress;
+            final ProgressBar bar = getProgressBar();
+            if (bar != null) {
+                bar.setProgress(currProgress);
+                bar.invalidate();
+            }
+        }
+
+        /**
+         * Set the range of the progress to 0...max.
+         * Only useful for the type {@link #PROGRESS_TYPE_NORMAL}.
+         * @param max
+         * @see android.widget.ProgressBar#setMax(int)
+         */
+        public void setMax(int max) {
+            maxValue = max;
+            final ProgressBar bar = getProgressBar();
+            if (bar != null) {
+                bar.setMax(maxValue);
+            }
+        }
+
+        /**
+         * Set the label text that appears near the progress indicator.
+         * @param text
+         */
+        public void setProgressLabel(String text) {
+            label = text;
+            final TextView labelView = getProgressLabel();
+            if (labelView != null) {
+                labelView.setText(text);
+            }
+        }
+
+        /**
+         * Set how progress is displayed. The parameter must be one of three supported types:
+         * <ul><li>{@link Card#PROGRESS_TYPE_NORMAL: Standard progress bar with label text</li>
+         * <li>{@link Card#PROGRESS_TYPE_INDETERMINATE}: 
+         * Indeterminate progress bar with label txt</li>
+         * <li>{@link Card#PROGRESS_TYPE_LABEL}: Label only, no progresss bar</li>
+         * @param type
+         */
+        public void setProgressType(int type) {
+            progressType = type;
+            if (progressView != null) {
+                switch (type) {
+                    case PROGRESS_TYPE_NO_PROGRESS: {
+                        progressView.setVisibility(View.GONE);
+                        break;
+                    }
+                    case PROGRESS_TYPE_NORMAL: {
+                        progressView.setVisibility(View.VISIBLE);
+                        getProgressBar().setIndeterminate(false);
+                        break;
+                    }
+                    case PROGRESS_TYPE_INDETERMINATE: {
+                        progressView.setVisibility(View.VISIBLE);
+                        getProgressBar().setIndeterminate(true);
+                        break;
+                    }
+                }
+            }
+        }
+
+        private TextView getProgressLabel() {
+            if (progressLabel != null) {
+                return progressLabel;
+            } else if (progressView != null) {
+                progressLabel = (TextView) progressView.findViewById(R.id.card_progress_text);
+                return progressLabel;
+            } else {
+                return null;
+            }
+        }
+
+        private ProgressBar getProgressBar() {
+            if (progressBar != null) {
+                return progressBar;
+            } else if (progressView != null) {
+                progressBar = (ProgressBar) progressView.findViewById(R.id.card_progress);
+                return progressBar;
+            } else {
+                return null;
+            }
+        }
+
+    }
+}
+
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardActionButton.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardActionButton.java
new file mode 100644
index 0000000..01c7094
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardActionButton.java
@@ -0,0 +1,69 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
+
+/**
+ * Custom Button with a special 'pressed' effect for touch events.
+ */
+public class CardActionButton extends Button {
+
+    public CardActionButton(Context context) {
+        super(context);
+    }
+
+    public CardActionButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CardActionButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+
+        switch(event.getAction()){
+            case MotionEvent.ACTION_DOWN:
+                setPressed(true);
+                animate().scaleX(0.98f).scaleY(0.98f).alpha(0.8f).setDuration(100).
+                        setInterpolator(new DecelerateInterpolator());
+                break;
+            case MotionEvent.ACTION_UP:
+                animate().scaleX(1.0f).scaleY(1.f).alpha(1.0f).setDuration(50).
+                        setInterpolator(new BounceInterpolator());
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                animate().scaleX(1.0f).scaleY(1.f).alpha(1.0f).setDuration(50).
+                        setInterpolator(new BounceInterpolator());
+                break;
+        }
+
+        return super.onTouchEvent(event);
+    }
+
+}
+
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardLayout.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardLayout.java
new file mode 100644
index 0000000..ce20f7f
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardLayout.java
@@ -0,0 +1,94 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.RelativeLayout;
+
+/**
+ * Custom Button with a special 'pressed' effect for touch events.
+ */
+public class CardLayout extends RelativeLayout {
+
+    private boolean mSwiping = false;
+    private float mDownX = 0.f;
+    private float mDownY = 0.f;
+    private float mTouchSlop = 0.f;
+
+    public CardLayout(Context context) {
+        super(context);
+        init();
+    }
+
+    public CardLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public CardLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    private void init(){
+        setFocusable(true);
+        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+        setWillNotDraw(false);
+        setClickable(true);
+
+        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop() * 2.f;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch(event.getAction()){
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mSwiping = false;
+                break;
+        }
+        return super.onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+
+        switch(event.getAction()){
+            case MotionEvent.ACTION_MOVE:
+                if( !mSwiping ){
+                    mSwiping = Math.abs(mDownX - event.getX()) > mTouchSlop;
+                }
+                break;
+            case MotionEvent.ACTION_DOWN:
+                mDownX = event.getX();
+                mDownY = event.getY();
+                mSwiping = false;
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mSwiping = false;
+                break;
+        }
+        return mSwiping;
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStream.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStream.java
new file mode 100644
index 0000000..70cc5dd
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStream.java
@@ -0,0 +1,25 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+public interface CardStream {
+    public CardStreamFragment getCardStream();
+}
+
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamAnimator.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamAnimator.java
new file mode 100644
index 0000000..de06cf6
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamAnimator.java
@@ -0,0 +1,120 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.view.View;
+
+/**
+ * An abstract class which defines animators for CardStreamLinearLayout.
+ */
+abstract class CardStreamAnimator {
+
+    protected float mSpeedFactor = 1.f;
+
+    /**
+     * Set speed factor of animations. Higher value means longer duration & slow animation.
+     *
+     * @param speedFactor speed type 1: SLOW, 2: NORMAL, 3:FAST
+     */
+    public void setSpeedFactor(float speedFactor) {
+        mSpeedFactor = speedFactor;
+    }
+
+    /**
+     * Define initial animation of each child which fired when a user rotate a screen.
+     *
+     * @param context
+     * @return ObjectAnimator for initial animation
+     */
+    public abstract ObjectAnimator getInitalAnimator(Context context);
+
+    /**
+     * Define disappearing animation of a child which fired when a view is removed programmatically
+     *
+     * @param context
+     * @return ObjectAnimator for disappearing animation
+     */
+    public abstract ObjectAnimator getDisappearingAnimator(Context context);
+
+    /**
+     * Define appearing animation of a child which fired when a view is added programmatically
+     *
+     * @param context
+     * @return ObjectAnimator for appearing animation
+     */
+    public abstract ObjectAnimator getAppearingAnimator(Context context);
+
+    /**
+     * Define swipe-in (back to the origin position) animation of a child
+     * which fired when a view is not moved enough to be removed.
+     *
+     * @param view   target view
+     * @param deltaX delta distance by x-axis
+     * @param deltaY delta distance by y-axis
+     * @return ObjectAnimator for swipe-in animation
+     */
+    public abstract ObjectAnimator getSwipeInAnimator(View view, float deltaX, float deltaY);
+
+    /**
+     * Define swipe-out animation of a child
+     * which fired when a view is removing by a user swipe action.
+     *
+     * @param view   target view
+     * @param deltaX delta distance by x-axis
+     * @param deltaY delta distance by y-axis
+     * @return ObjectAnimator for swipe-out animation
+     */
+    public abstract ObjectAnimator getSwipeOutAnimator(View view, float deltaX, float deltaY);
+
+    /**
+     * A simple CardStreamAnimator implementation which is used to turn animations off.
+     */
+    public static class EmptyAnimator extends CardStreamAnimator {
+
+        @Override
+        public ObjectAnimator getInitalAnimator(Context context) {
+            return null;
+        }
+
+        @Override
+        public ObjectAnimator getDisappearingAnimator(Context context) {
+            return null;
+        }
+
+        @Override
+        public ObjectAnimator getAppearingAnimator(Context context) {
+            return null;
+        }
+
+        @Override
+        public ObjectAnimator getSwipeInAnimator(View view, float deltaX, float deltaY) {
+            return null;
+        }
+
+        @Override
+        public ObjectAnimator getSwipeOutAnimator(View view, float deltaX, float deltaY) {
+            return null;
+        }
+    }
+
+}
+
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamFragment.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamFragment.java
new file mode 100644
index 0000000..5388cd9
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamFragment.java
@@ -0,0 +1,276 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+
+import com.example.android.batchstepsensor.R;
+
+/**
+ * A Fragment that handles a stream of cards.
+ * Cards can be shown or hidden. When a card is shown it can also be marked as not-dismissible, see
+ * {@link CardStreamLinearLayout#addCard(android.view.View, boolean)}.
+ */
+public class CardStreamFragment extends Fragment {
+
+    private static final int INITIAL_SIZE = 15;
+    private CardStreamLinearLayout mLayout = null;
+    private LinkedHashMap<String, Card> mVisibleCards = new LinkedHashMap<String, Card>(INITIAL_SIZE);
+    private HashMap<String, Card> mHiddenCards = new HashMap<String, Card>(INITIAL_SIZE);
+    private HashSet<String> mDismissibleCards = new HashSet<String>(INITIAL_SIZE);
+
+    // Set the listener to handle dismissed cards by moving them to the hidden cards map.
+    private CardStreamLinearLayout.OnDissmissListener mCardDismissListener =
+            new CardStreamLinearLayout.OnDissmissListener() {
+                @Override
+                public void onDismiss(String tag) {
+                    dismissCard(tag);
+                }
+            };
+
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+
+        View view = inflater.inflate(R.layout.cardstream, container, false);
+        mLayout = (CardStreamLinearLayout) view.findViewById(R.id.card_stream);
+        mLayout.setOnDismissListener(mCardDismissListener);
+
+        return view;
+    }
+
+    /**
+     * Add a visible, dismissible card to the card stream.
+     *
+     * @param card
+     */
+    public void addCard(Card card) {
+        final String tag = card.getTag();
+
+        if (!mVisibleCards.containsKey(tag) && !mHiddenCards.containsKey(tag)) {
+            final View view = card.getView();
+            view.setTag(tag);
+            mHiddenCards.put(tag, card);
+        }
+    }
+
+    /**
+     * Add and show a card.
+     *
+     * @param card
+     * @param show
+     */
+    public void addCard(Card card, boolean show) {
+        addCard(card);
+        if (show) {
+            showCard(card.getTag());
+        }
+    }
+
+    /**
+     * Remove a card and return true if it has been successfully removed.
+     *
+     * @param tag
+     * @return
+     */
+    public boolean removeCard(String tag) {
+        // Attempt to remove a visible card first
+        Card card = mVisibleCards.get(tag);
+        if (card != null) {
+            // Card is visible, also remove from layout
+            mVisibleCards.remove(tag);
+            mLayout.removeView(card.getView());
+            return true;
+        } else {
+            // Card is hidden, no need to remove from layout
+            card = mHiddenCards.remove(tag);
+            return card != null;
+        }
+    }
+
+    /**
+     * Show a dismissible card, returns false if the card could not be shown.
+     *
+     * @param tag
+     * @return
+     */
+    public boolean showCard(String tag) {
+        return showCard(tag, true);
+    }
+
+    /**
+     * Show a card, returns false if the card could not be shown.
+     *
+     * @param tag
+     * @param dismissible
+     * @return
+     */
+    public boolean showCard(String tag, boolean dismissible) {
+        final Card card = mHiddenCards.get(tag);
+        // ensure the card is hidden and not already visible
+        if (card != null && !mVisibleCards.containsValue(tag)) {
+            mHiddenCards.remove(tag);
+            mVisibleCards.put(tag, card);
+            mLayout.addCard(card.getView(), dismissible);
+            if (dismissible) {
+                mDismissibleCards.add(tag);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Hides the card, returns false if the card could not be hidden.
+     *
+     * @param tag
+     * @return
+     */
+    public boolean hideCard(String tag) {
+        final Card card = mVisibleCards.get(tag);
+        if (card != null) {
+            mVisibleCards.remove(tag);
+            mDismissibleCards.remove(tag);
+            mHiddenCards.put(tag, card);
+
+            mLayout.removeView(card.getView());
+            return true;
+        }
+        return mHiddenCards.containsValue(tag);
+    }
+
+
+    private void dismissCard(String tag) {
+        final Card card = mVisibleCards.get(tag);
+        if (card != null) {
+            mDismissibleCards.remove(tag);
+            mVisibleCards.remove(tag);
+            mHiddenCards.put(tag, card);
+        }
+    }
+
+
+    public boolean isCardVisible(String tag) {
+        return mVisibleCards.containsValue(tag);
+    }
+
+    /**
+     * Returns true if the card is shown and is dismissible.
+     *
+     * @param tag
+     * @return
+     */
+    public boolean isCardDismissible(String tag) {
+        return mDismissibleCards.contains(tag);
+    }
+
+    /**
+     * Returns the Card for this tag.
+     *
+     * @param tag
+     * @return
+     */
+    public Card getCard(String tag) {
+        final Card card = mVisibleCards.get(tag);
+        if (card != null) {
+            return card;
+        } else {
+            return mHiddenCards.get(tag);
+        }
+    }
+
+    /**
+     * Moves the view port to show the card with this tag.
+     *
+     * @param tag
+     * @see CardStreamLinearLayout#setFirstVisibleCard(String)
+     */
+    public void setFirstVisibleCard(String tag) {
+        final Card card = mVisibleCards.get(tag);
+        if (card != null) {
+            mLayout.setFirstVisibleCard(tag);
+        }
+    }
+
+    public int getVisibleCardCount() {
+        return mVisibleCards.size();
+    }
+
+    public Collection<Card> getVisibleCards() {
+        return mVisibleCards.values();
+    }
+
+    public void restoreState(CardStreamState state, OnCardClickListener callback) {
+        // restore hidden cards
+        for (Card c : state.hiddenCards) {
+            Card card = new Card.Builder(callback,c).build(getActivity());
+            mHiddenCards.put(card.getTag(), card);
+        }
+
+        // temporarily set up list of dismissible
+        final HashSet<String> dismissibleCards = state.dismissibleCards;
+
+        //restore shown cards
+        for (Card c : state.visibleCards) {
+            Card card = new Card.Builder(callback,c).build(getActivity());
+            addCard(card);
+            final String tag = card.getTag();
+            showCard(tag, dismissibleCards.contains(tag));
+        }
+
+        // move to first visible card
+        final String firstShown = state.shownTag;
+        if (firstShown != null) {
+            mLayout.setFirstVisibleCard(firstShown);
+        }
+
+        mLayout.triggerShowInitialAnimation();
+    }
+
+    public CardStreamState dumpState() {
+        final Card[] visible = cloneCards(mVisibleCards.values());
+        final Card[] hidden = cloneCards(mHiddenCards.values());
+        final HashSet<String> dismissible = new HashSet<String>(mDismissibleCards);
+        final String firstVisible = mLayout.getFirstVisibleCardTag();
+
+        return new CardStreamState(visible, hidden, dismissible, firstVisible);
+    }
+
+    private Card[] cloneCards(Collection<Card> cards) {
+        Card[] cardArray = new Card[cards.size()];
+        int i = 0;
+        for (Card c : cards) {
+            cardArray[i++] = c.createShallowClone();
+        }
+
+        return cardArray;
+    }
+
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamLinearLayout.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamLinearLayout.java
new file mode 100644
index 0000000..5e4ba15
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamLinearLayout.java
@@ -0,0 +1,570 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.animation.Animator;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+
+import com.example.android.common.logger.Log;
+import com.example.android.batchstepsensor.R;
+
+import java.util.ArrayList;
+
+/**
+ * A Layout that contains a stream of card views.
+ */
+public class CardStreamLinearLayout extends LinearLayout {
+
+    public static final int ANIMATION_SPEED_SLOW = 1001;
+    public static final int ANIMATION_SPEED_NORMAL = 1002;
+    public static final int ANIMATION_SPEED_FAST = 1003;
+
+    private static final String TAG = "CardStreamLinearLayout";
+    private final ArrayList<View> mFixedViewList = new ArrayList<View>();
+    private final Rect mChildRect = new Rect();
+    private CardStreamAnimator mAnimators;
+    private OnDissmissListener mDismissListener = null;
+    private boolean mLayouted = false;
+    private boolean mSwiping = false;
+    private String mFirstVisibleCardTag = null;
+    private boolean mShowInitialAnimation = false;
+
+    /**
+     * Handle touch events to fade/move dragged items as they are swiped out
+     */
+    private OnTouchListener mTouchListener = new OnTouchListener() {
+
+        private float mDownX;
+        private float mDownY;
+
+        @Override
+        public boolean onTouch(final View v, MotionEvent event) {
+
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    mDownX = event.getX();
+                    mDownY = event.getY();
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                    resetAnimatedView(v);
+                    mSwiping = false;
+                    mDownX = 0.f;
+                    mDownY = 0.f;
+                    break;
+                case MotionEvent.ACTION_MOVE: {
+
+                    float x = event.getX() + v.getTranslationX();
+                    float y = event.getY() + v.getTranslationY();
+
+                    mDownX = mDownX == 0.f ? x : mDownX;
+                    mDownY = mDownY == 0.f ? x : mDownY;
+
+                    float deltaX = x - mDownX;
+                    float deltaY = y - mDownY;
+
+                    if (!mSwiping && isSwiping(deltaX, deltaY)) {
+                        mSwiping = true;
+                        v.getParent().requestDisallowInterceptTouchEvent(true);
+                    } else {
+                        swipeView(v, deltaX, deltaY);
+                    }
+                }
+                break;
+                case MotionEvent.ACTION_UP: {
+                    // User let go - figure out whether to animate the view out, or back into place
+                    if (mSwiping) {
+                        float x = event.getX() + v.getTranslationX();
+                        float y = event.getY() + v.getTranslationY();
+
+                        float deltaX = x - mDownX;
+                        float deltaY = y - mDownX;
+                        float deltaXAbs = Math.abs(deltaX);
+
+                        // User let go - figure out whether to animate the view out, or back into place
+                        boolean remove = deltaXAbs > v.getWidth() / 4 && !isFixedView(v);
+                        if( remove )
+                            handleViewSwipingOut(v, deltaX, deltaY);
+                        else
+                            handleViewSwipingIn(v, deltaX, deltaY);
+                    }
+                    mDownX = 0.f;
+                    mDownY = 0.f;
+                    mSwiping = false;
+                }
+                break;
+                default:
+                    return false;
+            }
+            return false;
+        }
+    };
+    private int mSwipeSlop = -1;
+    /**
+     * Handle end-transition animation event of each child and launch a following animation.
+     */
+    private LayoutTransition.TransitionListener mTransitionListener
+            = new LayoutTransition.TransitionListener() {
+
+        @Override
+        public void startTransition(LayoutTransition transition, ViewGroup container, View
+                view, int transitionType) {
+            Log.d(TAG, "Start LayoutTransition animation:" + transitionType);
+        }
+
+        @Override
+        public void endTransition(LayoutTransition transition, ViewGroup container,
+                                  final View view, int transitionType) {
+
+            Log.d(TAG, "End LayoutTransition animation:" + transitionType);
+            if (transitionType == LayoutTransition.APPEARING) {
+                final View area = view.findViewById(R.id.card_actionarea);
+                if (area != null) {
+                    runShowActionAreaAnimation(container, area);
+                }
+            }
+        }
+    };
+    /**
+     * Handle a hierarchy change event
+     * when a new child is added, scroll to bottom and hide action area..
+     */
+    private OnHierarchyChangeListener mOnHierarchyChangeListener
+            = new OnHierarchyChangeListener() {
+        @Override
+        public void onChildViewAdded(final View parent, final View child) {
+
+            Log.d(TAG, "child is added: " + child);
+
+            ViewParent scrollView = parent.getParent();
+            if (scrollView != null && scrollView instanceof ScrollView) {
+                ((ScrollView) scrollView).fullScroll(FOCUS_DOWN);
+            }
+
+            if (getLayoutTransition() != null) {
+                View view = child.findViewById(R.id.card_actionarea);
+                if (view != null)
+                    view.setAlpha(0.f);
+            }
+        }
+
+        @Override
+        public void onChildViewRemoved(View parent, View child) {
+            Log.d(TAG, "child is removed: " + child);
+            mFixedViewList.remove(child);
+        }
+    };
+    private int mLastDownX;
+
+    public CardStreamLinearLayout(Context context) {
+        super(context);
+        initialize(null, 0);
+    }
+
+    public CardStreamLinearLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize(attrs, 0);
+    }
+
+    @SuppressLint("NewApi")
+    public CardStreamLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initialize(attrs, defStyle);
+    }
+
+    /**
+     * add a card view w/ canDismiss flag.
+     *
+     * @param cardView   a card view
+     * @param canDismiss flag to indicate this card is dismissible or not.
+     */
+    public void addCard(View cardView, boolean canDismiss) {
+        if (cardView.getParent() == null) {
+            initCard(cardView, canDismiss);
+
+            ViewGroup.LayoutParams param = cardView.getLayoutParams();
+            if(param == null)
+                param = generateDefaultLayoutParams();
+
+            super.addView(cardView, -1, param);
+        }
+    }
+
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        if (child.getParent() == null) {
+            initCard(child, true);
+            super.addView(child, index, params);
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        Log.d(TAG, "onLayout: " + changed);
+
+        if( changed && !mLayouted ){
+            mLayouted = true;
+
+            ObjectAnimator animator;
+            LayoutTransition layoutTransition = new LayoutTransition();
+
+            animator = mAnimators.getDisappearingAnimator(getContext());
+            layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, animator);
+
+            animator = mAnimators.getAppearingAnimator(getContext());
+            layoutTransition.setAnimator(LayoutTransition.APPEARING, animator);
+
+            layoutTransition.addTransitionListener(mTransitionListener);
+
+            if( animator != null )
+                layoutTransition.setDuration(animator.getDuration());
+
+            setLayoutTransition(layoutTransition);
+
+            if( mShowInitialAnimation )
+                runInitialAnimations();
+
+            if (mFirstVisibleCardTag != null) {
+                scrollToCard(mFirstVisibleCardTag);
+                mFirstVisibleCardTag = null;
+            }
+        }
+    }
+
+    /**
+     * Check whether a user moved enough distance to start a swipe action or not.
+     *
+     * @param deltaX
+     * @param deltaY
+     * @return true if a user is swiping.
+     */
+    protected boolean isSwiping(float deltaX, float deltaY) {
+
+        if (mSwipeSlop < 0) {
+            //get swipping slop from ViewConfiguration;
+            mSwipeSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+        }
+
+        boolean swipping = false;
+        float absDeltaX = Math.abs(deltaX);
+
+        if( absDeltaX > mSwipeSlop )
+            return true;
+
+        return swipping;
+    }
+
+    /**
+     * Swipe a view by moving distance
+     *
+     * @param child a target view
+     * @param deltaX x moving distance by x-axis.
+     * @param deltaY y moving distance by y-axis.
+     */
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    protected void swipeView(View child, float deltaX, float deltaY) {
+        if (isFixedView(child)){
+            deltaX = deltaX / 4;
+        }
+
+        float deltaXAbs = Math.abs(deltaX);
+        float fractionCovered = deltaXAbs / (float) child.getWidth();
+
+        child.setTranslationX(deltaX);
+        child.setAlpha(1.f - fractionCovered);
+
+        if (deltaX > 0)
+            child.setRotationY(-15.f * fractionCovered);
+        else
+            child.setRotationY(15.f * fractionCovered);
+    }
+
+    protected void notifyOnDismissEvent( View child ){
+        if( child == null || mDismissListener == null )
+            return;
+
+        mDismissListener.onDismiss((String) child.getTag());
+    }
+
+    /**
+     * get the tag of the first visible child in this layout
+     *
+     * @return tag of the first visible child or null
+     */
+    public String getFirstVisibleCardTag() {
+
+        final int count = getChildCount();
+
+        if (count == 0)
+            return null;
+
+        for (int index = 0; index < count; ++index) {
+            //check the position of each view.
+            View child = getChildAt(index);
+            if (child.getGlobalVisibleRect(mChildRect) == true)
+                return (String) child.getTag();
+        }
+
+        return null;
+    }
+
+    /**
+     * Set the first visible card of this linear layout.
+     *
+     * @param tag tag of a card which should already added to this layout.
+     */
+    public void setFirstVisibleCard(String tag) {
+        if (tag == null)
+            return; //do nothing.
+
+        if (mLayouted) {
+            scrollToCard(tag);
+        } else {
+            //keep the tag for next use.
+            mFirstVisibleCardTag = tag;
+        }
+    }
+
+    /**
+     * If this flag is set,
+     * after finishing initial onLayout event, an initial animation which is defined in DefaultCardStreamAnimator is launched.
+     */
+    public void triggerShowInitialAnimation(){
+        mShowInitialAnimation = true;
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    public void setCardStreamAnimator( CardStreamAnimator animators ){
+
+        if( animators == null )
+            mAnimators = new CardStreamAnimator.EmptyAnimator();
+        else
+            mAnimators = animators;
+
+        LayoutTransition layoutTransition = getLayoutTransition();
+
+        if( layoutTransition != null ){
+            layoutTransition.setAnimator( LayoutTransition.APPEARING,
+                    mAnimators.getAppearingAnimator(getContext()) );
+            layoutTransition.setAnimator( LayoutTransition.DISAPPEARING,
+                    mAnimators.getDisappearingAnimator(getContext()) );
+        }
+    }
+
+    /**
+     * set a OnDismissListener which called when user dismiss a card.
+     *
+     * @param listener
+     */
+    public void setOnDismissListener(OnDissmissListener listener) {
+        mDismissListener = listener;
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    private void initialize(AttributeSet attrs, int defStyle) {
+
+        float speedFactor = 1.f;
+
+        if (attrs != null) {
+            TypedArray a = getContext().obtainStyledAttributes(attrs,
+                    R.styleable.CardStream, defStyle, 0);
+
+            if( a != null ){
+                int speedType = a.getInt(R.styleable.CardStream_animationDuration, 1001);
+                switch (speedType){
+                    case ANIMATION_SPEED_FAST:
+                        speedFactor = 0.5f;
+                        break;
+                    case ANIMATION_SPEED_NORMAL:
+                        speedFactor = 1.f;
+                        break;
+                    case ANIMATION_SPEED_SLOW:
+                        speedFactor = 2.f;
+                        break;
+                }
+
+                String animatorName = a.getString(R.styleable.CardStream_animators);
+
+                try {
+                    if( animatorName != null )
+                        mAnimators = (CardStreamAnimator) getClass().getClassLoader()
+                                .loadClass(animatorName).newInstance();
+                } catch (Exception e) {
+                    Log.e(TAG, "Fail to load animator:" + animatorName, e);
+                } finally {
+                    if(mAnimators == null)
+                        mAnimators = new DefaultCardStreamAnimator();
+                }
+                a.recycle();
+            }
+        }
+
+        mAnimators.setSpeedFactor(speedFactor);
+        mSwipeSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+        setOnHierarchyChangeListener(mOnHierarchyChangeListener);
+    }
+
+    private void initCard(View cardView, boolean canDismiss) {
+        resetAnimatedView(cardView);
+        cardView.setOnTouchListener(mTouchListener);
+        if (!canDismiss)
+            mFixedViewList.add(cardView);
+    }
+
+    private boolean isFixedView(View v) {
+        return mFixedViewList.contains(v);
+    }
+
+    private void resetAnimatedView(View child) {
+        child.setAlpha(1.f);
+        child.setTranslationX(0.f);
+        child.setTranslationY(0.f);
+        child.setRotation(0.f);
+        child.setRotationY(0.f);
+        child.setRotationX(0.f);
+        child.setScaleX(1.f);
+        child.setScaleY(1.f);
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    private void runInitialAnimations() {
+        if( mAnimators == null )
+            return;
+
+        final int count = getChildCount();
+
+        for (int index = 0; index < count; ++index) {
+            final View child = getChildAt(index);
+            ObjectAnimator animator =  mAnimators.getInitalAnimator(getContext());
+            if( animator != null ){
+                animator.setTarget(child);
+                animator.start();
+            }
+        }
+    }
+
+    private void runShowActionAreaAnimation(View parent, View area) {
+        area.setPivotY(0.f);
+        area.setPivotX(parent.getWidth() / 2.f);
+
+        area.setAlpha(0.5f);
+        area.setRotationX(-90.f);
+        area.animate().rotationX(0.f).alpha(1.f).setDuration(400);
+    }
+
+    private void handleViewSwipingOut(final View child, float deltaX, float deltaY) {
+        ObjectAnimator animator = mAnimators.getSwipeOutAnimator(child, deltaX, deltaY);
+        if( animator != null ){
+            animator.addListener(new EndAnimationWrapper() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    removeView(child);
+                    notifyOnDismissEvent(child);
+                }
+            });
+        } else {
+            removeView(child);
+            notifyOnDismissEvent(child);
+        }
+
+        if( animator != null ){
+            animator.setTarget(child);
+            animator.start();
+        }
+    }
+
+    private void handleViewSwipingIn(final View child, float deltaX, float deltaY) {
+        ObjectAnimator animator = mAnimators.getSwipeInAnimator(child, deltaX, deltaY);
+        if( animator != null ){
+            animator.addListener(new EndAnimationWrapper() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    child.setTranslationY(0.f);
+                    child.setTranslationX(0.f);
+                }
+            });
+        } else {
+            child.setTranslationY(0.f);
+            child.setTranslationX(0.f);
+        }
+
+        if( animator != null ){
+            animator.setTarget(child);
+            animator.start();
+        }
+    }
+
+    private void scrollToCard(String tag) {
+
+
+        final int count = getChildCount();
+        for (int index = 0; index < count; ++index) {
+            View child = getChildAt(index);
+
+            if (tag.equals(child.getTag())) {
+
+                ViewParent parent = getParent();
+                if( parent != null && parent instanceof ScrollView ){
+                    ((ScrollView)parent).smoothScrollTo(
+                            0, child.getTop() - getPaddingTop() - child.getPaddingTop());
+                }
+                return;
+            }
+        }
+    }
+
+    public interface OnDissmissListener {
+        public void onDismiss(String tag);
+    }
+
+    /**
+     * Empty default AnimationListener
+     */
+    private abstract class EndAnimationWrapper implements Animator.AnimatorListener {
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+    }//end of inner class
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamState.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamState.java
new file mode 100644
index 0000000..57ba177
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/CardStreamState.java
@@ -0,0 +1,40 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import java.util.HashSet;
+
+/**
+ * A struct object that holds the state of a {@link CardStreamFragment}.
+ */
+public class CardStreamState {
+    protected Card[] visibleCards;
+    protected Card[] hiddenCards;
+    protected HashSet<String> dismissibleCards;
+    protected String shownTag;
+
+    protected CardStreamState(Card[] visible, Card[] hidden, HashSet<String> dismissible, String shownTag) {
+        visibleCards = visible;
+        hiddenCards = hidden;
+        dismissibleCards = dismissible;
+        this.shownTag = shownTag;
+    }
+
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/DefaultCardStreamAnimator.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/DefaultCardStreamAnimator.java
new file mode 100644
index 0000000..21e54d3
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/DefaultCardStreamAnimator.java
@@ -0,0 +1,124 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Build;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.BounceInterpolator;
+
+class DefaultCardStreamAnimator extends CardStreamAnimator {
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    @Override
+    public ObjectAnimator getDisappearingAnimator(Context context){
+
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(new Object(),
+                PropertyValuesHolder.ofFloat("alpha", 1.f, 0.f),
+                PropertyValuesHolder.ofFloat("scaleX", 1.f, 0.f),
+                PropertyValuesHolder.ofFloat("scaleY", 1.f, 0.f),
+                PropertyValuesHolder.ofFloat("rotation", 0.f, 270.f));
+
+        animator.setDuration((long) (200 * mSpeedFactor));
+        return animator;
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+    @Override
+    public ObjectAnimator getAppearingAnimator(Context context){
+
+        final Point outPoint = new Point();
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getSize(outPoint);
+
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(new Object(),
+                PropertyValuesHolder.ofFloat("alpha", 0.f, 1.f),
+                PropertyValuesHolder.ofFloat("translationY", outPoint.y / 2.f, 0.f),
+                PropertyValuesHolder.ofFloat("rotation", -45.f, 0.f));
+
+        animator.setDuration((long) (200 * mSpeedFactor));
+        return animator;
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+    @Override
+    public ObjectAnimator getInitalAnimator(Context context){
+
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(new Object(),
+                PropertyValuesHolder.ofFloat("alpha", 0.5f, 1.f),
+                PropertyValuesHolder.ofFloat("rotation", 60.f, 0.f));
+
+        animator.setDuration((long) (200 * mSpeedFactor));
+        return animator;
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    @Override
+    public ObjectAnimator getSwipeInAnimator(View view, float deltaX, float deltaY){
+
+        float deltaXAbs = Math.abs(deltaX);
+
+        float fractionCovered = 1.f - (deltaXAbs / view.getWidth());
+        long duration = Math.abs((int) ((1 - fractionCovered) * 200 * mSpeedFactor));
+
+        // Animate position and alpha of swiped item
+
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view,
+                PropertyValuesHolder.ofFloat("alpha", 1.f),
+                PropertyValuesHolder.ofFloat("translationX", 0.f),
+                PropertyValuesHolder.ofFloat("rotationY", 0.f));
+
+        animator.setDuration(duration).setInterpolator(new BounceInterpolator());
+
+        return  animator;
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    @Override
+    public ObjectAnimator getSwipeOutAnimator(View view, float deltaX, float deltaY){
+
+        float endX;
+        float endRotationY;
+
+        float deltaXAbs = Math.abs(deltaX);
+
+        float fractionCovered = 1.f - (deltaXAbs / view.getWidth());
+        long duration = Math.abs((int) ((1 - fractionCovered) * 200 * mSpeedFactor));
+
+        endX = deltaX < 0 ? -view.getWidth() : view.getWidth();
+        if (deltaX > 0)
+            endRotationY = -15.f;
+        else
+            endRotationY = 15.f;
+
+        // Animate position and alpha of swiped item
+        return ObjectAnimator.ofPropertyValuesHolder(view,
+                PropertyValuesHolder.ofFloat("alpha", 0.f),
+                PropertyValuesHolder.ofFloat("translationX", endX),
+                PropertyValuesHolder.ofFloat("rotationY", endRotationY)).setDuration(duration);
+
+    }
+
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/OnCardClickListener.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/OnCardClickListener.java
new file mode 100644
index 0000000..90ba21e
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/OnCardClickListener.java
@@ -0,0 +1,24 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+public interface OnCardClickListener {
+    public void onCardClick(int cardActionId, String cardTag);
+}
diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/StreamRetentionFragment.java b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/StreamRetentionFragment.java
new file mode 100644
index 0000000..51d5428
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/src/com.example.android.batchstepsensor/cardstream/StreamRetentionFragment.java
@@ -0,0 +1,41 @@
+/*
+* Copyright 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.batchstepsensor.cardstream;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+
+public class StreamRetentionFragment extends Fragment {
+
+    CardStreamState mState;
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        setRetainInstance(true);
+    }
+
+    public void storeCardStream(CardStreamState state) {
+        mState = state;
+    }
+
+    public CardStreamState getCardStream() {
+        return mState;
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/BatchStepSensor/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/BluetoothLeGatt/res/values-sw600dp/dimens.xml b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BluetoothLeGatt/res/values-sw600dp/dimens.xml
rename to samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BluetoothLeGatt/res/values-sw600dp/styles.xml b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BluetoothLeGatt/res/values-sw600dp/styles.xml
rename to samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BluetoothLeGatt/res/values-v11/template-styles.xml b/samples/browseable/BluetoothLeGatt/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BluetoothLeGatt/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BluetoothLeGatt/res/values/styles.xml b/samples/browseable/BluetoothLeGatt/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/BluetoothLeGatt/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BluetoothLeGatt/res/values/dimens.xml b/samples/browseable/BluetoothLeGatt/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BluetoothLeGatt/res/values/dimens.xml
rename to samples/browseable/BluetoothLeGatt/res/values/template-dimens.xml
diff --git a/samples/browseable/BluetoothLeGatt/res/values/template-styles.xml b/samples/browseable/BluetoothLeGatt/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BluetoothLeGatt/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/BorderlessButtons/res/values-sw600dp/dimens.xml b/samples/browseable/BorderlessButtons/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BorderlessButtons/res/values-sw600dp/dimens.xml
rename to samples/browseable/BorderlessButtons/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BorderlessButtons/res/values-sw600dp/styles.xml b/samples/browseable/BorderlessButtons/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BorderlessButtons/res/values-sw600dp/styles.xml
rename to samples/browseable/BorderlessButtons/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BorderlessButtons/res/values-v11/template-styles.xml b/samples/browseable/BorderlessButtons/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/BorderlessButtons/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/BorderlessButtons/res/values/dimens.xml b/samples/browseable/BorderlessButtons/res/values/dimens.xml
index 39e710b..71a1fc7 100644
--- a/samples/browseable/BorderlessButtons/res/values/dimens.xml
+++ b/samples/browseable/BorderlessButtons/res/values/dimens.xml
@@ -16,17 +16,10 @@
 
 <resources>
 
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+    <dimen name="standard_touch_target_size">48dp</dimen>
 
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
+    <!-- Meta-dimension that switches on screen size -->
 
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+    <dimen name="page_margin">@dimen/margin_medium</dimen>
 
 </resources>
diff --git a/samples/browseable/BorderlessButtons/res/values/styles.xml b/samples/browseable/BorderlessButtons/res/values/styles.xml
index 404623e..36e0445 100644
--- a/samples/browseable/BorderlessButtons/res/values/styles.xml
+++ b/samples/browseable/BorderlessButtons/res/values/styles.xml
@@ -16,27 +16,16 @@
 
 <resources>
 
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
     <!-- Widget styling -->
 
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
+    <style name="Widget.DescriptionBar">
+        <item name="android:background">#fb3</item>
+        <item name="android:paddingTop">@dimen/margin_medium</item>
+        <item name="android:paddingBottom">@dimen/margin_medium</item>
+        <item name="android:paddingLeft">@dimen/page_margin</item>
+        <item name="android:paddingRight">@dimen/page_margin</item>
         <item name="android:textAppearance">?android:textAppearanceMedium</item>
         <item name="android:lineSpacingMultiplier">1.1</item>
     </style>
 
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
 </resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/BorderlessButtons/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/BorderlessButtons/res/values/template-dimens.xml
diff --git a/samples/browseable/BorderlessButtons/res/values/template-styles.xml b/samples/browseable/BorderlessButtons/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/BorderlessButtons/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/CardEmulation/AndroidManifest.xml b/samples/browseable/CardEmulation/AndroidManifest.xml
new file mode 100644
index 0000000..4a4af08
--- /dev/null
+++ b/samples/browseable/CardEmulation/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.cardemulation"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <!-- Card emulation was introduced in API 19. -->
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+    <uses-feature android:name="android.hardware.nfc.hce" android:required="true" />
+    <uses-permission android:name="android.permission.NFC" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <!-- Basic UI for sample discoverability. -->
+        <activity android:name=".MainActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <!-- BEGIN_INCLUDE(CardEmulationManifest) -->
+        <!-- Service for handling communication with NFC terminal. -->
+        <service android:name=".CardService"
+                 android:exported="true"
+                 android:permission="android.permission.BIND_NFC_SERVICE">
+            <!-- Intent filter indicating that we support card emulation. -->
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <!-- Required XML configuration file, listing the AIDs that we are emulating cards
+                 for. This defines what protocols our card emulation service supports. -->
+            <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
+                       android:resource="@xml/aid_list"/>
+        </service>
+        <!-- END_INCLUDE(CardEmulationManifest) -->
+    </application>
+
+
+</manifest>
diff --git a/samples/browseable/CardEmulation/_index.jd b/samples/browseable/CardEmulation/_index.jd
new file mode 100644
index 0000000..1f3e3c7
--- /dev/null
+++ b/samples/browseable/CardEmulation/_index.jd
@@ -0,0 +1,19 @@
+
+
+
+page.tags="CardEmulation"
+sample.group=Connectivity
+@jd:body
+
+<p>
+  This sample demonstrates how to emulate an NFC card, using the <a href=
+  "{@docRoot}guide/topics/connectivity/nfc/hce.html">Host Card Emulation</a>
+  feature added in Android 4.4. This sample makes the device appear as a
+  loyalty card whenever the screen is on and the user taps their device on an
+  appropriately configured NFC reader.
+</p>
+
+<p>
+  The <a href="{@docRoot}samples/CardReader/index.html">CardReader</a> sample
+  can be used to read the loyalty card implemented in this sample.
+</p>
diff --git a/samples/browseable/CardEmulation/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0be8f85
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/CardEmulation/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/CardEmulation/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/CardEmulation/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..eb13405
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CardEmulation/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..8d1e6a6
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CardEmulation/res/drawable-xxhdpi/card_background.png b/samples/browseable/CardEmulation/res/drawable-xxhdpi/card_background.png
new file mode 100644
index 0000000..86a7ba7
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/drawable-xxhdpi/card_background.png
Binary files differ
diff --git a/samples/browseable/CardEmulation/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..7ea109f
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CardEmulation/res/layout-w720dp/activity_main.xml b/samples/browseable/CardEmulation/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/CardEmulation/res/layout/activity_main.xml b/samples/browseable/CardEmulation/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/CardEmulation/res/layout/main_fragment.xml b/samples/browseable/CardEmulation/res/layout/main_fragment.xml
new file mode 100644
index 0000000..2c69743
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/layout/main_fragment.xml
@@ -0,0 +1,65 @@
+<!--
+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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="380dp"
+    android:layout_height="242.25dp"
+    android:layout_gravity="center"
+    android:layout_margin="20dp">
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@drawable/card_background"
+        />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="20dp"
+        android:layout_gravity="center"
+        android:clickable="true">
+        <TextView
+            android:id="@+id/card_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/card_title"
+            android:fontFamily="sans-serif-condensed"
+            android:textStyle="bold"
+            android:textSize="32dp"
+            />
+        <TextView
+            android:id="@+id/card_account_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/account_number"
+            android:fontFamily="sans-serif"
+            android:textStyle="bold"
+            android:textSize="18dp"
+            android:layout_marginTop="40dp"
+            />
+        <EditText
+            android:id="@+id/card_account_field"
+            android:width="360dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif-condensed"
+            android:textStyle="bold"
+            android:textSize="42dp"
+            android:singleLine="true"
+            android:inputType="number" />
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/browseable/CardEmulation/res/menu/main.xml b/samples/browseable/CardEmulation/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml b/samples/browseable/CardEmulation/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml
copy to samples/browseable/CardEmulation/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml b/samples/browseable/CardEmulation/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml
copy to samples/browseable/CardEmulation/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/CardEmulation/res/values-v11/template-styles.xml b/samples/browseable/CardEmulation/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/CardEmulation/res/values/base-strings.xml b/samples/browseable/CardEmulation/res/values/base-strings.xml
new file mode 100644
index 0000000..9062a23
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/values/base-strings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">CardEmulation</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample demonstrates how to emulate an NFC card, using the "host card emulation"
+            feature added in Android 4.4. This sample makes the device appear as a loyalty card
+            whenever the screen is on and the user taps their device on an appropriately configured
+            NFC reader.
+
+            The "CardReader" sample can be used to read the loyalty card implemented in this sample.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/CardEmulation/res/values/strings.xml b/samples/browseable/CardEmulation/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/CardEmulation/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/CardEmulation/res/values/template-dimens.xml
diff --git a/samples/browseable/CardEmulation/res/values/template-styles.xml b/samples/browseable/CardEmulation/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/CardEmulation/res/xml/aid_list.xml b/samples/browseable/CardEmulation/res/xml/aid_list.xml
new file mode 100644
index 0000000..15ed754
--- /dev/null
+++ b/samples/browseable/CardEmulation/res/xml/aid_list.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+
+<!-- This file defines which AIDs this application should emulate cards for.
+
+     Vendor-specific AIDs should always start with an "F", according to the ISO 7816 spec. We
+     recommended vendor-specific AIDs be at least 6 characters long, to provide sufficient
+     uniqueness. Note, however, that longer AIDs may impose a burden on non-Android NFC terminals.
+     AIDs may not exceed 32 characters (16 bytes).
+
+     Additionally, AIDs must always contain an even number of characters, in hexadecimal format.
+
+     In order to avoid prompting the user to select which service they want to use when the device
+     is scanned, this app must be selected as the default handler for an AID group by the user, or
+     the terminal must select *all* AIDs defined in the category simultaneously ("exact match").
+-->
+<!-- BEGIN_INCLUDE(CardEmulationXML) -->
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/service_name"
+    android:requireDeviceUnlock="false">
+    <!--
+    If category="payment" is used for any aid-groups, you must also add an android:apduServiceBanner
+    attribute above, like so:
+
+    android:apduServiceBanner="@drawable/settings_banner"
+
+     apduServiceBanner should be 260x96 dp. In pixels, that works out to...
+       - drawable-xxhdpi: 780x288 px
+       - drawable-xhdpi:  520x192 px
+       - drawable-hdpi:   390x144 px
+       - drawable-mdpi:   260x96  px
+
+    The apduServiceBanner is displayed in the "Tap & Pay" menu in the system Settings app, and
+    is only displayed for apps which implement the "payment" AID category.
+
+    Since this sample is implementing a non-standard card type (a loyalty card, specifically), we
+    do not need to define a banner.
+
+    Important: category="payment" should only be used for industry-standard payment cards. If you are
+        implementing a closed-loop payment system (e.g. stored value cards for a specific merchant
+        or transit system), use category="other". This is because only one "payment" card may be
+        active at a time, whereas all "other" cards are active simultaneously (subject to AID
+        dispatch).
+    -->
+
+    <aid-group android:description="@string/card_title" android:category="other">
+        <aid-filter android:name="F222222222"/>
+    </aid-group>
+<!-- END_INCLUDE(CardEmulationXML) -->
+</host-apdu-service>
diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/AccountStorage.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/AccountStorage.java
new file mode 100644
index 0000000..e02d480
--- /dev/null
+++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/AccountStorage.java
@@ -0,0 +1,58 @@
+/*
+ * 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.cardemulation;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+/**
+ * Utility class for persisting account numbers to disk.
+ *
+ * <p>The default SharedPreferences instance is used as the backing storage. Values are cached
+ * in memory for performance.
+ *
+ * <p>This class is thread-safe.
+ */
+public class AccountStorage {
+    private static final String PREF_ACCOUNT_NUMBER = "account_number";
+    private static final String DEFAULT_ACCOUNT_NUMBER = "00000000";
+    private static final String TAG = "AccountStorage";
+    private static String sAccount = null;
+    private static final Object sAccountLock = new Object();
+
+    public static void SetAccount(Context c, String s) {
+        synchronized(sAccountLock) {
+            Log.i(TAG, "Setting account number: " + s);
+            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
+            prefs.edit().putString(PREF_ACCOUNT_NUMBER, s).commit();
+            sAccount = s;
+        }
+    }
+
+    public static String GetAccount(Context c) {
+        synchronized (sAccountLock) {
+            if (sAccount == null) {
+                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
+                String account = prefs.getString(PREF_ACCOUNT_NUMBER, DEFAULT_ACCOUNT_NUMBER);
+                sAccount = account;
+            }
+            return sAccount;
+        }
+    }
+}
diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardEmulationFragment.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardEmulationFragment.java
new file mode 100644
index 0000000..f26efda
--- /dev/null
+++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardEmulationFragment.java
@@ -0,0 +1,70 @@
+/*
+ * 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.cardemulation;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+/**
+ * Generic UI for sample discovery.
+ */
+public class CardEmulationFragment extends Fragment {
+
+    public static final String TAG = "CardEmulationFragment";
+
+    /** Called when sample is created. Displays generic UI with welcome text. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        View v = inflater.inflate(R.layout.main_fragment, container, false);
+        EditText account = (EditText) v.findViewById(R.id.card_account_field);
+        account.setText(AccountStorage.GetAccount(getActivity()));
+        account.addTextChangedListener(new AccountUpdater());
+        return v;
+    }
+
+
+    private class AccountUpdater implements TextWatcher {
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            // Not implemented.
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            // Not implemented.
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            String account = s.toString();
+            AccountStorage.SetAccount(getActivity(), account);
+        }
+    }
+}
diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardService.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardService.java
new file mode 100644
index 0000000..a409d28
--- /dev/null
+++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardService.java
@@ -0,0 +1,173 @@
+/*
+ * 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.cardemulation;
+
+import android.nfc.cardemulation.HostApduService;
+import android.os.Bundle;
+import com.example.android.common.logger.Log;
+
+import java.util.Arrays;
+
+/**
+ * This is a sample APDU Service which demonstrates how to interface with the card emulation support
+ * added in Android 4.4, KitKat.
+ *
+ * <p>This sample replies to any requests sent with the string "Hello World". In real-world
+ * situations, you would need to modify this code to implement your desired communication
+ * protocol.
+ *
+ * <p>This sample will be invoked for any terminals selecting AIDs of 0xF11111111, 0xF22222222, or
+ * 0xF33333333. See src/main/res/xml/aid_list.xml for more details.
+ *
+ * <p class="note">Note: This is a low-level interface. Unlike the NdefMessage many developers
+ * are familiar with for implementing Android Beam in apps, card emulation only provides a
+ * byte-array based communication channel. It is left to developers to implement higher level
+ * protocol support as needed.
+ */
+public class CardService extends HostApduService {
+    private static final String TAG = "CardService";
+    // AID for our loyalty card service.
+    private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
+    // ISO-DEP command HEADER for selecting an AID.
+    // Format: [Class | Instruction | Parameter 1 | Parameter 2]
+    private static final String SELECT_APDU_HEADER = "00A40400";
+    // "OK" status word sent in response to SELECT AID command (0x9000)
+    private static final byte[] SELECT_OK_SW = HexStringToByteArray("9000");
+    // "UNKNOWN" status word sent in response to invalid APDU command (0x0000)
+    private static final byte[] UNKNOWN_CMD_SW = HexStringToByteArray("0000");
+    private static final byte[] SELECT_APDU = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
+
+    /**
+     * Called if the connection to the NFC card is lost, in order to let the application know the
+     * cause for the disconnection (either a lost link, or another AID being selected by the
+     * reader).
+     *
+     * @param reason Either DEACTIVATION_LINK_LOSS or DEACTIVATION_DESELECTED
+     */
+    @Override
+    public void onDeactivated(int reason) { }
+
+    /**
+     * This method will be called when a command APDU has been received from a remote device. A
+     * response APDU can be provided directly by returning a byte-array in this method. In general
+     * response APDUs must be sent as quickly as possible, given the fact that the user is likely
+     * holding his device over an NFC reader when this method is called.
+     *
+     * <p class="note">If there are multiple services that have registered for the same AIDs in
+     * their meta-data entry, you will only get called if the user has explicitly selected your
+     * service, either as a default or just for the next tap.
+     *
+     * <p class="note">This method is running on the main thread of your application. If you
+     * cannot return a response APDU immediately, return null and use the {@link
+     * #sendResponseApdu(byte[])} method later.
+     *
+     * @param commandApdu The APDU that received from the remote device
+     * @param extras A bundle containing extra data. May be null.
+     * @return a byte-array containing the response APDU, or null if no response APDU can be sent
+     * at this point.
+     */
+    // BEGIN_INCLUDE(processCommandApdu)
+    @Override
+    public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
+        Log.i(TAG, "Received APDU: " + ByteArrayToHexString(commandApdu));
+        // If the APDU matches the SELECT AID command for this service,
+        // send the loyalty card account number, followed by a SELECT_OK status trailer (0x9000).
+        if (Arrays.equals(SELECT_APDU, commandApdu)) {
+            String account = AccountStorage.GetAccount(this);
+            byte[] accountBytes = account.getBytes();
+            Log.i(TAG, "Sending account number: " + account);
+            return ConcatArrays(accountBytes, SELECT_OK_SW);
+        } else {
+            return UNKNOWN_CMD_SW;
+        }
+    }
+    // END_INCLUDE(processCommandApdu)
+
+    /**
+     * Build APDU for SELECT AID command. This command indicates which service a reader is
+     * interested in communicating with. See ISO 7816-4.
+     *
+     * @param aid Application ID (AID) to select
+     * @return APDU for SELECT AID command
+     */
+    public static byte[] BuildSelectApdu(String aid) {
+        // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
+        return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X",
+                aid.length() / 2) + aid);
+    }
+
+    /**
+     * Utility method to convert a byte array to a hexadecimal string.
+     *
+     * @param bytes Bytes to convert
+     * @return String, containing hexadecimal representation.
+     */
+    public static String ByteArrayToHexString(byte[] bytes) {
+        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+        char[] hexChars = new char[bytes.length * 2]; // Each byte has two hex characters (nibbles)
+        int v;
+        for (int j = 0; j < bytes.length; j++) {
+            v = bytes[j] & 0xFF; // Cast bytes[j] to int, treating as unsigned value
+            hexChars[j * 2] = hexArray[v >>> 4]; // Select hex character from upper nibble
+            hexChars[j * 2 + 1] = hexArray[v & 0x0F]; // Select hex character from lower nibble
+        }
+        return new String(hexChars);
+    }
+
+    /**
+     * Utility method to convert a hexadecimal string to a byte string.
+     *
+     * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
+     *
+     * @param s String containing hexadecimal characters to convert
+     * @return Byte array generated from input
+     * @throws java.lang.IllegalArgumentException if input length is incorrect
+     */
+    public static byte[] HexStringToByteArray(String s) throws IllegalArgumentException {
+        int len = s.length();
+        if (len % 2 == 1) {
+            throw new IllegalArgumentException("Hex string must have even number of characters");
+        }
+        byte[] data = new byte[len / 2]; // Allocate 1 byte per 2 hex characters
+        for (int i = 0; i < len; i += 2) {
+            // Convert each character into a integer (base-16), then bit-shift into place
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+                    + Character.digit(s.charAt(i+1), 16));
+        }
+        return data;
+    }
+
+    /**
+     * Utility method to concatenate two byte arrays.
+     * @param first First array
+     * @param rest Any remaining arrays
+     * @return Concatenated copy of input arrays
+     */
+    public static byte[] ConcatArrays(byte[] first, byte[]... rest) {
+        int totalLength = first.length;
+        for (byte[] array : rest) {
+            totalLength += array.length;
+        }
+        byte[] result = Arrays.copyOf(first, totalLength);
+        int offset = first.length;
+        for (byte[] array : rest) {
+            System.arraycopy(array, 0, result, offset, array.length);
+            offset += array.length;
+        }
+        return result;
+    }
+}
diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/MainActivity.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/MainActivity.java
new file mode 100644
index 0000000..0515609
--- /dev/null
+++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.cardemulation;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        CardEmulationFragment fragment = new CardEmulationFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/CardEmulation/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/CardEmulation/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/CardReader/AndroidManifest.xml b/samples/browseable/CardReader/AndroidManifest.xml
new file mode 100644
index 0000000..a8ebd13
--- /dev/null
+++ b/samples/browseable/CardReader/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.cardreader"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <!-- NFC Reader Mode was added in API 19. -->
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+    <uses-permission android:name="android.permission.NFC" />
+    <uses-feature android:name="android.hardware.nfc" android:required="true" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+        <activity android:name=".MainActivity"
+                  android:label="@string/app_name"
+                  android:launchMode="singleTop">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+
+            <!-- NFC-related intent filter. Allows application to handle messages from any
+                 NFC-A devices discovered. Other Android devices are required to support NFC-A.
+                 See: res/xml/nfc_tech_filter.xml -->
+            <intent-filter>
+                <action android:name="android.nfc.action.TECH_DISCOVERED" />
+            </intent-filter>
+            <meta-data
+                android:name="android.nfc.action.TECH_DISCOVERED"
+                android:resource="@xml/nfc_tech_filter" />
+        </activity>
+    </application>
+
+
+</manifest>
diff --git a/samples/browseable/CardReader/_index.jd b/samples/browseable/CardReader/_index.jd
new file mode 100644
index 0000000..9ba051e
--- /dev/null
+++ b/samples/browseable/CardReader/_index.jd
@@ -0,0 +1,20 @@
+
+
+
+page.tags="CardReader"
+sample.group=Connectivity
+@jd:body
+
+<p>
+  This sample demonstrates how to implement a low-level NFC card reader, for
+  reading cards that do not contain NDEF or Android Beam data. This sample is
+  designed to read the virtual loyalty card implemented in the <a href=
+  "{@docRoot}samples/CardEmulation/index.html">CardEmulation</a> sample.
+</p>
+
+<p>
+  In particular, this sample demonstrates how to disable Android Beam, select
+  which AIDs the reader is interested in, and establish communication with the
+  card. See <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">Host-based
+  Card Emulation</a> for more information on the HCE APIs.
+</p>
diff --git a/samples/browseable/CardReader/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..9114e44
--- /dev/null
+++ b/samples/browseable/CardReader/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/CardReader/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/CardReader/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/CardReader/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..604fbd8
--- /dev/null
+++ b/samples/browseable/CardReader/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CardReader/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..9e58d22
--- /dev/null
+++ b/samples/browseable/CardReader/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CardReader/res/drawable-xxhdpi/card_background.png b/samples/browseable/CardReader/res/drawable-xxhdpi/card_background.png
new file mode 100644
index 0000000..86a7ba7
--- /dev/null
+++ b/samples/browseable/CardReader/res/drawable-xxhdpi/card_background.png
Binary files differ
diff --git a/samples/browseable/CardReader/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..c550c04
--- /dev/null
+++ b/samples/browseable/CardReader/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CardReader/res/layout-w720dp/activity_main.xml b/samples/browseable/CardReader/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/CardReader/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/CardReader/res/layout/activity_main.xml b/samples/browseable/CardReader/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/CardReader/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/CardReader/res/layout/main_fragment.xml b/samples/browseable/CardReader/res/layout/main_fragment.xml
new file mode 100644
index 0000000..1e3886c
--- /dev/null
+++ b/samples/browseable/CardReader/res/layout/main_fragment.xml
@@ -0,0 +1,64 @@
+<!--
+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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="380dp"
+    android:layout_height="242.25dp"
+    android:layout_gravity="center"
+    android:layout_margin="20dp">
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@drawable/card_background"
+        />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="20dp"
+        android:layout_gravity="center"
+        android:clickable="true">
+        <TextView
+            android:id="@+id/card_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/card_title"
+            android:fontFamily="sans-serif-condensed"
+            android:textStyle="bold"
+            android:textSize="32dp"
+            />
+        <TextView
+            android:id="@+id/card_account_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/account_number"
+            android:fontFamily="sans-serif"
+            android:textStyle="bold"
+            android:textSize="18dp"
+            android:layout_marginTop="40dp"
+            />
+        <TextView
+            android:id="@+id/card_account_field"
+            android:width="360dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif-condensed"
+            android:textStyle="bold"
+            android:textSize="42dp"
+            android:singleLine="true"
+            />
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/browseable/CardReader/res/menu/main.xml b/samples/browseable/CardReader/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/CardReader/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/DoneBar/res/values-sw600dp/dimens.xml b/samples/browseable/CardReader/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values-sw600dp/dimens.xml
copy to samples/browseable/CardReader/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/DoneBar/res/values-sw600dp/styles.xml b/samples/browseable/CardReader/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values-sw600dp/styles.xml
copy to samples/browseable/CardReader/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/CardReader/res/values-v11/template-styles.xml b/samples/browseable/CardReader/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/CardReader/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/CardReader/res/values/base-strings.xml b/samples/browseable/CardReader/res/values/base-strings.xml
new file mode 100644
index 0000000..ac12480
--- /dev/null
+++ b/samples/browseable/CardReader/res/values/base-strings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">CardReader</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample demonstrates how to implement a low-level NFC card reader, for reading cards
+            that do not contain NDEF or Android Beam data. This sample is designed to read the virtual
+            loyalty card implemented in the "CardEmulation" sample.\n\n
+
+            In particular, this sample demonstrates how to disable Android Beam, select which AIDs the
+            reader is interested, and establish communication with the card
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/CardReader/res/values/strings.xml b/samples/browseable/CardReader/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/CardReader/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/DoneBar/res/values/dimens.xml b/samples/browseable/CardReader/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values/dimens.xml
copy to samples/browseable/CardReader/res/values/template-dimens.xml
diff --git a/samples/browseable/CardReader/res/values/template-styles.xml b/samples/browseable/CardReader/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/CardReader/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/CardReader/res/xml/nfc_tech_filter.xml b/samples/browseable/CardReader/res/xml/nfc_tech_filter.xml
new file mode 100644
index 0000000..dcfc979
--- /dev/null
+++ b/samples/browseable/CardReader/res/xml/nfc_tech_filter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This file is used as part of the filter for incoming NFC TECH_DISCOVERED intents. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Android's host card emulation feature only supports the IsoDep protocol. -->
+    <tech-list>
+        <tech>android.nfc.tech.IsoDep</tech>
+    </tech-list>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/CardReader/src/com.example.android.cardreader/CardReaderFragment.java b/samples/browseable/CardReader/src/com.example.android.cardreader/CardReaderFragment.java
new file mode 100644
index 0000000..3f27e9b
--- /dev/null
+++ b/samples/browseable/CardReader/src/com.example.android.cardreader/CardReaderFragment.java
@@ -0,0 +1,109 @@
+/*
+ * 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.cardreader;
+
+import android.app.Activity;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.common.logger.Log;
+
+/**
+ * Generic UI for sample discovery.
+ */
+public class CardReaderFragment extends Fragment implements LoyaltyCardReader.AccountCallback {
+
+    public static final String TAG = "CardReaderFragment";
+    // Recommend NfcAdapter flags for reading from other Android devices. Indicates that this
+    // activity is interested in NFC-A devices (including other Android devices), and that the
+    // system should not check for the presence of NDEF-formatted data (e.g. Android Beam).
+    public static int READER_FLAGS =
+            NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
+    public LoyaltyCardReader mLoyaltyCardReader;
+    private TextView mAccountField;
+
+    /** Called when sample is created. Displays generic UI with welcome text. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        View v = inflater.inflate(R.layout.main_fragment, container, false);
+        if (v != null) {
+            mAccountField = (TextView) v.findViewById(R.id.card_account_field);
+            mAccountField.setText("Waiting...");
+
+            mLoyaltyCardReader = new LoyaltyCardReader(this);
+
+            // Disable Android Beam and register our card reader callback
+            enableReaderMode();
+        }
+
+        return v;
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        disableReaderMode();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        enableReaderMode();
+    }
+
+    private void enableReaderMode() {
+        Log.i(TAG, "Enabling reader mode");
+        Activity activity = getActivity();
+        NfcAdapter nfc = NfcAdapter.getDefaultAdapter(activity);
+        if (nfc != null) {
+            nfc.enableReaderMode(activity, mLoyaltyCardReader, READER_FLAGS, null);
+        }
+    }
+
+    private void disableReaderMode() {
+        Log.i(TAG, "Disabling reader mode");
+        Activity activity = getActivity();
+        NfcAdapter nfc = NfcAdapter.getDefaultAdapter(activity);
+        if (nfc != null) {
+            nfc.disableReaderMode(activity);
+        }
+    }
+
+    @Override
+    public void onAccountReceived(final String account) {
+        // This callback is run on a background thread, but updates to UI elements must be performed
+        // on the UI thread.
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mAccountField.setText(account);
+            }
+        });
+    }
+}
diff --git a/samples/browseable/CardReader/src/com.example.android.cardreader/LoyaltyCardReader.java b/samples/browseable/CardReader/src/com.example.android.cardreader/LoyaltyCardReader.java
new file mode 100644
index 0000000..c29bdfd
--- /dev/null
+++ b/samples/browseable/CardReader/src/com.example.android.cardreader/LoyaltyCardReader.java
@@ -0,0 +1,149 @@
+/*
+ * 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.cardreader;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.tech.IsoDep;
+
+import com.example.android.common.logger.Log;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+
+/**
+ * Callback class, invoked when an NFC card is scanned while the device is running in reader mode.
+ *
+ * Reader mode can be invoked by calling NfcAdapter
+ */
+public class LoyaltyCardReader implements NfcAdapter.ReaderCallback {
+    private static final String TAG = "LoyaltyCardReader";
+    // AID for our loyalty card service.
+    private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222";
+    // ISO-DEP command HEADER for selecting an AID.
+    // Format: [Class | Instruction | Parameter 1 | Parameter 2]
+    private static final String SELECT_APDU_HEADER = "00A40400";
+    // "OK" status word sent in response to SELECT AID command (0x9000)
+    private static final byte[] SELECT_OK_SW = {(byte) 0x90, (byte) 0x00};
+
+    // Weak reference to prevent retain loop. mAccountCallback is responsible for exiting
+    // foreground mode before it becomes invalid (e.g. during onPause() or onStop()).
+    private WeakReference<AccountCallback> mAccountCallback;
+
+    public interface AccountCallback {
+        public void onAccountReceived(String account);
+    }
+
+    public LoyaltyCardReader(AccountCallback accountCallback) {
+        mAccountCallback = new WeakReference<AccountCallback>(accountCallback);
+    }
+
+    /**
+     * Callback when a new tag is discovered by the system.
+     *
+     * <p>Communication with the card should take place here.
+     *
+     * @param tag Discovered tag
+     */
+    @Override
+    public void onTagDiscovered(Tag tag) {
+        Log.i(TAG, "New tag discovered");
+        // Android's Host-based Card Emulation (HCE) feature implements the ISO-DEP (ISO 14443-4)
+        // protocol.
+        //
+        // In order to communicate with a device using HCE, the discovered tag should be processed
+        // using the IsoDep class.
+        IsoDep isoDep = IsoDep.get(tag);
+        if (isoDep != null) {
+            try {
+                // Connect to the remote NFC device
+                isoDep.connect();
+                // Build SELECT AID command for our loyalty card service.
+                // This command tells the remote device which service we wish to communicate with.
+                Log.i(TAG, "Requesting remote AID: " + SAMPLE_LOYALTY_CARD_AID);
+                byte[] command = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID);
+                // Send command to remote device
+                Log.i(TAG, "Sending: " + ByteArrayToHexString(command));
+                byte[] result = isoDep.transceive(command);
+                // If AID is successfully selected, 0x9000 is returned as the status word (last 2
+                // bytes of the result) by convention. Everything before the status word is
+                // optional payload, which is used here to hold the account number.
+                int resultLength = result.length;
+                byte[] statusWord = {result[resultLength-2], result[resultLength-1]};
+                byte[] payload = Arrays.copyOf(result, resultLength-2);
+                if (Arrays.equals(SELECT_OK_SW, statusWord)) {
+                    // The remote NFC device will immediately respond with its stored account number
+                    String accountNumber = new String(payload, "UTF-8");
+                    Log.i(TAG, "Received: " + accountNumber);
+                    // Inform CardReaderFragment of received account number
+                    mAccountCallback.get().onAccountReceived(accountNumber);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Error communicating with card: " + e.toString());
+            }
+        }
+    }
+
+    /**
+     * Build APDU for SELECT AID command. This command indicates which service a reader is
+     * interested in communicating with. See ISO 7816-4.
+     *
+     * @param aid Application ID (AID) to select
+     * @return APDU for SELECT AID command
+     */
+    public static byte[] BuildSelectApdu(String aid) {
+        // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
+        return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid);
+    }
+
+    /**
+     * Utility class to convert a byte array to a hexadecimal string.
+     *
+     * @param bytes Bytes to convert
+     * @return String, containing hexadecimal representation.
+     */
+    public static String ByteArrayToHexString(byte[] bytes) {
+        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+        char[] hexChars = new char[bytes.length * 2];
+        int v;
+        for ( int j = 0; j < bytes.length; j++ ) {
+            v = bytes[j] & 0xFF;
+            hexChars[j * 2] = hexArray[v >>> 4];
+            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+        }
+        return new String(hexChars);
+    }
+
+    /**
+     * Utility class to convert a hexadecimal string to a byte string.
+     *
+     * <p>Behavior with input strings containing non-hexadecimal characters is undefined.
+     *
+     * @param s String containing hexadecimal characters to convert
+     * @return Byte array generated from input
+     */
+    public static byte[] HexStringToByteArray(String s) {
+        int len = s.length();
+        byte[] data = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+                    + Character.digit(s.charAt(i+1), 16));
+        }
+        return data;
+    }
+
+}
diff --git a/samples/browseable/CardReader/src/com.example.android.cardreader/MainActivity.java b/samples/browseable/CardReader/src/com.example.android.cardreader/MainActivity.java
new file mode 100644
index 0000000..e0280e9
--- /dev/null
+++ b/samples/browseable/CardReader/src/com.example.android.cardreader/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.cardreader;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        CardReaderFragment fragment = new CardReaderFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/CardReader/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/CardReader/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/CardReader/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/CardReader/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/CardReader/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/CardReader/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/CardReader/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/CardReader/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/CardReader/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/CardReader/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml b/samples/browseable/CustomChoiceList/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml
rename to samples/browseable/CustomChoiceList/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml b/samples/browseable/CustomChoiceList/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml
rename to samples/browseable/CustomChoiceList/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/CustomChoiceList/res/values-v11/template-styles.xml b/samples/browseable/CustomChoiceList/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/CustomChoiceList/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/CustomChoiceList/res/values/dimens.xml b/samples/browseable/CustomChoiceList/res/values/dimens.xml
index 39e710b..c22027e 100644
--- a/samples/browseable/CustomChoiceList/res/values/dimens.xml
+++ b/samples/browseable/CustomChoiceList/res/values/dimens.xml
@@ -16,17 +16,8 @@
 
 <resources>
 
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+    <!-- Meta-dimension that switches on screen size -->
 
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+    <dimen name="page_margin">@dimen/margin_medium</dimen>
 
 </resources>
diff --git a/samples/browseable/CustomChoiceList/res/values/styles.xml b/samples/browseable/CustomChoiceList/res/values/styles.xml
index 404623e..0851a81 100644
--- a/samples/browseable/CustomChoiceList/res/values/styles.xml
+++ b/samples/browseable/CustomChoiceList/res/values/styles.xml
@@ -15,28 +15,14 @@
   -->
 
 <resources>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
+    <style name="Widget.DescriptionBar">
+        <item name="android:background">#fb3</item>
+        <item name="android:paddingTop">@dimen/margin_medium</item>
+        <item name="android:paddingBottom">@dimen/margin_medium</item>
+        <item name="android:paddingLeft">@dimen/page_margin</item>
+        <item name="android:paddingRight">@dimen/page_margin</item>
         <item name="android:textAppearance">?android:textAppearanceMedium</item>
         <item name="android:lineSpacingMultiplier">1.1</item>
     </style>
 
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
 </resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/CustomChoiceList/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/CustomChoiceList/res/values/template-dimens.xml
diff --git a/samples/browseable/CustomChoiceList/res/values/template-styles.xml b/samples/browseable/CustomChoiceList/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/CustomChoiceList/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/CustomNotifications/res/values-sw600dp/dimens.xml b/samples/browseable/CustomNotifications/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/CustomNotifications/res/values-sw600dp/dimens.xml
rename to samples/browseable/CustomNotifications/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/CustomNotifications/res/values-sw600dp/styles.xml b/samples/browseable/CustomNotifications/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/CustomNotifications/res/values-sw600dp/styles.xml
rename to samples/browseable/CustomNotifications/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/CustomNotifications/res/values-v11/template-styles.xml b/samples/browseable/CustomNotifications/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/CustomNotifications/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/CustomNotifications/res/values/dimens.xml b/samples/browseable/CustomNotifications/res/values/dimens.xml
index 39e710b..ca1b9e6 100644
--- a/samples/browseable/CustomNotifications/res/values/dimens.xml
+++ b/samples/browseable/CustomNotifications/res/values/dimens.xml
@@ -1,32 +1,20 @@
 <!--
-  Copyright 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.
+  * 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>
-
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
-
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 </resources>
diff --git a/samples/browseable/CustomNotifications/res/values/styles.xml b/samples/browseable/CustomNotifications/res/values/styles.xml
index 404623e..1d3d45b 100644
--- a/samples/browseable/CustomNotifications/res/values/styles.xml
+++ b/samples/browseable/CustomNotifications/res/values/styles.xml
@@ -1,42 +1,22 @@
 <!--
-  Copyright 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.
+  * 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>
 
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
+    <style name="NotificationContent" parent="@android:style/TextAppearance.Small">
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
     </style>
 
 </resources>
diff --git a/samples/browseable/BasicNotifications/res/values/dimens.xml b/samples/browseable/CustomNotifications/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/BasicNotifications/res/values/dimens.xml
copy to samples/browseable/CustomNotifications/res/values/template-dimens.xml
diff --git a/samples/browseable/CustomNotifications/res/values/template-styles.xml b/samples/browseable/CustomNotifications/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/CustomNotifications/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/CustomTransition/AndroidManifest.xml b/samples/browseable/CustomTransition/AndroidManifest.xml
new file mode 100644
index 0000000..b3328f1
--- /dev/null
+++ b/samples/browseable/CustomTransition/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.customtransition"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/CustomTransition/_index.jd b/samples/browseable/CustomTransition/_index.jd
new file mode 100644
index 0000000..c68a762
--- /dev/null
+++ b/samples/browseable/CustomTransition/_index.jd
@@ -0,0 +1,8 @@
+
+
+
+page.tags="CustomTransition"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to create and use a custom {@link android.transition.Transition}.</p>
diff --git a/samples/browseable/CustomTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CustomTransition/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..846a7fe
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/CustomTransition/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/CustomTransition/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/CustomTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CustomTransition/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..7a21d06
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CustomTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CustomTransition/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71734d7
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CustomTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CustomTransition/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d6d9b26
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/CustomTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/CustomTransition/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/CustomTransition/res/layout/activity_main.xml b/samples/browseable/CustomTransition/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/CustomTransition/res/layout/fragment_custom_transition.xml b/samples/browseable/CustomTransition/res/layout/fragment_custom_transition.xml
new file mode 100644
index 0000000..c48f270
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/layout/fragment_custom_transition.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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"
+    tools:context="com.example.android.customtransition.CustomTransitionFragment">
+
+    <Button
+        android:id="@+id/show_next_scene"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Show next scene"/>
+
+    <FrameLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/samples/browseable/CustomTransition/res/layout/scene1.xml b/samples/browseable/CustomTransition/res/layout/scene1.xml
new file mode 100644
index 0000000..e686ff7
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/layout/scene1.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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
+    android:id="@+id/container"
+    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="horizontal"
+    tools:context="com.example.android.customtransition.CustomTransitionFragment">
+
+    <View
+        android:id="@+id/view_1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#f00"/>
+
+    <View
+        android:id="@+id/view_2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#0f0"/>
+
+    <View
+        android:id="@+id/view_3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#00f"/>
+
+</LinearLayout>
diff --git a/samples/browseable/CustomTransition/res/layout/scene2.xml b/samples/browseable/CustomTransition/res/layout/scene2.xml
new file mode 100644
index 0000000..e37e8b0
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/layout/scene2.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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
+    android:id="@+id/container"
+    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="horizontal"
+    tools:context="com.example.android.customtransition.CustomTransitionFragment">
+
+    <View
+        android:id="@+id/view_1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#0f0"/>
+
+    <View
+        android:id="@+id/view_2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#00f"/>
+
+    <View
+        android:id="@+id/view_3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#f00"/>
+
+</LinearLayout>
diff --git a/samples/browseable/CustomTransition/res/layout/scene3.xml b/samples/browseable/CustomTransition/res/layout/scene3.xml
new file mode 100644
index 0000000..e95fc3c
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/layout/scene3.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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
+    android:id="@+id/container"
+    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="horizontal"
+    tools:context="com.example.android.customtransition.CustomTransitionFragment">
+
+    <View
+        android:id="@+id/view_1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#00f"/>
+
+    <View
+        android:id="@+id/view_2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#f00"/>
+
+    <View
+        android:id="@+id/view_3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_margin="8dp"
+        android:background="#0f0"/>
+
+</LinearLayout>
diff --git a/samples/browseable/CustomTransition/res/menu/main.xml b/samples/browseable/CustomTransition/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml b/samples/browseable/CustomTransition/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml
copy to samples/browseable/CustomTransition/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml b/samples/browseable/CustomTransition/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml
copy to samples/browseable/CustomTransition/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/CustomTransition/res/values-v11/template-styles.xml b/samples/browseable/CustomTransition/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/CustomTransition/res/values/base-strings.xml b/samples/browseable/CustomTransition/res/values/base-strings.xml
new file mode 100644
index 0000000..68c9c41
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/values/base-strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">CustomTransition</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample demonstrates how to create and use a custom Transition.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/CustomTransition/res/values/strings.xml b/samples/browseable/CustomTransition/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/CustomTransition/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/CustomTransition/res/values/template-dimens.xml
diff --git a/samples/browseable/CustomTransition/res/values/template-styles.xml b/samples/browseable/CustomTransition/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/CustomTransition/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/CustomTransition/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/CustomTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/CustomTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java b/samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java
new file mode 100644
index 0000000..d36cfb4
--- /dev/null
+++ b/samples/browseable/CustomTransition/src/com.example.android.customtransition/ChangeColor.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 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.customtransition;
+
+import android.animation.Animator;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.transition.ChangeBounds;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+
+public class ChangeColor extends Transition {
+
+    /** Key to store a color value in TransitionValues object */
+    private static final String PROPNAME_BACKGROUND = "customtransition:change_color:background";
+
+    // BEGIN_INCLUDE (capture_values)
+    /**
+     * Convenience method: Add the background Drawable property value
+     * to the TransitionsValues.value Map for a target.
+     */
+    private void captureValues(TransitionValues values) {
+        // Capture the property values of views for later use
+        values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    // Capture the value of the background drawable property for a target in the ending Scene.
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+    // END_INCLUDE (capture_values)
+
+    // BEGIN_INCLUDE (create_animator)
+    // Create an animation for each target that is in both the starting and ending Scene. For each
+    // pair of targets, if their background property value is a color (rather than a graphic),
+    // create a ValueAnimator based on an ArgbEvaluator that interpolates between the starting and
+    // ending color. Also create an update listener that sets the View background color for each
+    // animation frame
+    @Override
+    public Animator createAnimator(ViewGroup sceneRoot,
+                                   TransitionValues startValues, TransitionValues endValues) {
+        // This transition can only be applied to views that are on both starting and ending scenes.
+        if (null == startValues || null == endValues) {
+            return null;
+        }
+        // Store a convenient reference to the target. Both the starting and ending layout have the
+        // same target.
+        final View view = endValues.view;
+        // Store the object containing the background property for both the starting and ending
+        // layouts.
+        Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
+        Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);
+        // This transition changes background colors for a target. It doesn't animate any other
+        // background changes. If the property isn't a ColorDrawable, ignore the target.
+        if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+            ColorDrawable startColor = (ColorDrawable) startBackground;
+            ColorDrawable endColor = (ColorDrawable) endBackground;
+            // If the background color for the target in the starting and ending layouts is
+            // different, create an animation.
+            if (startColor.getColor() != endColor.getColor()) {
+                // Create a new Animator object to apply to the targets as the transitions framework
+                // changes from the starting to the ending layout. Use the class ValueAnimator,
+                // which provides a timing pulse to change property values provided to it. The
+                // animation runs on the UI thread. The Evaluator controls what type of
+                // interpolation is done. In this case, an ArgbEvaluator interpolates between two
+                // #argb values, which are specified as the 2nd and 3rd input arguments.
+                ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(),
+                        startColor.getColor(), endColor.getColor());
+                // Add an update listener to the Animator object.
+                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        Object value = animation.getAnimatedValue();
+                        // Each time the ValueAnimator produces a new frame in the animation, change
+                        // the background color of the target. Ensure that the value isn't null.
+                        if (null != value) {
+                            view.setBackgroundColor((Integer) value);
+                        }
+                    }
+                });
+                // Return the Animator object to the transitions framework. As the framework changes
+                // between the starting and ending layouts, it applies the animation you've created.
+                return animator;
+            }
+        }
+        // For non-ColorDrawable backgrounds, we just return null, and no animation will take place.
+        return null;
+    }
+    // END_INCLUDE (create_animator)
+
+}
diff --git a/samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java b/samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java
new file mode 100644
index 0000000..8ef695a
--- /dev/null
+++ b/samples/browseable/CustomTransition/src/com.example.android.customtransition/CustomTransitionFragment.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 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.customtransition;
+
+import com.example.android.common.logger.Log;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.transition.Scene;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+public class CustomTransitionFragment extends Fragment implements View.OnClickListener {
+
+    private static final String STATE_CURRENT_SCENE = "current_scene";
+
+    /** Tag for the logger */
+    private static final String TAG = "CustomTransitionFragment";
+
+    /** These are the Scenes we use. */
+    private Scene[] mScenes;
+
+    /** The current index for mScenes. */
+    private int mCurrentScene;
+
+    /** This is the custom Transition we use in this sample. */
+    private Transition mTransition;
+
+    public CustomTransitionFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_custom_transition, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        Context context = getActivity();
+        FrameLayout container = (FrameLayout) view.findViewById(R.id.container);
+        view.findViewById(R.id.show_next_scene).setOnClickListener(this);
+        if (null != savedInstanceState) {
+            mCurrentScene = savedInstanceState.getInt(STATE_CURRENT_SCENE);
+        }
+        // We set up the Scenes here.
+        mScenes = new Scene[] {
+                Scene.getSceneForLayout(container, R.layout.scene1, context),
+                Scene.getSceneForLayout(container, R.layout.scene2, context),
+                Scene.getSceneForLayout(container, R.layout.scene3, context),
+        };
+        // This is the custom Transition.
+        mTransition = new ChangeColor();
+        // Show the initial Scene.
+        TransitionManager.go(mScenes[mCurrentScene % mScenes.length]);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(STATE_CURRENT_SCENE, mCurrentScene);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.show_next_scene: {
+                mCurrentScene = (mCurrentScene + 1) % mScenes.length;
+                Log.i(TAG, "Transitioning to scene #" + mCurrentScene);
+                // Pass the custom Transition as second argument for TransitionManager.go
+                TransitionManager.go(mScenes[mCurrentScene], mTransition);
+                break;
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java b/samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java
new file mode 100644
index 0000000..04c4e4d
--- /dev/null
+++ b/samples/browseable/CustomTransition/src/com.example.android.customtransition/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.customtransition;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        CustomTransitionFragment fragment = new CustomTransitionFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/AndroidManifest.xml b/samples/browseable/DisplayingBitmaps/AndroidManifest.xml
new file mode 100644
index 0000000..23db308
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.displayingbitmaps"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:description="@string/intro_message"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppThemeDark">
+
+        <activity android:name=".ui.ImageGridActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".ui.ImageDetailActivity"
+            android:label="@string/app_name"
+            android:parentActivityName=".ui.ImageGridActivity"
+            android:theme="@style/AppThemeDark.FullScreen" >
+            <meta-data android:name="android.support.PARENT_ACTIVITY"
+                android:value=".ui.ImageGridActivity" />
+        </activity>
+
+    </application>
+
+</manifest>
diff --git a/samples/browseable/DisplayingBitmaps/_index.jd b/samples/browseable/DisplayingBitmaps/_index.jd
new file mode 100644
index 0000000..eb88097
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/_index.jd
@@ -0,0 +1,21 @@
+
+
+
+page.tags="DisplayingBitmaps"
+sample.group=UI
+@jd:body
+
+<p>This is a sample application for the 
+<a href="{@docRoot}training/displaying-bitmaps/index.html">Displaying
+Bitmaps Efficiently</a> Android Training class.</p>
+
+<p>
+The sample demonstrates:
+</p>
+<ul>
+<li>Loading large bitmaps efficiently outside the main UI thread.</li>
+<li>Caching bitmaps (both in memory and on disk).</li>
+<li>Managing bitmap memory.</li>
+<li>Displaying bitmaps in UI elements (such as {@link android.support.v4.view.ViewPager ViewPager},
+{@link android.widget.ListView ListView}, and {@link android.widget.GridView GridView}).</li>
+</ul>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png b/samples/browseable/DisplayingBitmaps/res/drawable-hdpi/ic_launcher.png
similarity index 100%
copy from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png
copy to samples/browseable/DisplayingBitmaps/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/DisplayingBitmaps/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/DisplayingBitmaps/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-mdpi/ic_launcher.png b/samples/browseable/DisplayingBitmaps/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/DisplayingBitmaps/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-nodpi/empty_photo.png b/samples/browseable/DisplayingBitmaps/res/drawable-nodpi/empty_photo.png
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-nodpi/empty_photo.png
rename to samples/browseable/DisplayingBitmaps/res/drawable-nodpi/empty_photo.png
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/DisplayingBitmaps/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
copy from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png
copy to samples/browseable/DisplayingBitmaps/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/DisplayingBitmaps/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/DisplayingBitmaps/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DisplayingBitmaps/res/drawable/photogrid_list_selector.xml b/samples/browseable/DisplayingBitmaps/res/drawable/photogrid_list_selector.xml
new file mode 100644
index 0000000..d331c39
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/drawable/photogrid_list_selector.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true">
+        <shape>
+            <solid android:color="@color/grid_state_pressed" />
+        </shape>
+    </item>
+
+    <item android:state_focused="true">
+        <shape>
+            <solid android:color="@color/grid_state_focused" />
+        </shape>
+    </item>
+
+    <item android:drawable="@android:color/transparent" />
+
+</selector>
diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/DisplayingBitmaps/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/Basic/res/layout/activity_main.xml
copy to samples/browseable/DisplayingBitmaps/res/layout/activity_main.xml
diff --git a/samples/browseable/DisplayingBitmaps/res/layout/image_detail_fragment.xml b/samples/browseable/DisplayingBitmaps/res/layout/image_detail_fragment.xml
new file mode 100644
index 0000000..97ac520
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/layout/image_detail_fragment.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <ProgressBar
+        style="?android:attr/progressBarStyleLarge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center" />
+
+    <com.example.android.displayingbitmaps.ui.RecyclingImageView
+        android:id="@+id/imageView"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:contentDescription="@string/imageview_description" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_pager.xml b/samples/browseable/DisplayingBitmaps/res/layout/image_detail_pager.xml
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_pager.xml
rename to samples/browseable/DisplayingBitmaps/res/layout/image_detail_pager.xml
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_grid_fragment.xml b/samples/browseable/DisplayingBitmaps/res/layout/image_grid_fragment.xml
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_grid_fragment.xml
rename to samples/browseable/DisplayingBitmaps/res/layout/image_grid_fragment.xml
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/menu/main_menu.xml b/samples/browseable/DisplayingBitmaps/res/menu/main_menu.xml
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/menu/main_menu.xml
rename to samples/browseable/DisplayingBitmaps/res/menu/main_menu.xml
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values-large/dimens.xml b/samples/browseable/DisplayingBitmaps/res/values-large/dimens.xml
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/values-large/dimens.xml
rename to samples/browseable/DisplayingBitmaps/res/values-large/dimens.xml
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml b/samples/browseable/DisplayingBitmaps/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml
copy to samples/browseable/DisplayingBitmaps/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml b/samples/browseable/DisplayingBitmaps/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml
copy to samples/browseable/DisplayingBitmaps/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/DisplayingBitmaps/res/values-v11/styles.xml b/samples/browseable/DisplayingBitmaps/res/values-v11/styles.xml
new file mode 100644
index 0000000..57da0fb
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values-v11/styles.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+
+    <style name="AppThemeDark" parent="@android:style/Theme.Holo">
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="android:windowBackground">@android:color/black</item>
+        <item name="android:actionBarStyle">@style/TranslucentDarkActionBar</item>
+    </style>
+
+    <style name="AppThemeDark.FullScreen" />
+
+    <style name="TranslucentDarkActionBar" parent="@android:style/Widget.Holo.ActionBar">
+        <item name="android:background">#99000000</item>
+    </style>
+
+    <!--<style name="PhotoGridLayout">-->
+        <!--<item name="android:drawSelectorOnTop">true</item>-->
+    <!--</style>-->
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml b/samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values-xlarge/dimens.xml b/samples/browseable/DisplayingBitmaps/res/values-xlarge/dimens.xml
similarity index 100%
rename from samples/training/bitmapfun/BitmapFun/src/main/res/values-xlarge/dimens.xml
rename to samples/browseable/DisplayingBitmaps/res/values-xlarge/dimens.xml
diff --git a/samples/browseable/DisplayingBitmaps/res/values/base-strings.xml b/samples/browseable/DisplayingBitmaps/res/values/base-strings.xml
new file mode 100644
index 0000000..a6a8390
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values/base-strings.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">DisplayingBitmaps</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This is a sample application for the Android Training class
+            &quot;Displaying Bitmaps Efficiently&quot;
+            (http://developer.android.com/training/displaying-bitmaps/).\n\n
+
+            It demonstrates how to load large bitmaps efficiently off the main UI thread, caching
+            bitmaps (both in memory and on disk), managing bitmap memory and displaying bitmaps
+            in UI elements such as ViewPager and ListView/GridView.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/DisplayingBitmaps/res/values/colors.xml b/samples/browseable/DisplayingBitmaps/res/values/colors.xml
new file mode 100644
index 0000000..521b4b9
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values/colors.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+
+    <color name="grid_state_pressed">#1Affffff</color>
+    <color name="grid_state_focused">#80000000</color>
+
+</resources>
diff --git a/samples/browseable/DisplayingBitmaps/res/values/dimens.xml b/samples/browseable/DisplayingBitmaps/res/values/dimens.xml
new file mode 100644
index 0000000..53f61f0
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+
+    <dimen name="image_thumbnail_size">100dp</dimen>
+    <dimen name="image_thumbnail_spacing">1dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/DisplayingBitmaps/res/values/strings.xml b/samples/browseable/DisplayingBitmaps/res/values/strings.xml
new file mode 100644
index 0000000..84b6137
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+
+    <string name="clear_cache_menu">Clear Caches</string>
+    <string name="clear_cache_complete_toast">Caches have been cleared</string>
+    <string name="imageview_description">Image Thumbnail</string>
+    <string name="no_network_connection_toast">No network connection found</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/DisplayingBitmaps/res/values/styles.xml b/samples/browseable/DisplayingBitmaps/res/values/styles.xml
new file mode 100644
index 0000000..e9aabee
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2012 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<resources>
+
+    <style name="AppThemeDark" parent="android:Theme" />
+
+    <style name="AppThemeDark.FullScreen" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen" />
+
+    <style name="PhotoGridLayout">
+        <item name="android:drawSelectorOnTop">true</item>
+        <item name="android:listSelector">@drawable/photogrid_list_selector</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/DisplayingBitmaps/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/DisplayingBitmaps/res/values/template-dimens.xml
diff --git a/samples/browseable/DisplayingBitmaps/res/values/template-styles.xml b/samples/browseable/DisplayingBitmaps/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/provider/Images.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/provider/Images.java
new file mode 100644
index 0000000..5a89546
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/provider/Images.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.provider;
+
+/**
+ * Some simple test data to use for this sample app.
+ */
+public class Images {
+
+    /**
+     * This are PicasaWeb URLs and could potentially change. Ideally the PicasaWeb API should be
+     * used to fetch the URLs.
+     *
+     * Credit to Romain Guy for the photos:
+     * http://www.curious-creature.org/
+     * https://plus.google.com/109538161516040592207/about
+     * http://www.flickr.com/photos/romainguy
+     */
+    public final static String[] imageUrls = new String[] {
+            "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg",
+            "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s1024/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
+            "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s1024/Another%252520Rockaway%252520Sunset.jpg",
+            "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s1024/Antelope%252520Butte.jpg",
+            "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s1024/Antelope%252520Hallway.jpg",
+            "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s1024/Antelope%252520Walls.jpg",
+            "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s1024/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
+            "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s1024/Backlit%252520Cloud.jpg",
+            "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s1024/Bee%252520and%252520Flower.jpg",
+            "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s1024/Bonzai%252520Rock%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s1024/Caterpillar.jpg",
+            "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s1024/Chess.jpg",
+            "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s1024/Chihuly.jpg",
+            "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s1024/Closed%252520Door.jpg",
+            "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s1024/Colorado%252520River%252520Sunset.jpg",
+            "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s1024/Colors%252520of%252520Autumn.jpg",
+            "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s1024/Countryside.jpg",
+            "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s1024/Death%252520Valley%252520-%252520Dunes.jpg",
+            "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s1024/Delicate%252520Arch.jpg",
+            "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s1024/Despair.jpg",
+            "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s1024/Eagle%252520Fall%252520Sunrise.jpg",
+            "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s1024/Electric%252520Storm.jpg",
+            "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s1024/False%252520Kiva.jpg",
+            "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s1024/Fitzgerald%252520Streaks.jpg",
+            "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s1024/Foggy%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s1024/Frantic.jpg",
+            "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s1024/Golden%252520Gate%252520Afternoon.jpg",
+            "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s1024/Golden%252520Gate%252520Fog.jpg",
+            "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s1024/Golden%252520Grass.jpg",
+            "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s1024/Grand%252520Teton.jpg",
+            "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s1024/Grass%252520Closeup.jpg",
+            "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s1024/Green%252520Grass.jpg",
+            "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s1024/Hanging%252520Leaf.jpg",
+            "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s1024/Highway%2525201.jpg",
+            "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s1024/Horseshoe%252520Bend%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s1024/Horseshoe%252520Bend.jpg",
+            "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s1024/Into%252520the%252520Blue.jpg",
+            "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s1024/Jelly%252520Fish%2525202.jpg",
+            "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s1024/Jelly%252520Fish%2525203.jpg",
+            "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s1024/Kauai.jpg",
+            "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s1024/Kyoto%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s1024/Lake%252520Tahoe%252520Colors.jpg",
+            "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s1024/Lava%252520from%252520the%252520Sky.jpg",
+            "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s1024/Leica%25252050mm%252520Summilux.jpg",
+            "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s1024/Leica%25252050mm%252520Summilux.jpg",
+            "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s1024/Leica%252520M8%252520%252528Front%252529.jpg",
+            "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s1024/Light%252520to%252520Sand.jpg",
+            "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s1024/Little%252520Bit%252520of%252520Paradise.jpg",
+            "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s1024/Lone%252520Pine%252520Sunset.jpg",
+            "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s1024/Lonely%252520Rock.jpg",
+            "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s1024/Longue%252520Vue.jpg",
+            "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s1024/Look%252520Me%252520in%252520the%252520Eye.jpg",
+            "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s1024/Lost%252520in%252520a%252520Field.jpg",
+            "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s1024/Marshall%252520Beach%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s1024/Mono%252520Lake%252520Blue.jpg",
+            "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s1024/Monument%252520Valley%252520Overlook.jpg",
+            "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s1024/Moving%252520Rock.jpg",
+            "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s1024/Napali%252520Coast.jpg",
+            "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s1024/One%252520Wheel.jpg",
+            "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s1024/Open%252520Sky.jpg",
+            "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s1024/Orange%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s1024/Orchid.jpg",
+            "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s1024/Over%252520there.jpg",
+            "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s1024/Plumes.jpg",
+            "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s1024/Rainbokeh.jpg",
+            "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s1024/Rainbow.jpg",
+            "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s1024/Rice%252520Fields.jpg",
+            "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s1024/Rockaway%252520Fire%252520Sky.jpg",
+            "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s1024/Rockaway%252520Flow.jpg",
+            "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s1024/Rockaway%252520Sunset%252520Sky.jpg",
+            "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s1024/Russian%252520Ridge%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s1024/Rust%252520Knot.jpg",
+            "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s1024/Sailing%252520Stones.jpg",
+            "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s1024/Seahorse.jpg",
+            "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s1024/Shinjuku%252520Street.jpg",
+            "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s1024/Sierra%252520Heavens.jpg",
+            "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s1024/Sierra%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s1024/Sin%252520Lights.jpg",
+            "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s1024/Starry%252520Lake.jpg",
+            "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s1024/Starry%252520Night.jpg",
+            "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s1024/Stream.jpg",
+            "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s1024/Strip%252520Sunset.jpg",
+            "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s1024/Sunset%252520Hills.jpg",
+            "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s1024/Tenaya%252520Lake%2525202.jpg",
+            "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s1024/Tenaya%252520Lake.jpg",
+            "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s1024/The%252520Cave%252520BW.jpg",
+            "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s1024/The%252520Fisherman.jpg",
+            "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s1024/The%252520Night%252520is%252520Coming.jpg",
+            "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s1024/The%252520Road.jpg",
+            "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s1024/Tokyo%252520Heights.jpg",
+            "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s1024/Tokyo%252520Highway.jpg",
+            "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s1024/Tokyo%252520Smog.jpg",
+            "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s1024/Tufa%252520at%252520Night.jpg",
+            "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s1024/Valley%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s1024/Windmill%252520Sunrise.jpg",
+            "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s1024/Windmill.jpg",
+            "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s1024/Windmills.jpg",
+            "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s1024/Yet%252520Another%252520Rockaway%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s1024/Yosemite%252520Tree.jpg",
+    };
+
+    /**
+     * This are PicasaWeb thumbnail URLs and could potentially change. Ideally the PicasaWeb API
+     * should be used to fetch the URLs.
+     *
+     * Credit to Romain Guy for the photos:
+     * http://www.curious-creature.org/
+     * https://plus.google.com/109538161516040592207/about
+     * http://www.flickr.com/photos/romainguy
+     */
+    public final static String[] imageThumbUrls = new String[] {
+            "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s240-c/A%252520Photographer.jpg",
+            "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s240-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
+            "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s240-c/Another%252520Rockaway%252520Sunset.jpg",
+            "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s240-c/Antelope%252520Butte.jpg",
+            "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s240-c/Antelope%252520Hallway.jpg",
+            "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s240-c/Antelope%252520Walls.jpg",
+            "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s240-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
+            "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s240-c/Backlit%252520Cloud.jpg",
+            "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s240-c/Bee%252520and%252520Flower.jpg",
+            "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s240-c/Bonzai%252520Rock%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s240-c/Caterpillar.jpg",
+            "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s240-c/Chess.jpg",
+            "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s240-c/Chihuly.jpg",
+            "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s240-c/Closed%252520Door.jpg",
+            "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s240-c/Colorado%252520River%252520Sunset.jpg",
+            "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s240-c/Colors%252520of%252520Autumn.jpg",
+            "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s240-c/Countryside.jpg",
+            "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s240-c/Death%252520Valley%252520-%252520Dunes.jpg",
+            "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s240-c/Delicate%252520Arch.jpg",
+            "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s240-c/Despair.jpg",
+            "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s240-c/Eagle%252520Fall%252520Sunrise.jpg",
+            "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s240-c/Electric%252520Storm.jpg",
+            "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s240-c/False%252520Kiva.jpg",
+            "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s240-c/Fitzgerald%252520Streaks.jpg",
+            "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s240-c/Foggy%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s240-c/Frantic.jpg",
+            "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s240-c/Golden%252520Gate%252520Afternoon.jpg",
+            "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s240-c/Golden%252520Gate%252520Fog.jpg",
+            "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s240-c/Golden%252520Grass.jpg",
+            "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s240-c/Grand%252520Teton.jpg",
+            "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s240-c/Grass%252520Closeup.jpg",
+            "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s240-c/Green%252520Grass.jpg",
+            "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s240-c/Hanging%252520Leaf.jpg",
+            "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s240-c/Highway%2525201.jpg",
+            "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s240-c/Horseshoe%252520Bend%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s240-c/Horseshoe%252520Bend.jpg",
+            "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s240-c/Into%252520the%252520Blue.jpg",
+            "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s240-c/Jelly%252520Fish%2525202.jpg",
+            "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s240-c/Jelly%252520Fish%2525203.jpg",
+            "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s240-c/Kauai.jpg",
+            "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s240-c/Kyoto%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s240-c/Lake%252520Tahoe%252520Colors.jpg",
+            "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s240-c/Lava%252520from%252520the%252520Sky.jpg",
+            "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s240-c/Leica%25252050mm%252520Summilux.jpg",
+            "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s240-c/Leica%25252050mm%252520Summilux.jpg",
+            "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s240-c/Leica%252520M8%252520%252528Front%252529.jpg",
+            "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s240-c/Light%252520to%252520Sand.jpg",
+            "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s240-c/Little%252520Bit%252520of%252520Paradise.jpg",
+            "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s240-c/Lone%252520Pine%252520Sunset.jpg",
+            "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s240-c/Lonely%252520Rock.jpg",
+            "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s240-c/Longue%252520Vue.jpg",
+            "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s240-c/Look%252520Me%252520in%252520the%252520Eye.jpg",
+            "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s240-c/Lost%252520in%252520a%252520Field.jpg",
+            "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s240-c/Marshall%252520Beach%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s240-c/Mono%252520Lake%252520Blue.jpg",
+            "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s240-c/Monument%252520Valley%252520Overlook.jpg",
+            "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s240-c/Moving%252520Rock.jpg",
+            "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s240-c/Napali%252520Coast.jpg",
+            "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s240-c/One%252520Wheel.jpg",
+            "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s240-c/Open%252520Sky.jpg",
+            "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s240-c/Orange%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s240-c/Orchid.jpg",
+            "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s240-c/Over%252520there.jpg",
+            "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s240-c/Plumes.jpg",
+            "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s240-c/Rainbokeh.jpg",
+            "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s240-c/Rainbow.jpg",
+            "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s240-c/Rice%252520Fields.jpg",
+            "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s240-c/Rockaway%252520Fire%252520Sky.jpg",
+            "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s240-c/Rockaway%252520Flow.jpg",
+            "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s240-c/Rockaway%252520Sunset%252520Sky.jpg",
+            "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s240-c/Russian%252520Ridge%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s240-c/Rust%252520Knot.jpg",
+            "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s240-c/Sailing%252520Stones.jpg",
+            "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s240-c/Seahorse.jpg",
+            "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s240-c/Shinjuku%252520Street.jpg",
+            "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s240-c/Sierra%252520Heavens.jpg",
+            "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s240-c/Sierra%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s240-c/Sin%252520Lights.jpg",
+            "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s240-c/Starry%252520Lake.jpg",
+            "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s240-c/Starry%252520Night.jpg",
+            "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s240-c/Stream.jpg",
+            "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s240-c/Strip%252520Sunset.jpg",
+            "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s240-c/Sunset%252520Hills.jpg",
+            "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s240-c/Tenaya%252520Lake%2525202.jpg",
+            "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s240-c/Tenaya%252520Lake.jpg",
+            "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s240-c/The%252520Cave%252520BW.jpg",
+            "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s240-c/The%252520Fisherman.jpg",
+            "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s240-c/The%252520Night%252520is%252520Coming.jpg",
+            "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s240-c/The%252520Road.jpg",
+            "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s240-c/Tokyo%252520Heights.jpg",
+            "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s240-c/Tokyo%252520Highway.jpg",
+            "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s240-c/Tokyo%252520Smog.jpg",
+            "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s240-c/Tufa%252520at%252520Night.jpg",
+            "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s240-c/Valley%252520Sunset.jpg",
+            "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s240-c/Windmill%252520Sunrise.jpg",
+            "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s240-c/Windmill.jpg",
+            "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s240-c/Windmills.jpg",
+            "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s240-c/Yet%252520Another%252520Rockaway%252520Sunset.jpg",
+            "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s240-c/Yosemite%252520Tree.jpg",
+    };
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailActivity.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailActivity.java
new file mode 100644
index 0000000..c2be1ac
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailActivity.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.ui;
+
+import android.annotation.TargetApi;
+import android.app.ActionBar;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.app.NavUtils;
+import android.support.v4.view.ViewPager;
+import android.util.DisplayMetrics;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Toast;
+
+import com.example.android.displayingbitmaps.BuildConfig;
+import com.example.android.displayingbitmaps.R;
+import com.example.android.displayingbitmaps.provider.Images;
+import com.example.android.displayingbitmaps.util.ImageCache;
+import com.example.android.displayingbitmaps.util.ImageFetcher;
+import com.example.android.displayingbitmaps.util.Utils;
+
+public class ImageDetailActivity extends FragmentActivity implements OnClickListener {
+    private static final String IMAGE_CACHE_DIR = "images";
+    public static final String EXTRA_IMAGE = "extra_image";
+
+    private ImagePagerAdapter mAdapter;
+    private ImageFetcher mImageFetcher;
+    private ViewPager mPager;
+
+    @TargetApi(VERSION_CODES.HONEYCOMB)
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        if (BuildConfig.DEBUG) {
+            Utils.enableStrictMode();
+        }
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.image_detail_pager);
+
+        // Fetch screen height and width, to use as our max size when loading images as this
+        // activity runs full screen
+        final DisplayMetrics displayMetrics = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+        final int height = displayMetrics.heightPixels;
+        final int width = displayMetrics.widthPixels;
+
+        // For this sample we'll use half of the longest width to resize our images. As the
+        // image scaling ensures the image is larger than this, we should be left with a
+        // resolution that is appropriate for both portrait and landscape. For best image quality
+        // we shouldn't divide by 2, but this will use more memory and require a larger memory
+        // cache.
+        final int longest = (height > width ? height : width) / 2;
+
+        ImageCache.ImageCacheParams cacheParams =
+                new ImageCache.ImageCacheParams(this, IMAGE_CACHE_DIR);
+        cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory
+
+        // The ImageFetcher takes care of loading images into our ImageView children asynchronously
+        mImageFetcher = new ImageFetcher(this, longest);
+        mImageFetcher.addImageCache(getSupportFragmentManager(), cacheParams);
+        mImageFetcher.setImageFadeIn(false);
+
+        // Set up ViewPager and backing adapter
+        mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), Images.imageUrls.length);
+        mPager = (ViewPager) findViewById(R.id.pager);
+        mPager.setAdapter(mAdapter);
+        mPager.setPageMargin((int) getResources().getDimension(R.dimen.horizontal_page_margin));
+        mPager.setOffscreenPageLimit(2);
+
+        // Set up activity to go full screen
+        getWindow().addFlags(LayoutParams.FLAG_FULLSCREEN);
+
+        // Enable some additional newer visibility and ActionBar features to create a more
+        // immersive photo viewing experience
+        if (Utils.hasHoneycomb()) {
+            final ActionBar actionBar = getActionBar();
+
+            // Hide title text and set home as up
+            actionBar.setDisplayShowTitleEnabled(false);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+
+            // Hide and show the ActionBar as the visibility changes
+            mPager.setOnSystemUiVisibilityChangeListener(
+                    new View.OnSystemUiVisibilityChangeListener() {
+                        @Override
+                        public void onSystemUiVisibilityChange(int vis) {
+                            if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+                                actionBar.hide();
+                            } else {
+                                actionBar.show();
+                            }
+                        }
+                    });
+
+            // Start low profile mode and hide ActionBar
+            mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+            actionBar.hide();
+        }
+
+        // Set the current item based on the extra passed in to this activity
+        final int extraCurrentItem = getIntent().getIntExtra(EXTRA_IMAGE, -1);
+        if (extraCurrentItem != -1) {
+            mPager.setCurrentItem(extraCurrentItem);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mImageFetcher.setExitTasksEarly(false);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mImageFetcher.setExitTasksEarly(true);
+        mImageFetcher.flushCache();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mImageFetcher.closeCache();
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                NavUtils.navigateUpFromSameTask(this);
+                return true;
+            case R.id.clear_cache:
+                mImageFetcher.clearCache();
+                Toast.makeText(
+                        this, R.string.clear_cache_complete_toast,Toast.LENGTH_SHORT).show();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main_menu, menu);
+        return true;
+    }
+
+    /**
+     * Called by the ViewPager child fragments to load images via the one ImageFetcher
+     */
+    public ImageFetcher getImageFetcher() {
+        return mImageFetcher;
+    }
+
+    /**
+     * The main adapter that backs the ViewPager. A subclass of FragmentStatePagerAdapter as there
+     * could be a large number of items in the ViewPager and we don't want to retain them all in
+     * memory at once but create/destroy them on the fly.
+     */
+    private class ImagePagerAdapter extends FragmentStatePagerAdapter {
+        private final int mSize;
+
+        public ImagePagerAdapter(FragmentManager fm, int size) {
+            super(fm);
+            mSize = size;
+        }
+
+        @Override
+        public int getCount() {
+            return mSize;
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            return ImageDetailFragment.newInstance(Images.imageUrls[position]);
+        }
+    }
+
+    /**
+     * Set on the ImageView in the ViewPager children fragments, to enable/disable low profile mode
+     * when the ImageView is touched.
+     */
+    @TargetApi(VERSION_CODES.HONEYCOMB)
+    @Override
+    public void onClick(View v) {
+        final int vis = mPager.getSystemUiVisibility();
+        if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+            mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+        } else {
+            mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+        }
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailFragment.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailFragment.java
new file mode 100644
index 0000000..506729a
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailFragment.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.ui;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.example.android.displayingbitmaps.R;
+import com.example.android.displayingbitmaps.util.ImageFetcher;
+import com.example.android.displayingbitmaps.util.ImageWorker;
+import com.example.android.displayingbitmaps.util.Utils;
+
+/**
+ * This fragment will populate the children of the ViewPager from {@link ImageDetailActivity}.
+ */
+public class ImageDetailFragment extends Fragment {
+    private static final String IMAGE_DATA_EXTRA = "extra_image_data";
+    private String mImageUrl;
+    private ImageView mImageView;
+    private ImageFetcher mImageFetcher;
+
+    /**
+     * Factory method to generate a new instance of the fragment given an image number.
+     *
+     * @param imageUrl The image url to load
+     * @return A new instance of ImageDetailFragment with imageNum extras
+     */
+    public static ImageDetailFragment newInstance(String imageUrl) {
+        final ImageDetailFragment f = new ImageDetailFragment();
+
+        final Bundle args = new Bundle();
+        args.putString(IMAGE_DATA_EXTRA, imageUrl);
+        f.setArguments(args);
+
+        return f;
+    }
+
+    /**
+     * Empty constructor as per the Fragment documentation
+     */
+    public ImageDetailFragment() {}
+
+    /**
+     * Populate image using a url from extras, use the convenience factory method
+     * {@link ImageDetailFragment#newInstance(String)} to create this fragment.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mImageUrl = getArguments() != null ? getArguments().getString(IMAGE_DATA_EXTRA) : null;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Inflate and locate the main ImageView
+        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
+        mImageView = (ImageView) v.findViewById(R.id.imageView);
+        return v;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        // Use the parent activity to load the image asynchronously into the ImageView (so a single
+        // cache can be used over all pages in the ViewPager
+        if (ImageDetailActivity.class.isInstance(getActivity())) {
+            mImageFetcher = ((ImageDetailActivity) getActivity()).getImageFetcher();
+            mImageFetcher.loadImage(mImageUrl, mImageView);
+        }
+
+        // Pass clicks on the ImageView to the parent activity to handle
+        if (OnClickListener.class.isInstance(getActivity()) && Utils.hasHoneycomb()) {
+            mImageView.setOnClickListener((OnClickListener) getActivity());
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mImageView != null) {
+            // Cancel any pending image work
+            ImageWorker.cancelWork(mImageView);
+            mImageView.setImageDrawable(null);
+        }
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridActivity.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridActivity.java
new file mode 100644
index 0000000..f171955
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.ui;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+
+import com.example.android.displayingbitmaps.BuildConfig;
+import com.example.android.displayingbitmaps.util.Utils;
+
+/**
+ * Simple FragmentActivity to hold the main {@link ImageGridFragment} and not much else.
+ */
+public class ImageGridActivity extends FragmentActivity {
+    private static final String TAG = "ImageGridActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        if (BuildConfig.DEBUG) {
+            Utils.enableStrictMode();
+        }
+        super.onCreate(savedInstanceState);
+
+        if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
+            final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+            ft.add(android.R.id.content, new ImageGridFragment(), TAG);
+            ft.commit();
+        }
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridFragment.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridFragment.java
new file mode 100644
index 0000000..4eb1f1b
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridFragment.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.ui;
+
+import android.annotation.TargetApi;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+import com.example.android.displayingbitmaps.R;
+import com.example.android.displayingbitmaps.provider.Images;
+import com.example.android.displayingbitmaps.util.ImageCache;
+import com.example.android.displayingbitmaps.util.ImageFetcher;
+import com.example.android.displayingbitmaps.util.Utils;
+
+/**
+ * The main fragment that powers the ImageGridActivity screen. Fairly straight forward GridView
+ * implementation with the key addition being the ImageWorker class w/ImageCache to load children
+ * asynchronously, keeping the UI nice and smooth and caching thumbnails for quick retrieval. The
+ * cache is retained over configuration changes like orientation change so the images are populated
+ * quickly if, for example, the user rotates the device.
+ */
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+    private static final String TAG = "ImageGridFragment";
+    private static final String IMAGE_CACHE_DIR = "thumbs";
+
+    private int mImageThumbSize;
+    private int mImageThumbSpacing;
+    private ImageAdapter mAdapter;
+    private ImageFetcher mImageFetcher;
+
+    /**
+     * Empty constructor as per the Fragment documentation
+     */
+    public ImageGridFragment() {}
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setHasOptionsMenu(true);
+
+        mImageThumbSize = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
+        mImageThumbSpacing = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);
+
+        mAdapter = new ImageAdapter(getActivity());
+
+        ImageCache.ImageCacheParams cacheParams =
+                new ImageCache.ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
+
+        cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory
+
+        // The ImageFetcher takes care of loading images into our ImageView children asynchronously
+        mImageFetcher = new ImageFetcher(getActivity(), mImageThumbSize);
+        mImageFetcher.setLoadingImage(R.drawable.empty_photo);
+        mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);
+    }
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
+        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
+        mGridView.setAdapter(mAdapter);
+        mGridView.setOnItemClickListener(this);
+        mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
+            @Override
+            public void onScrollStateChanged(AbsListView absListView, int scrollState) {
+                // Pause fetcher to ensure smoother scrolling when flinging
+                if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
+                    // Before Honeycomb pause image loading on scroll to help with performance
+                    if (!Utils.hasHoneycomb()) {
+                        mImageFetcher.setPauseWork(true);
+                    }
+                } else {
+                    mImageFetcher.setPauseWork(false);
+                }
+            }
+
+            @Override
+            public void onScroll(AbsListView absListView, int firstVisibleItem,
+                    int visibleItemCount, int totalItemCount) {
+            }
+        });
+
+        // This listener is used to get the final width of the GridView and then calculate the
+        // number of columns and the width of each column. The width of each column is variable
+        // as the GridView has stretchMode=columnWidth. The column width is used to set the height
+        // of each view so we get nice square thumbnails.
+        mGridView.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @TargetApi(VERSION_CODES.JELLY_BEAN)
+                    @Override
+                    public void onGlobalLayout() {
+                        if (mAdapter.getNumColumns() == 0) {
+                            final int numColumns = (int) Math.floor(
+                                    mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing));
+                            if (numColumns > 0) {
+                                final int columnWidth =
+                                        (mGridView.getWidth() / numColumns) - mImageThumbSpacing;
+                                mAdapter.setNumColumns(numColumns);
+                                mAdapter.setItemHeight(columnWidth);
+                                if (BuildConfig.DEBUG) {
+                                    Log.d(TAG, "onCreateView - numColumns set to " + numColumns);
+                                }
+                                if (Utils.hasJellyBean()) {
+                                    mGridView.getViewTreeObserver()
+                                            .removeOnGlobalLayoutListener(this);
+                                } else {
+                                    mGridView.getViewTreeObserver()
+                                            .removeGlobalOnLayoutListener(this);
+                                }
+                            }
+                        }
+                    }
+                });
+
+        return v;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mImageFetcher.setExitTasksEarly(false);
+        mAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mImageFetcher.setPauseWork(false);
+        mImageFetcher.setExitTasksEarly(true);
+        mImageFetcher.flushCache();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mImageFetcher.closeCache();
+    }
+
+    @TargetApi(VERSION_CODES.JELLY_BEAN)
+    @Override
+    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
+        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, (int) id);
+        if (Utils.hasJellyBean()) {
+            // makeThumbnailScaleUpAnimation() looks kind of ugly here as the loading spinner may
+            // show plus the thumbnail image in GridView is cropped. so using
+            // makeScaleUpAnimation() instead.
+            ActivityOptions options =
+                    ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getWidth(), v.getHeight());
+            getActivity().startActivity(i, options.toBundle());
+        } else {
+            startActivity(i);
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.main_menu, menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.clear_cache:
+                mImageFetcher.clearCache();
+                Toast.makeText(getActivity(), R.string.clear_cache_complete_toast,
+                        Toast.LENGTH_SHORT).show();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * The main adapter that backs the GridView. This is fairly standard except the number of
+     * columns in the GridView is used to create a fake top row of empty views as we use a
+     * transparent ActionBar and don't want the real top row of images to start off covered by it.
+     */
+    private class ImageAdapter extends BaseAdapter {
+
+        private final Context mContext;
+        private int mItemHeight = 0;
+        private int mNumColumns = 0;
+        private int mActionBarHeight = 0;
+        private GridView.LayoutParams mImageViewLayoutParams;
+
+        public ImageAdapter(Context context) {
+            super();
+            mContext = context;
+            mImageViewLayoutParams = new GridView.LayoutParams(
+                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+            // Calculate ActionBar height
+            TypedValue tv = new TypedValue();
+            if (context.getTheme().resolveAttribute(
+                    android.R.attr.actionBarSize, tv, true)) {
+                mActionBarHeight = TypedValue.complexToDimensionPixelSize(
+                        tv.data, context.getResources().getDisplayMetrics());
+            }
+        }
+
+        @Override
+        public int getCount() {
+            // If columns have yet to be determined, return no items
+            if (getNumColumns() == 0) {
+                return 0;
+            }
+
+            // Size + number of columns for top empty row
+            return Images.imageThumbUrls.length + mNumColumns;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return position < mNumColumns ?
+                    null : Images.imageThumbUrls[position - mNumColumns];
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position < mNumColumns ? 0 : position - mNumColumns;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            // Two types of views, the normal ImageView and the top row of empty views
+            return 2;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return (position < mNumColumns) ? 1 : 0;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup container) {
+            //BEGIN_INCLUDE(load_gridview_item)
+            // First check if this is the top row
+            if (position < mNumColumns) {
+                if (convertView == null) {
+                    convertView = new View(mContext);
+                }
+                // Set empty view with height of ActionBar
+                convertView.setLayoutParams(new AbsListView.LayoutParams(
+                        LayoutParams.MATCH_PARENT, mActionBarHeight));
+                return convertView;
+            }
+
+            // Now handle the main ImageView thumbnails
+            ImageView imageView;
+            if (convertView == null) { // if it's not recycled, instantiate and initialize
+                imageView = new RecyclingImageView(mContext);
+                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+                imageView.setLayoutParams(mImageViewLayoutParams);
+            } else { // Otherwise re-use the converted view
+                imageView = (ImageView) convertView;
+            }
+
+            // Check the height matches our calculated column width
+            if (imageView.getLayoutParams().height != mItemHeight) {
+                imageView.setLayoutParams(mImageViewLayoutParams);
+            }
+
+            // Finally load the image asynchronously into the ImageView, this also takes care of
+            // setting a placeholder image while the background thread runs
+            mImageFetcher.loadImage(Images.imageThumbUrls[position - mNumColumns], imageView);
+            return imageView;
+            //END_INCLUDE(load_gridview_item)
+        }
+
+        /**
+         * Sets the item height. Useful for when we know the column width so the height can be set
+         * to match.
+         *
+         * @param height
+         */
+        public void setItemHeight(int height) {
+            if (height == mItemHeight) {
+                return;
+            }
+            mItemHeight = height;
+            mImageViewLayoutParams =
+                    new GridView.LayoutParams(LayoutParams.MATCH_PARENT, mItemHeight);
+            mImageFetcher.setImageSize(height);
+            notifyDataSetChanged();
+        }
+
+        public void setNumColumns(int numColumns) {
+            mNumColumns = numColumns;
+        }
+
+        public int getNumColumns() {
+            return mNumColumns;
+        }
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/RecyclingImageView.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/RecyclingImageView.java
new file mode 100644
index 0000000..1db134c
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/RecyclingImageView.java
@@ -0,0 +1,89 @@
+/*
+ * 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.displayingbitmaps.ui;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.example.android.displayingbitmaps.util.RecyclingBitmapDrawable;
+
+/**
+ * Sub-class of ImageView which automatically notifies the drawable when it is
+ * being displayed.
+ */
+public class RecyclingImageView extends ImageView {
+
+    public RecyclingImageView(Context context) {
+        super(context);
+    }
+
+    public RecyclingImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * @see android.widget.ImageView#onDetachedFromWindow()
+     */
+    @Override
+    protected void onDetachedFromWindow() {
+        // This has been detached from Window, so clear the drawable
+        setImageDrawable(null);
+
+        super.onDetachedFromWindow();
+    }
+
+    /**
+     * @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)
+     */
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        // Keep hold of previous Drawable
+        final Drawable previousDrawable = getDrawable();
+
+        // Call super to set new Drawable
+        super.setImageDrawable(drawable);
+
+        // Notify new Drawable that it is being displayed
+        notifyDrawable(drawable, true);
+
+        // Notify old Drawable so it is no longer being displayed
+        notifyDrawable(previousDrawable, false);
+    }
+
+    /**
+     * Notifies the drawable that it's displayed state has changed.
+     *
+     * @param drawable
+     * @param isDisplayed
+     */
+    private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
+        if (drawable instanceof RecyclingBitmapDrawable) {
+            // The drawable is a CountingBitmapDrawable, so notify it
+            ((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
+        } else if (drawable instanceof LayerDrawable) {
+            // The drawable is a LayerDrawable, so recurse on each layer
+            LayerDrawable layerDrawable = (LayerDrawable) drawable;
+            for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
+                notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/AsyncTask.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/AsyncTask.java
new file mode 100644
index 0000000..dffade4
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/AsyncTask.java
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2008 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.displayingbitmaps.util;
+
+import android.annotation.TargetApi;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+
+import java.util.ArrayDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * *************************************
+ * Copied from JB release framework:
+ * https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/java/android/os/AsyncTask.java
+ *
+ * so that threading behavior on all OS versions is the same and we can tweak behavior by using
+ * executeOnExecutor() if needed.
+ *
+ * There are 3 changes in this copy of AsyncTask:
+ *    -pre-HC a single thread executor is used for serial operation
+ *    (Executors.newSingleThreadExecutor) and is the default
+ *    -the default THREAD_POOL_EXECUTOR was changed to use DiscardOldestPolicy
+ *    -a new fixed thread pool called DUAL_THREAD_EXECUTOR was added
+ * *************************************
+ *
+ * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
+ * perform background operations and publish results on the UI thread without
+ * having to manipulate threads and/or handlers.</p>
+ *
+ * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link android.os.Handler}
+ * and does not constitute a generic threading framework. AsyncTasks should ideally be
+ * used for short operations (a few seconds at the most.) If you need to keep threads
+ * running for long periods of time, it is highly recommended you use the various APIs
+ * provided by the <code>java.util.concurrent</code> pacakge such as {@link java.util.concurrent.Executor},
+ * {@link java.util.concurrent.ThreadPoolExecutor} and {@link java.util.concurrent.FutureTask}.</p>
+ *
+ * <p>An asynchronous task is defined by a computation that runs on a background thread and
+ * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
+ * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
+ * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
+ * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using tasks and threads, read the
+ * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
+ * Threads</a> developer guide.</p>
+ * </div>
+ *
+ * <h2>Usage</h2>
+ * <p>AsyncTask must be subclassed to be used. The subclass will override at least
+ * one method ({@link #doInBackground}), and most often will override a
+ * second one ({@link #onPostExecute}.)</p>
+ *
+ * <p>Here is an example of subclassing:</p>
+ * <pre class="prettyprint">
+ * private class DownloadFilesTask extends AsyncTask&lt;URL, Integer, Long&gt; {
+ *     protected Long doInBackground(URL... urls) {
+ *         int count = urls.length;
+ *         long totalSize = 0;
+ *         for (int i = 0; i < count; i++) {
+ *             totalSize += Downloader.downloadFile(urls[i]);
+ *             publishProgress((int) ((i / (float) count) * 100));
+ *             // Escape early if cancel() is called
+ *             if (isCancelled()) break;
+ *         }
+ *         return totalSize;
+ *     }
+ *
+ *     protected void onProgressUpdate(Integer... progress) {
+ *         setProgressPercent(progress[0]);
+ *     }
+ *
+ *     protected void onPostExecute(Long result) {
+ *         showDialog("Downloaded " + result + " bytes");
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Once created, a task is executed very simply:</p>
+ * <pre class="prettyprint">
+ * new DownloadFilesTask().execute(url1, url2, url3);
+ * </pre>
+ *
+ * <h2>AsyncTask's generic types</h2>
+ * <p>The three types used by an asynchronous task are the following:</p>
+ * <ol>
+ *     <li><code>Params</code>, the type of the parameters sent to the task upon
+ *     execution.</li>
+ *     <li><code>Progress</code>, the type of the progress units published during
+ *     the background computation.</li>
+ *     <li><code>Result</code>, the type of the result of the background
+ *     computation.</li>
+ * </ol>
+ * <p>Not all types are always used by an asynchronous task. To mark a type as unused,
+ * simply use the type {@link Void}:</p>
+ * <pre>
+ * private class MyTask extends AsyncTask&lt;Void, Void, Void&gt; { ... }
+ * </pre>
+ *
+ * <h2>The 4 steps</h2>
+ * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
+ * <ol>
+ *     <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
+ *     is executed. This step is normally used to setup the task, for instance by
+ *     showing a progress bar in the user interface.</li>
+ *     <li>{@link #doInBackground}, invoked on the background thread
+ *     immediately after {@link #onPreExecute()} finishes executing. This step is used
+ *     to perform background computation that can take a long time. The parameters
+ *     of the asynchronous task are passed to this step. The result of the computation must
+ *     be returned by this step and will be passed back to the last step. This step
+ *     can also use {@link #publishProgress} to publish one or more units
+ *     of progress. These values are published on the UI thread, in the
+ *     {@link #onProgressUpdate} step.</li>
+ *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
+ *     call to {@link #publishProgress}. The timing of the execution is
+ *     undefined. This method is used to display any form of progress in the user
+ *     interface while the background computation is still executing. For instance,
+ *     it can be used to animate a progress bar or show logs in a text field.</li>
+ *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
+ *     computation finishes. The result of the background computation is passed to
+ *     this step as a parameter.</li>
+ * </ol>
+ *
+ * <h2>Cancelling a task</h2>
+ * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
+ * this method will cause subsequent calls to {@link #isCancelled()} to return true.
+ * After invoking this method, {@link #onCancelled(Object)}, instead of
+ * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
+ * returns. To ensure that a task is cancelled as quickly as possible, you should always
+ * check the return value of {@link #isCancelled()} periodically from
+ * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
+ *
+ * <h2>Threading rules</h2>
+ * <p>There are a few threading rules that must be followed for this class to
+ * work properly:</p>
+ * <ul>
+ *     <li>The AsyncTask class must be loaded on the UI thread. This is done
+ *     automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
+ *     <li>The task instance must be created on the UI thread.</li>
+ *     <li>{@link #execute} must be invoked on the UI thread.</li>
+ *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
+ *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
+ *     <li>The task can be executed only once (an exception will be thrown if
+ *     a second execution is attempted.)</li>
+ * </ul>
+ *
+ * <h2>Memory observability</h2>
+ * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
+ * operations are safe without explicit synchronizations.</p>
+ * <ul>
+ *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
+ *     in {@link #doInBackground}.
+ *     <li>Set member fields in {@link #doInBackground}, and refer to them in
+ *     {@link #onProgressUpdate} and {@link #onPostExecute}.
+ * </ul>
+ *
+ * <h2>Order of execution</h2>
+ * <p>When first introduced, AsyncTasks were executed serially on a single background
+ * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+ * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
+ * thread to avoid common application errors caused by parallel execution.</p>
+ * <p>If you truly want parallel execution, you can invoke
+ * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
+ * {@link #THREAD_POOL_EXECUTOR}.</p>
+ */
+public abstract class AsyncTask<Params, Progress, Result> {
+    private static final String LOG_TAG = "AsyncTask";
+
+    private static final int CORE_POOL_SIZE = 5;
+    private static final int MAXIMUM_POOL_SIZE = 128;
+    private static final int KEEP_ALIVE = 1;
+
+    private static final ThreadFactory  sThreadFactory = new ThreadFactory() {
+        private final AtomicInteger mCount = new AtomicInteger(1);
+
+        public Thread newThread(Runnable r) {
+            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
+        }
+    };
+
+    private static final BlockingQueue<Runnable> sPoolWorkQueue =
+            new LinkedBlockingQueue<Runnable>(10);
+
+    /**
+     * An {@link java.util.concurrent.Executor} that can be used to execute tasks in parallel.
+     */
+    public static final Executor THREAD_POOL_EXECUTOR
+            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
+            TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory,
+            new ThreadPoolExecutor.DiscardOldestPolicy());
+
+    /**
+     * An {@link java.util.concurrent.Executor} that executes tasks one at a time in serial
+     * order.  This serialization is global to a particular process.
+     */
+    public static final Executor SERIAL_EXECUTOR = Utils.hasHoneycomb() ? new SerialExecutor() :
+            Executors.newSingleThreadExecutor(sThreadFactory);
+
+    public static final Executor DUAL_THREAD_EXECUTOR =
+            Executors.newFixedThreadPool(2, sThreadFactory);
+
+    private static final int MESSAGE_POST_RESULT = 0x1;
+    private static final int MESSAGE_POST_PROGRESS = 0x2;
+
+    private static final InternalHandler sHandler = new InternalHandler();
+
+    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
+    private final WorkerRunnable<Params, Result> mWorker;
+    private final FutureTask<Result> mFuture;
+
+    private volatile Status mStatus = Status.PENDING;
+
+    private final AtomicBoolean mCancelled = new AtomicBoolean();
+    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
+
+    @TargetApi(11)
+    private static class SerialExecutor implements Executor {
+        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
+        Runnable mActive;
+
+        public synchronized void execute(final Runnable r) {
+            mTasks.offer(new Runnable() {
+                public void run() {
+                    try {
+                        r.run();
+                    } finally {
+                        scheduleNext();
+                    }
+                }
+            });
+            if (mActive == null) {
+                scheduleNext();
+            }
+        }
+
+        protected synchronized void scheduleNext() {
+            if ((mActive = mTasks.poll()) != null) {
+                THREAD_POOL_EXECUTOR.execute(mActive);
+            }
+        }
+    }
+
+    /**
+     * Indicates the current status of the task. Each status will be set only once
+     * during the lifetime of a task.
+     */
+    public enum Status {
+        /**
+         * Indicates that the task has not been executed yet.
+         */
+        PENDING,
+        /**
+         * Indicates that the task is running.
+         */
+        RUNNING,
+        /**
+         * Indicates that {@link AsyncTask#onPostExecute} has finished.
+         */
+        FINISHED,
+    }
+
+    /** @hide Used to force static handler to be created. */
+    public static void init() {
+        sHandler.getLooper();
+    }
+
+    /** @hide */
+    public static void setDefaultExecutor(Executor exec) {
+        sDefaultExecutor = exec;
+    }
+
+    /**
+     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     */
+    public AsyncTask() {
+        mWorker = new WorkerRunnable<Params, Result>() {
+            public Result call() throws Exception {
+                mTaskInvoked.set(true);
+
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                //noinspection unchecked
+                return postResult(doInBackground(mParams));
+            }
+        };
+
+        mFuture = new FutureTask<Result>(mWorker) {
+            @Override
+            protected void done() {
+                try {
+                    postResultIfNotInvoked(get());
+                } catch (InterruptedException e) {
+                    android.util.Log.w(LOG_TAG, e);
+                } catch (ExecutionException e) {
+                    throw new RuntimeException("An error occured while executing doInBackground()",
+                            e.getCause());
+                } catch (CancellationException e) {
+                    postResultIfNotInvoked(null);
+                }
+            }
+        };
+    }
+
+    private void postResultIfNotInvoked(Result result) {
+        final boolean wasTaskInvoked = mTaskInvoked.get();
+        if (!wasTaskInvoked) {
+            postResult(result);
+        }
+    }
+
+    private Result postResult(Result result) {
+        @SuppressWarnings("unchecked")
+        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+                new AsyncTaskResult<Result>(this, result));
+        message.sendToTarget();
+        return result;
+    }
+
+    /**
+     * Returns the current status of this task.
+     *
+     * @return The current status.
+     */
+    public final Status getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Override this method to perform a computation on a background thread. The
+     * specified parameters are the parameters passed to {@link #execute}
+     * by the caller of this task.
+     *
+     * This method can call {@link #publishProgress} to publish updates
+     * on the UI thread.
+     *
+     * @param params The parameters of the task.
+     *
+     * @return A result, defined by the subclass of this task.
+     *
+     * @see #onPreExecute()
+     * @see #onPostExecute
+     * @see #publishProgress
+     */
+    protected abstract Result doInBackground(Params... params);
+
+    /**
+     * Runs on the UI thread before {@link #doInBackground}.
+     *
+     * @see #onPostExecute
+     * @see #doInBackground
+     */
+    protected void onPreExecute() {
+    }
+
+    /**
+     * <p>Runs on the UI thread after {@link #doInBackground}. The
+     * specified result is the value returned by {@link #doInBackground}.</p>
+     *
+     * <p>This method won't be invoked if the task was cancelled.</p>
+     *
+     * @param result The result of the operation computed by {@link #doInBackground}.
+     *
+     * @see #onPreExecute
+     * @see #doInBackground
+     * @see #onCancelled(Object)
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected void onPostExecute(Result result) {
+    }
+
+    /**
+     * Runs on the UI thread after {@link #publishProgress} is invoked.
+     * The specified values are the values passed to {@link #publishProgress}.
+     *
+     * @param values The values indicating progress.
+     *
+     * @see #publishProgress
+     * @see #doInBackground
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected void onProgressUpdate(Progress... values) {
+    }
+
+    /**
+     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+     * {@link #doInBackground(Object[])} has finished.</p>
+     *
+     * <p>The default implementation simply invokes {@link #onCancelled()} and
+     * ignores the result. If you write your own implementation, do not call
+     * <code>super.onCancelled(result)</code>.</p>
+     *
+     * @param result The result, if any, computed in
+     *               {@link #doInBackground(Object[])}, can be null
+     *
+     * @see #cancel(boolean)
+     * @see #isCancelled()
+     */
+    @SuppressWarnings({"UnusedParameters"})
+    protected void onCancelled(Result result) {
+        onCancelled();
+    }
+
+    /**
+     * <p>Applications should preferably override {@link #onCancelled(Object)}.
+     * This method is invoked by the default implementation of
+     * {@link #onCancelled(Object)}.</p>
+     *
+     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+     * {@link #doInBackground(Object[])} has finished.</p>
+     *
+     * @see #onCancelled(Object)
+     * @see #cancel(boolean)
+     * @see #isCancelled()
+     */
+    protected void onCancelled() {
+    }
+
+    /**
+     * Returns <tt>true</tt> if this task was cancelled before it completed
+     * normally. If you are calling {@link #cancel(boolean)} on the task,
+     * the value returned by this method should be checked periodically from
+     * {@link #doInBackground(Object[])} to end the task as soon as possible.
+     *
+     * @return <tt>true</tt> if task was cancelled before it completed
+     *
+     * @see #cancel(boolean)
+     */
+    public final boolean isCancelled() {
+        return mCancelled.get();
+    }
+
+    /**
+     * <p>Attempts to cancel execution of this task.  This attempt will
+     * fail if the task has already completed, already been cancelled,
+     * or could not be cancelled for some other reason. If successful,
+     * and this task has not started when <tt>cancel</tt> is called,
+     * this task should never run. If the task has already started,
+     * then the <tt>mayInterruptIfRunning</tt> parameter determines
+     * whether the thread executing this task should be interrupted in
+     * an attempt to stop the task.</p>
+     *
+     * <p>Calling this method will result in {@link #onCancelled(Object)} being
+     * invoked on the UI thread after {@link #doInBackground(Object[])}
+     * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
+     * is never invoked. After invoking this method, you should check the
+     * value returned by {@link #isCancelled()} periodically from
+     * {@link #doInBackground(Object[])} to finish the task as early as
+     * possible.</p>
+     *
+     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
+     *        task should be interrupted; otherwise, in-progress tasks are allowed
+     *        to complete.
+     *
+     * @return <tt>false</tt> if the task could not be cancelled,
+     *         typically because it has already completed normally;
+     *         <tt>true</tt> otherwise
+     *
+     * @see #isCancelled()
+     * @see #onCancelled(Object)
+     */
+    public final boolean cancel(boolean mayInterruptIfRunning) {
+        mCancelled.set(true);
+        return mFuture.cancel(mayInterruptIfRunning);
+    }
+
+    /**
+     * Waits if necessary for the computation to complete, and then
+     * retrieves its result.
+     *
+     * @return The computed result.
+     *
+     * @throws java.util.concurrent.CancellationException If the computation was cancelled.
+     * @throws java.util.concurrent.ExecutionException If the computation threw an exception.
+     * @throws InterruptedException If the current thread was interrupted
+     *         while waiting.
+     */
+    public final Result get() throws InterruptedException, ExecutionException {
+        return mFuture.get();
+    }
+
+    /**
+     * Waits if necessary for at most the given time for the computation
+     * to complete, and then retrieves its result.
+     *
+     * @param timeout Time to wait before cancelling the operation.
+     * @param unit The time unit for the timeout.
+     *
+     * @return The computed result.
+     *
+     * @throws java.util.concurrent.CancellationException If the computation was cancelled.
+     * @throws java.util.concurrent.ExecutionException If the computation threw an exception.
+     * @throws InterruptedException If the current thread was interrupted
+     *         while waiting.
+     * @throws java.util.concurrent.TimeoutException If the wait timed out.
+     */
+    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
+            ExecutionException, TimeoutException {
+        return mFuture.get(timeout, unit);
+    }
+
+    /**
+     * Executes the task with the specified parameters. The task returns
+     * itself (this) so that the caller can keep a reference to it.
+     *
+     * <p>Note: this function schedules the task on a queue for a single background
+     * thread or pool of threads depending on the platform version.  When first
+     * introduced, AsyncTasks were executed serially on a single background thread.
+     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
+     * executed on a single thread to avoid common application errors caused
+     * by parallel execution.  If you truly want parallel execution, you can use
+     * the {@link #executeOnExecutor} version of this method
+     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
+     * on its use.
+     *
+     * <p>This method must be invoked on the UI thread.
+     *
+     * @param params The parameters of the task.
+     *
+     * @return This instance of AsyncTask.
+     *
+     * @throws IllegalStateException If {@link #getStatus()} returns either
+     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+     * @see #execute(Runnable)
+     */
+    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
+        return executeOnExecutor(sDefaultExecutor, params);
+    }
+
+    /**
+     * Executes the task with the specified parameters. The task returns
+     * itself (this) so that the caller can keep a reference to it.
+     *
+     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
+     * allow multiple tasks to run in parallel on a pool of threads managed by
+     * AsyncTask, however you can also use your own {@link java.util.concurrent.Executor} for custom
+     * behavior.
+     *
+     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
+     * a thread pool is generally <em>not</em> what one wants, because the order
+     * of their operation is not defined.  For example, if these tasks are used
+     * to modify any state in common (such as writing a file due to a button click),
+     * there are no guarantees on the order of the modifications.
+     * Without careful work it is possible in rare cases for the newer version
+     * of the data to be over-written by an older one, leading to obscure data
+     * loss and stability issues.  Such changes are best
+     * executed in serial; to guarantee such work is serialized regardless of
+     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
+     *
+     * <p>This method must be invoked on the UI thread.
+     *
+     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
+     *              convenient process-wide thread pool for tasks that are loosely coupled.
+     * @param params The parameters of the task.
+     *
+     * @return This instance of AsyncTask.
+     *
+     * @throws IllegalStateException If {@link #getStatus()} returns either
+     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #execute(Object[])
+     */
+    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
+            Params... params) {
+        if (mStatus != Status.PENDING) {
+            switch (mStatus) {
+                case RUNNING:
+                    throw new IllegalStateException("Cannot execute task:"
+                            + " the task is already running.");
+                case FINISHED:
+                    throw new IllegalStateException("Cannot execute task:"
+                            + " the task has already been executed "
+                            + "(a task can be executed only once)");
+            }
+        }
+
+        mStatus = Status.RUNNING;
+
+        onPreExecute();
+
+        mWorker.mParams = params;
+        exec.execute(mFuture);
+
+        return this;
+    }
+
+    /**
+     * Convenience version of {@link #execute(Object...)} for use with
+     * a simple Runnable object. See {@link #execute(Object[])} for more
+     * information on the order of execution.
+     *
+     * @see #execute(Object[])
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+     */
+    public static void execute(Runnable runnable) {
+        sDefaultExecutor.execute(runnable);
+    }
+
+    /**
+     * This method can be invoked from {@link #doInBackground} to
+     * publish updates on the UI thread while the background computation is
+     * still running. Each call to this method will trigger the execution of
+     * {@link #onProgressUpdate} on the UI thread.
+     *
+     * {@link #onProgressUpdate} will note be called if the task has been
+     * canceled.
+     *
+     * @param values The progress values to update the UI with.
+     *
+     * @see #onProgressUpdate
+     * @see #doInBackground
+     */
+    protected final void publishProgress(Progress... values) {
+        if (!isCancelled()) {
+            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+        }
+    }
+
+    private void finish(Result result) {
+        if (isCancelled()) {
+            onCancelled(result);
+        } else {
+            onPostExecute(result);
+        }
+        mStatus = Status.FINISHED;
+    }
+
+    private static class InternalHandler extends Handler {
+        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
+            switch (msg.what) {
+                case MESSAGE_POST_RESULT:
+                    // There is only one result
+                    result.mTask.finish(result.mData[0]);
+                    break;
+                case MESSAGE_POST_PROGRESS:
+                    result.mTask.onProgressUpdate(result.mData);
+                    break;
+            }
+        }
+    }
+
+    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
+        Params[] mParams;
+    }
+
+    @SuppressWarnings({"RawUseOfParameterizedType"})
+    private static class AsyncTaskResult<Data> {
+        final AsyncTask mTask;
+        final Data[] mData;
+
+        AsyncTaskResult(AsyncTask task, Data... data) {
+            mTask = task;
+            mData = data;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.java
new file mode 100644
index 0000000..4c14345
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.java
@@ -0,0 +1,953 @@
+/*
+ * Copyright (C) 2011 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.displayingbitmaps.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ ******************************************************************************
+ * Taken from the JB source code, can be found in:
+ * libcore/luni/src/main/java/libcore/io/DiskLruCache.java
+ * or direct link:
+ * https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
+ ******************************************************************************
+ *
+ * A cache that uses a bounded amount of space on a filesystem. Each cache
+ * entry has a string key and a fixed number of values. Values are byte
+ * sequences, accessible as streams or files. Each value must be between {@code
+ * 0} and {@code Integer.MAX_VALUE} bytes in length.
+ *
+ * <p>The cache stores its data in a directory on the filesystem. This
+ * directory must be exclusive to the cache; the cache may delete or overwrite
+ * files from its directory. It is an error for multiple processes to use the
+ * same cache directory at the same time.
+ *
+ * <p>This cache limits the number of bytes that it will store on the
+ * filesystem. When the number of stored bytes exceeds the limit, the cache will
+ * remove entries in the background until the limit is satisfied. The limit is
+ * not strict: the cache may temporarily exceed it while waiting for files to be
+ * deleted. The limit does not include filesystem overhead or the cache
+ * journal so space-sensitive applications should set a conservative limit.
+ *
+ * <p>Clients call {@link #edit} to create or update the values of an entry. An
+ * entry may have only one editor at one time; if a value is not available to be
+ * edited then {@link #edit} will return null.
+ * <ul>
+ *     <li>When an entry is being <strong>created</strong> it is necessary to
+ *         supply a full set of values; the empty value should be used as a
+ *         placeholder if necessary.
+ *     <li>When an entry is being <strong>edited</strong>, it is not necessary
+ *         to supply data for every value; values default to their previous
+ *         value.
+ * </ul>
+ * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
+ * or {@link Editor#abort}. Committing is atomic: a read observes the full set
+ * of values as they were before or after the commit, but never a mix of values.
+ *
+ * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
+ * observe the value at the time that {@link #get} was called. Updates and
+ * removals after the call do not impact ongoing reads.
+ *
+ * <p>This class is tolerant of some I/O errors. If files are missing from the
+ * filesystem, the corresponding entries will be dropped from the cache. If
+ * an error occurs while writing a cache value, the edit will fail silently.
+ * Callers should handle other problems by catching {@code IOException} and
+ * responding appropriately.
+ */
+public final class DiskLruCache implements Closeable {
+    static final String JOURNAL_FILE = "journal";
+    static final String JOURNAL_FILE_TMP = "journal.tmp";
+    static final String MAGIC = "libcore.io.DiskLruCache";
+    static final String VERSION_1 = "1";
+    static final long ANY_SEQUENCE_NUMBER = -1;
+    private static final String CLEAN = "CLEAN";
+    private static final String DIRTY = "DIRTY";
+    private static final String REMOVE = "REMOVE";
+    private static final String READ = "READ";
+
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+    private static final int IO_BUFFER_SIZE = 8 * 1024;
+
+    /*
+     * This cache uses a journal file named "journal". A typical journal file
+     * looks like this:
+     *     libcore.io.DiskLruCache
+     *     1
+     *     100
+     *     2
+     *
+     *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
+     *     DIRTY 335c4c6028171cfddfbaae1a9c313c52
+     *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
+     *     REMOVE 335c4c6028171cfddfbaae1a9c313c52
+     *     DIRTY 1ab96a171faeeee38496d8b330771a7a
+     *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
+     *     READ 335c4c6028171cfddfbaae1a9c313c52
+     *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
+     *
+     * The first five lines of the journal form its header. They are the
+     * constant string "libcore.io.DiskLruCache", the disk cache's version,
+     * the application's version, the value count, and a blank line.
+     *
+     * Each of the subsequent lines in the file is a record of the state of a
+     * cache entry. Each line contains space-separated values: a state, a key,
+     * and optional state-specific values.
+     *   o DIRTY lines track that an entry is actively being created or updated.
+     *     Every successful DIRTY action should be followed by a CLEAN or REMOVE
+     *     action. DIRTY lines without a matching CLEAN or REMOVE indicate that
+     *     temporary files may need to be deleted.
+     *   o CLEAN lines track a cache entry that has been successfully published
+     *     and may be read. A publish line is followed by the lengths of each of
+     *     its values.
+     *   o READ lines track accesses for LRU.
+     *   o REMOVE lines track entries that have been deleted.
+     *
+     * The journal file is appended to as cache operations occur. The journal may
+     * occasionally be compacted by dropping redundant lines. A temporary file named
+     * "journal.tmp" will be used during compaction; that file should be deleted if
+     * it exists when the cache is opened.
+     */
+
+    private final File directory;
+    private final File journalFile;
+    private final File journalFileTmp;
+    private final int appVersion;
+    private final long maxSize;
+    private final int valueCount;
+    private long size = 0;
+    private Writer journalWriter;
+    private final LinkedHashMap<String, Entry> lruEntries
+            = new LinkedHashMap<String, Entry>(0, 0.75f, true);
+    private int redundantOpCount;
+
+    /**
+     * To differentiate between old and current snapshots, each entry is given
+     * a sequence number each time an edit is committed. A snapshot is stale if
+     * its sequence number is not equal to its entry's sequence number.
+     */
+    private long nextSequenceNumber = 0;
+
+    /* From java.util.Arrays */
+    @SuppressWarnings("unchecked")
+    private static <T> T[] copyOfRange(T[] original, int start, int end) {
+        final int originalLength = original.length; // For exception priority compatibility.
+        if (start > end) {
+            throw new IllegalArgumentException();
+        }
+        if (start < 0 || start > originalLength) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        final int resultLength = end - start;
+        final int copyLength = Math.min(resultLength, originalLength - start);
+        final T[] result = (T[]) Array
+                .newInstance(original.getClass().getComponentType(), resultLength);
+        System.arraycopy(original, start, result, 0, copyLength);
+        return result;
+    }
+
+    /**
+     * Returns the remainder of 'reader' as a string, closing it when done.
+     */
+    public static String readFully(Reader reader) throws IOException {
+        try {
+            StringWriter writer = new StringWriter();
+            char[] buffer = new char[1024];
+            int count;
+            while ((count = reader.read(buffer)) != -1) {
+                writer.write(buffer, 0, count);
+            }
+            return writer.toString();
+        } finally {
+            reader.close();
+        }
+    }
+
+    /**
+     * Returns the ASCII characters up to but not including the next "\r\n", or
+     * "\n".
+     *
+     * @throws java.io.EOFException if the stream is exhausted before the next newline
+     *     character.
+     */
+    public static String readAsciiLine(InputStream in) throws IOException {
+        // TODO: support UTF-8 here instead
+
+        StringBuilder result = new StringBuilder(80);
+        while (true) {
+            int c = in.read();
+            if (c == -1) {
+                throw new EOFException();
+            } else if (c == '\n') {
+                break;
+            }
+
+            result.append((char) c);
+        }
+        int length = result.length();
+        if (length > 0 && result.charAt(length - 1) == '\r') {
+            result.setLength(length - 1);
+        }
+        return result.toString();
+    }
+
+    /**
+     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
+     */
+    public static void closeQuietly(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
+    /**
+     * Recursively delete everything in {@code dir}.
+     */
+    // TODO: this should specify paths as Strings rather than as Files
+    public static void deleteContents(File dir) throws IOException {
+        File[] files = dir.listFiles();
+        if (files == null) {
+            throw new IllegalArgumentException("not a directory: " + dir);
+        }
+        for (File file : files) {
+            if (file.isDirectory()) {
+                deleteContents(file);
+            }
+            if (!file.delete()) {
+                throw new IOException("failed to delete file: " + file);
+            }
+        }
+    }
+
+    /** This cache uses a single background thread to evict entries. */
+    private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
+            60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+    private final Callable<Void> cleanupCallable = new Callable<Void>() {
+        @Override public Void call() throws Exception {
+            synchronized (DiskLruCache.this) {
+                if (journalWriter == null) {
+                    return null; // closed
+                }
+                trimToSize();
+                if (journalRebuildRequired()) {
+                    rebuildJournal();
+                    redundantOpCount = 0;
+                }
+            }
+            return null;
+        }
+    };
+
+    private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
+        this.directory = directory;
+        this.appVersion = appVersion;
+        this.journalFile = new File(directory, JOURNAL_FILE);
+        this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
+        this.valueCount = valueCount;
+        this.maxSize = maxSize;
+    }
+
+    /**
+     * Opens the cache in {@code directory}, creating a cache if none exists
+     * there.
+     *
+     * @param directory a writable directory
+     * @param appVersion
+     * @param valueCount the number of values per cache entry. Must be positive.
+     * @param maxSize the maximum number of bytes this cache should use to store
+     * @throws java.io.IOException if reading or writing the cache directory fails
+     */
+    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
+            throws IOException {
+        if (maxSize <= 0) {
+            throw new IllegalArgumentException("maxSize <= 0");
+        }
+        if (valueCount <= 0) {
+            throw new IllegalArgumentException("valueCount <= 0");
+        }
+
+        // prefer to pick up where we left off
+        DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+        if (cache.journalFile.exists()) {
+            try {
+                cache.readJournal();
+                cache.processJournal();
+                cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
+                        IO_BUFFER_SIZE);
+                return cache;
+            } catch (IOException journalIsCorrupt) {
+//                System.logW("DiskLruCache " + directory + " is corrupt: "
+//                        + journalIsCorrupt.getMessage() + ", removing");
+                cache.delete();
+            }
+        }
+
+        // create a new empty cache
+        directory.mkdirs();
+        cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+        cache.rebuildJournal();
+        return cache;
+    }
+
+    private void readJournal() throws IOException {
+        InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE);
+        try {
+            String magic = readAsciiLine(in);
+            String version = readAsciiLine(in);
+            String appVersionString = readAsciiLine(in);
+            String valueCountString = readAsciiLine(in);
+            String blank = readAsciiLine(in);
+            if (!MAGIC.equals(magic)
+                    || !VERSION_1.equals(version)
+                    || !Integer.toString(appVersion).equals(appVersionString)
+                    || !Integer.toString(valueCount).equals(valueCountString)
+                    || !"".equals(blank)) {
+                throw new IOException("unexpected journal header: ["
+                        + magic + ", " + version + ", " + valueCountString + ", " + blank + "]");
+            }
+
+            while (true) {
+                try {
+                    readJournalLine(readAsciiLine(in));
+                } catch (EOFException endOfJournal) {
+                    break;
+                }
+            }
+        } finally {
+            closeQuietly(in);
+        }
+    }
+
+    private void readJournalLine(String line) throws IOException {
+        String[] parts = line.split(" ");
+        if (parts.length < 2) {
+            throw new IOException("unexpected journal line: " + line);
+        }
+
+        String key = parts[1];
+        if (parts[0].equals(REMOVE) && parts.length == 2) {
+            lruEntries.remove(key);
+            return;
+        }
+
+        Entry entry = lruEntries.get(key);
+        if (entry == null) {
+            entry = new Entry(key);
+            lruEntries.put(key, entry);
+        }
+
+        if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {
+            entry.readable = true;
+            entry.currentEditor = null;
+            entry.setLengths(copyOfRange(parts, 2, parts.length));
+        } else if (parts[0].equals(DIRTY) && parts.length == 2) {
+            entry.currentEditor = new Editor(entry);
+        } else if (parts[0].equals(READ) && parts.length == 2) {
+            // this work was already done by calling lruEntries.get()
+        } else {
+            throw new IOException("unexpected journal line: " + line);
+        }
+    }
+
+    /**
+     * Computes the initial size and collects garbage as a part of opening the
+     * cache. Dirty entries are assumed to be inconsistent and will be deleted.
+     */
+    private void processJournal() throws IOException {
+        deleteIfExists(journalFileTmp);
+        for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
+            Entry entry = i.next();
+            if (entry.currentEditor == null) {
+                for (int t = 0; t < valueCount; t++) {
+                    size += entry.lengths[t];
+                }
+            } else {
+                entry.currentEditor = null;
+                for (int t = 0; t < valueCount; t++) {
+                    deleteIfExists(entry.getCleanFile(t));
+                    deleteIfExists(entry.getDirtyFile(t));
+                }
+                i.remove();
+            }
+        }
+    }
+
+    /**
+     * Creates a new journal that omits redundant information. This replaces the
+     * current journal if it exists.
+     */
+    private synchronized void rebuildJournal() throws IOException {
+        if (journalWriter != null) {
+            journalWriter.close();
+        }
+
+        Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE);
+        writer.write(MAGIC);
+        writer.write("\n");
+        writer.write(VERSION_1);
+        writer.write("\n");
+        writer.write(Integer.toString(appVersion));
+        writer.write("\n");
+        writer.write(Integer.toString(valueCount));
+        writer.write("\n");
+        writer.write("\n");
+
+        for (Entry entry : lruEntries.values()) {
+            if (entry.currentEditor != null) {
+                writer.write(DIRTY + ' ' + entry.key + '\n');
+            } else {
+                writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+            }
+        }
+
+        writer.close();
+        journalFileTmp.renameTo(journalFile);
+        journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE);
+    }
+
+    private static void deleteIfExists(File file) throws IOException {
+//        try {
+//            Libcore.os.remove(file.getPath());
+//        } catch (ErrnoException errnoException) {
+//            if (errnoException.errno != OsConstants.ENOENT) {
+//                throw errnoException.rethrowAsIOException();
+//            }
+//        }
+        if (file.exists() && !file.delete()) {
+            throw new IOException();
+        }
+    }
+
+    /**
+     * Returns a snapshot of the entry named {@code key}, or null if it doesn't
+     * exist is not currently readable. If a value is returned, it is moved to
+     * the head of the LRU queue.
+     */
+    public synchronized Snapshot get(String key) throws IOException {
+        checkNotClosed();
+        validateKey(key);
+        Entry entry = lruEntries.get(key);
+        if (entry == null) {
+            return null;
+        }
+
+        if (!entry.readable) {
+            return null;
+        }
+
+        /*
+         * Open all streams eagerly to guarantee that we see a single published
+         * snapshot. If we opened streams lazily then the streams could come
+         * from different edits.
+         */
+        InputStream[] ins = new InputStream[valueCount];
+        try {
+            for (int i = 0; i < valueCount; i++) {
+                ins[i] = new FileInputStream(entry.getCleanFile(i));
+            }
+        } catch (FileNotFoundException e) {
+            // a file must have been deleted manually!
+            return null;
+        }
+
+        redundantOpCount++;
+        journalWriter.append(READ + ' ' + key + '\n');
+        if (journalRebuildRequired()) {
+            executorService.submit(cleanupCallable);
+        }
+
+        return new Snapshot(key, entry.sequenceNumber, ins);
+    }
+
+    /**
+     * Returns an editor for the entry named {@code key}, or null if another
+     * edit is in progress.
+     */
+    public Editor edit(String key) throws IOException {
+        return edit(key, ANY_SEQUENCE_NUMBER);
+    }
+
+    private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
+        checkNotClosed();
+        validateKey(key);
+        Entry entry = lruEntries.get(key);
+        if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
+                && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
+            return null; // snapshot is stale
+        }
+        if (entry == null) {
+            entry = new Entry(key);
+            lruEntries.put(key, entry);
+        } else if (entry.currentEditor != null) {
+            return null; // another edit is in progress
+        }
+
+        Editor editor = new Editor(entry);
+        entry.currentEditor = editor;
+
+        // flush the journal before creating files to prevent file leaks
+        journalWriter.write(DIRTY + ' ' + key + '\n');
+        journalWriter.flush();
+        return editor;
+    }
+
+    /**
+     * Returns the directory where this cache stores its data.
+     */
+    public File getDirectory() {
+        return directory;
+    }
+
+    /**
+     * Returns the maximum number of bytes that this cache should use to store
+     * its data.
+     */
+    public long maxSize() {
+        return maxSize;
+    }
+
+    /**
+     * Returns the number of bytes currently being used to store the values in
+     * this cache. This may be greater than the max size if a background
+     * deletion is pending.
+     */
+    public synchronized long size() {
+        return size;
+    }
+
+    private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
+        Entry entry = editor.entry;
+        if (entry.currentEditor != editor) {
+            throw new IllegalStateException();
+        }
+
+        // if this edit is creating the entry for the first time, every index must have a value
+        if (success && !entry.readable) {
+            for (int i = 0; i < valueCount; i++) {
+                if (!entry.getDirtyFile(i).exists()) {
+                    editor.abort();
+                    throw new IllegalStateException("edit didn't create file " + i);
+                }
+            }
+        }
+
+        for (int i = 0; i < valueCount; i++) {
+            File dirty = entry.getDirtyFile(i);
+            if (success) {
+                if (dirty.exists()) {
+                    File clean = entry.getCleanFile(i);
+                    dirty.renameTo(clean);
+                    long oldLength = entry.lengths[i];
+                    long newLength = clean.length();
+                    entry.lengths[i] = newLength;
+                    size = size - oldLength + newLength;
+                }
+            } else {
+                deleteIfExists(dirty);
+            }
+        }
+
+        redundantOpCount++;
+        entry.currentEditor = null;
+        if (entry.readable | success) {
+            entry.readable = true;
+            journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+            if (success) {
+                entry.sequenceNumber = nextSequenceNumber++;
+            }
+        } else {
+            lruEntries.remove(entry.key);
+            journalWriter.write(REMOVE + ' ' + entry.key + '\n');
+        }
+
+        if (size > maxSize || journalRebuildRequired()) {
+            executorService.submit(cleanupCallable);
+        }
+    }
+
+    /**
+     * We only rebuild the journal when it will halve the size of the journal
+     * and eliminate at least 2000 ops.
+     */
+    private boolean journalRebuildRequired() {
+        final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000;
+        return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD
+                && redundantOpCount >= lruEntries.size();
+    }
+
+    /**
+     * Drops the entry for {@code key} if it exists and can be removed. Entries
+     * actively being edited cannot be removed.
+     *
+     * @return true if an entry was removed.
+     */
+    public synchronized boolean remove(String key) throws IOException {
+        checkNotClosed();
+        validateKey(key);
+        Entry entry = lruEntries.get(key);
+        if (entry == null || entry.currentEditor != null) {
+            return false;
+        }
+
+        for (int i = 0; i < valueCount; i++) {
+            File file = entry.getCleanFile(i);
+            if (!file.delete()) {
+                throw new IOException("failed to delete " + file);
+            }
+            size -= entry.lengths[i];
+            entry.lengths[i] = 0;
+        }
+
+        redundantOpCount++;
+        journalWriter.append(REMOVE + ' ' + key + '\n');
+        lruEntries.remove(key);
+
+        if (journalRebuildRequired()) {
+            executorService.submit(cleanupCallable);
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if this cache has been closed.
+     */
+    public boolean isClosed() {
+        return journalWriter == null;
+    }
+
+    private void checkNotClosed() {
+        if (journalWriter == null) {
+            throw new IllegalStateException("cache is closed");
+        }
+    }
+
+    /**
+     * Force buffered operations to the filesystem.
+     */
+    public synchronized void flush() throws IOException {
+        checkNotClosed();
+        trimToSize();
+        journalWriter.flush();
+    }
+
+    /**
+     * Closes this cache. Stored values will remain on the filesystem.
+     */
+    public synchronized void close() throws IOException {
+        if (journalWriter == null) {
+            return; // already closed
+        }
+        for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
+            if (entry.currentEditor != null) {
+                entry.currentEditor.abort();
+            }
+        }
+        trimToSize();
+        journalWriter.close();
+        journalWriter = null;
+    }
+
+    private void trimToSize() throws IOException {
+        while (size > maxSize) {
+//            Map.Entry<String, Entry> toEvict = lruEntries.eldest();
+            final Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
+            remove(toEvict.getKey());
+        }
+    }
+
+    /**
+     * Closes the cache and deletes all of its stored values. This will delete
+     * all files in the cache directory including files that weren't created by
+     * the cache.
+     */
+    public void delete() throws IOException {
+        close();
+        deleteContents(directory);
+    }
+
+    private void validateKey(String key) {
+        if (key.contains(" ") || key.contains("\n") || key.contains("\r")) {
+            throw new IllegalArgumentException(
+                    "keys must not contain spaces or newlines: \"" + key + "\"");
+        }
+    }
+
+    private static String inputStreamToString(InputStream in) throws IOException {
+        return readFully(new InputStreamReader(in, UTF_8));
+    }
+
+    /**
+     * A snapshot of the values for an entry.
+     */
+    public final class Snapshot implements Closeable {
+        private final String key;
+        private final long sequenceNumber;
+        private final InputStream[] ins;
+
+        private Snapshot(String key, long sequenceNumber, InputStream[] ins) {
+            this.key = key;
+            this.sequenceNumber = sequenceNumber;
+            this.ins = ins;
+        }
+
+        /**
+         * Returns an editor for this snapshot's entry, or null if either the
+         * entry has changed since this snapshot was created or if another edit
+         * is in progress.
+         */
+        public Editor edit() throws IOException {
+            return DiskLruCache.this.edit(key, sequenceNumber);
+        }
+
+        /**
+         * Returns the unbuffered stream with the value for {@code index}.
+         */
+        public InputStream getInputStream(int index) {
+            return ins[index];
+        }
+
+        /**
+         * Returns the string value for {@code index}.
+         */
+        public String getString(int index) throws IOException {
+            return inputStreamToString(getInputStream(index));
+        }
+
+        @Override public void close() {
+            for (InputStream in : ins) {
+                closeQuietly(in);
+            }
+        }
+    }
+
+    /**
+     * Edits the values for an entry.
+     */
+    public final class Editor {
+        private final Entry entry;
+        private boolean hasErrors;
+
+        private Editor(Entry entry) {
+            this.entry = entry;
+        }
+
+        /**
+         * Returns an unbuffered input stream to read the last committed value,
+         * or null if no value has been committed.
+         */
+        public InputStream newInputStream(int index) throws IOException {
+            synchronized (DiskLruCache.this) {
+                if (entry.currentEditor != this) {
+                    throw new IllegalStateException();
+                }
+                if (!entry.readable) {
+                    return null;
+                }
+                return new FileInputStream(entry.getCleanFile(index));
+            }
+        }
+
+        /**
+         * Returns the last committed value as a string, or null if no value
+         * has been committed.
+         */
+        public String getString(int index) throws IOException {
+            InputStream in = newInputStream(index);
+            return in != null ? inputStreamToString(in) : null;
+        }
+
+        /**
+         * Returns a new unbuffered output stream to write the value at
+         * {@code index}. If the underlying output stream encounters errors
+         * when writing to the filesystem, this edit will be aborted when
+         * {@link #commit} is called. The returned output stream does not throw
+         * IOExceptions.
+         */
+        public OutputStream newOutputStream(int index) throws IOException {
+            synchronized (DiskLruCache.this) {
+                if (entry.currentEditor != this) {
+                    throw new IllegalStateException();
+                }
+                return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
+            }
+        }
+
+        /**
+         * Sets the value at {@code index} to {@code value}.
+         */
+        public void set(int index, String value) throws IOException {
+            Writer writer = null;
+            try {
+                writer = new OutputStreamWriter(newOutputStream(index), UTF_8);
+                writer.write(value);
+            } finally {
+                closeQuietly(writer);
+            }
+        }
+
+        /**
+         * Commits this edit so it is visible to readers.  This releases the
+         * edit lock so another edit may be started on the same key.
+         */
+        public void commit() throws IOException {
+            if (hasErrors) {
+                completeEdit(this, false);
+                remove(entry.key); // the previous entry is stale
+            } else {
+                completeEdit(this, true);
+            }
+        }
+
+        /**
+         * Aborts this edit. This releases the edit lock so another edit may be
+         * started on the same key.
+         */
+        public void abort() throws IOException {
+            completeEdit(this, false);
+        }
+
+        private class FaultHidingOutputStream extends FilterOutputStream {
+            private FaultHidingOutputStream(OutputStream out) {
+                super(out);
+            }
+
+            @Override public void write(int oneByte) {
+                try {
+                    out.write(oneByte);
+                } catch (IOException e) {
+                    hasErrors = true;
+                }
+            }
+
+            @Override public void write(byte[] buffer, int offset, int length) {
+                try {
+                    out.write(buffer, offset, length);
+                } catch (IOException e) {
+                    hasErrors = true;
+                }
+            }
+
+            @Override public void close() {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    hasErrors = true;
+                }
+            }
+
+            @Override public void flush() {
+                try {
+                    out.flush();
+                } catch (IOException e) {
+                    hasErrors = true;
+                }
+            }
+        }
+    }
+
+    private final class Entry {
+        private final String key;
+
+        /** Lengths of this entry's files. */
+        private final long[] lengths;
+
+        /** True if this entry has ever been published */
+        private boolean readable;
+
+        /** The ongoing edit or null if this entry is not being edited. */
+        private Editor currentEditor;
+
+        /** The sequence number of the most recently committed edit to this entry. */
+        private long sequenceNumber;
+
+        private Entry(String key) {
+            this.key = key;
+            this.lengths = new long[valueCount];
+        }
+
+        public String getLengths() throws IOException {
+            StringBuilder result = new StringBuilder();
+            for (long size : lengths) {
+                result.append(' ').append(size);
+            }
+            return result.toString();
+        }
+
+        /**
+         * Set lengths using decimal numbers like "10123".
+         */
+        private void setLengths(String[] strings) throws IOException {
+            if (strings.length != valueCount) {
+                throw invalidLengths(strings);
+            }
+
+            try {
+                for (int i = 0; i < strings.length; i++) {
+                    lengths[i] = Long.parseLong(strings[i]);
+                }
+            } catch (NumberFormatException e) {
+                throw invalidLengths(strings);
+            }
+        }
+
+        private IOException invalidLengths(String[] strings) throws IOException {
+            throw new IOException("unexpected journal line: " + Arrays.toString(strings));
+        }
+
+        public File getCleanFile(int i) {
+            return new File(directory, key + "." + i);
+        }
+
+        public File getDirtyFile(int i) {
+            return new File(directory, key + "." + i + ".tmp");
+        }
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageCache.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageCache.java
new file mode 100644
index 0000000..41da56c
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageCache.java
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.StatFs;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.util.LruCache;
+
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.SoftReference;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * This class handles disk and memory caching of bitmaps in conjunction with the
+ * {@link ImageWorker} class and its subclasses. Use
+ * {@link ImageCache#getInstance(android.support.v4.app.FragmentManager, ImageCacheParams)} to get an instance of this
+ * class, although usually a cache should be added directly to an {@link ImageWorker} by calling
+ * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCacheParams)}.
+ */
+public class ImageCache {
+    private static final String TAG = "ImageCache";
+
+    // Default memory cache size in kilobytes
+    private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 5MB
+
+    // Default disk cache size in bytes
+    private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
+
+    // Compression settings when writing images to disk cache
+    private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
+    private static final int DEFAULT_COMPRESS_QUALITY = 70;
+    private static final int DISK_CACHE_INDEX = 0;
+
+    // Constants to easily toggle various caches
+    private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
+    private static final boolean DEFAULT_DISK_CACHE_ENABLED = true;
+    private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
+
+    private DiskLruCache mDiskLruCache;
+    private LruCache<String, BitmapDrawable> mMemoryCache;
+    private ImageCacheParams mCacheParams;
+    private final Object mDiskCacheLock = new Object();
+    private boolean mDiskCacheStarting = true;
+
+    private Set<SoftReference<Bitmap>> mReusableBitmaps;
+
+    /**
+     * Create a new ImageCache object using the specified parameters. This should not be
+     * called directly by other classes, instead use
+     * {@link ImageCache#getInstance(android.support.v4.app.FragmentManager, ImageCacheParams)} to fetch an ImageCache
+     * instance.
+     *
+     * @param cacheParams The cache parameters to use to initialize the cache
+     */
+    private ImageCache(ImageCacheParams cacheParams) {
+        init(cacheParams);
+    }
+
+    /**
+     * Return an {@link ImageCache} instance. A {@link RetainFragment} is used to retain the
+     * ImageCache object across configuration changes such as a change in device orientation.
+     *
+     * @param fragmentManager The fragment manager to use when dealing with the retained fragment.
+     * @param cacheParams The cache parameters to use if the ImageCache needs instantiation.
+     * @return An existing retained ImageCache object or a new one if one did not exist
+     */
+    public static ImageCache getInstance(
+            FragmentManager fragmentManager, ImageCacheParams cacheParams) {
+
+        // Search for, or create an instance of the non-UI RetainFragment
+        final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager);
+
+        // See if we already have an ImageCache stored in RetainFragment
+        ImageCache imageCache = (ImageCache) mRetainFragment.getObject();
+
+        // No existing ImageCache, create one and store it in RetainFragment
+        if (imageCache == null) {
+            imageCache = new ImageCache(cacheParams);
+            mRetainFragment.setObject(imageCache);
+        }
+
+        return imageCache;
+    }
+
+    /**
+     * Initialize the cache, providing all parameters.
+     *
+     * @param cacheParams The cache parameters to initialize the cache
+     */
+    private void init(ImageCacheParams cacheParams) {
+        mCacheParams = cacheParams;
+
+        //BEGIN_INCLUDE(init_memory_cache)
+        // Set up memory cache
+        if (mCacheParams.memoryCacheEnabled) {
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")");
+            }
+
+            // If we're running on Honeycomb or newer, create a set of reusable bitmaps that can be
+            // populated into the inBitmap field of BitmapFactory.Options. Note that the set is
+            // of SoftReferences which will actually not be very effective due to the garbage
+            // collector being aggressive clearing Soft/WeakReferences. A better approach
+            // would be to use a strongly references bitmaps, however this would require some
+            // balancing of memory usage between this set and the bitmap LruCache. It would also
+            // require knowledge of the expected size of the bitmaps. From Honeycomb to JellyBean
+            // the size would need to be precise, from KitKat onward the size would just need to
+            // be the upper bound (due to changes in how inBitmap can re-use bitmaps).
+            if (Utils.hasHoneycomb()) {
+                mReusableBitmaps =
+                        Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
+            }
+
+            mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
+
+                /**
+                 * Notify the removed entry that is no longer being cached
+                 */
+                @Override
+                protected void entryRemoved(boolean evicted, String key,
+                        BitmapDrawable oldValue, BitmapDrawable newValue) {
+                    if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
+                        // The removed entry is a recycling drawable, so notify it
+                        // that it has been removed from the memory cache
+                        ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
+                    } else {
+                        // The removed entry is a standard BitmapDrawable
+
+                        if (Utils.hasHoneycomb()) {
+                            // We're running on Honeycomb or later, so add the bitmap
+                            // to a SoftReference set for possible use with inBitmap later
+                            mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
+                        }
+                    }
+                }
+
+                /**
+                 * Measure item size in kilobytes rather than units which is more practical
+                 * for a bitmap cache
+                 */
+                @Override
+                protected int sizeOf(String key, BitmapDrawable value) {
+                    final int bitmapSize = getBitmapSize(value) / 1024;
+                    return bitmapSize == 0 ? 1 : bitmapSize;
+                }
+            };
+        }
+        //END_INCLUDE(init_memory_cache)
+
+        // By default the disk cache is not initialized here as it should be initialized
+        // on a separate thread due to disk access.
+        if (cacheParams.initDiskCacheOnCreate) {
+            // Set up disk cache
+            initDiskCache();
+        }
+    }
+
+    /**
+     * Initializes the disk cache.  Note that this includes disk access so this should not be
+     * executed on the main/UI thread. By default an ImageCache does not initialize the disk
+     * cache when it is created, instead you should call initDiskCache() to initialize it on a
+     * background thread.
+     */
+    public void initDiskCache() {
+        // Set up disk cache
+        synchronized (mDiskCacheLock) {
+            if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
+                File diskCacheDir = mCacheParams.diskCacheDir;
+                if (mCacheParams.diskCacheEnabled && diskCacheDir != null) {
+                    if (!diskCacheDir.exists()) {
+                        diskCacheDir.mkdirs();
+                    }
+                    if (getUsableSpace(diskCacheDir) > mCacheParams.diskCacheSize) {
+                        try {
+                            mDiskLruCache = DiskLruCache.open(
+                                    diskCacheDir, 1, 1, mCacheParams.diskCacheSize);
+                            if (BuildConfig.DEBUG) {
+                                Log.d(TAG, "Disk cache initialized");
+                            }
+                        } catch (final IOException e) {
+                            mCacheParams.diskCacheDir = null;
+                            Log.e(TAG, "initDiskCache - " + e);
+                        }
+                    }
+                }
+            }
+            mDiskCacheStarting = false;
+            mDiskCacheLock.notifyAll();
+        }
+    }
+
+    /**
+     * Adds a bitmap to both memory and disk cache.
+     * @param data Unique identifier for the bitmap to store
+     * @param value The bitmap drawable to store
+     */
+    public void addBitmapToCache(String data, BitmapDrawable value) {
+        //BEGIN_INCLUDE(add_bitmap_to_cache)
+        if (data == null || value == null) {
+            return;
+        }
+
+        // Add to memory cache
+        if (mMemoryCache != null) {
+            if (RecyclingBitmapDrawable.class.isInstance(value)) {
+                // The removed entry is a recycling drawable, so notify it
+                // that it has been added into the memory cache
+                ((RecyclingBitmapDrawable) value).setIsCached(true);
+            }
+            mMemoryCache.put(data, value);
+        }
+
+        synchronized (mDiskCacheLock) {
+            // Add to disk cache
+            if (mDiskLruCache != null) {
+                final String key = hashKeyForDisk(data);
+                OutputStream out = null;
+                try {
+                    DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
+                    if (snapshot == null) {
+                        final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
+                        if (editor != null) {
+                            out = editor.newOutputStream(DISK_CACHE_INDEX);
+                            value.getBitmap().compress(
+                                    mCacheParams.compressFormat, mCacheParams.compressQuality, out);
+                            editor.commit();
+                            out.close();
+                        }
+                    } else {
+                        snapshot.getInputStream(DISK_CACHE_INDEX).close();
+                    }
+                } catch (final IOException e) {
+                    Log.e(TAG, "addBitmapToCache - " + e);
+                } catch (Exception e) {
+                    Log.e(TAG, "addBitmapToCache - " + e);
+                } finally {
+                    try {
+                        if (out != null) {
+                            out.close();
+                        }
+                    } catch (IOException e) {}
+                }
+            }
+        }
+        //END_INCLUDE(add_bitmap_to_cache)
+    }
+
+    /**
+     * Get from memory cache.
+     *
+     * @param data Unique identifier for which item to get
+     * @return The bitmap drawable if found in cache, null otherwise
+     */
+    public BitmapDrawable getBitmapFromMemCache(String data) {
+        //BEGIN_INCLUDE(get_bitmap_from_mem_cache)
+        BitmapDrawable memValue = null;
+
+        if (mMemoryCache != null) {
+            memValue = mMemoryCache.get(data);
+        }
+
+        if (BuildConfig.DEBUG && memValue != null) {
+            Log.d(TAG, "Memory cache hit");
+        }
+
+        return memValue;
+        //END_INCLUDE(get_bitmap_from_mem_cache)
+    }
+
+    /**
+     * Get from disk cache.
+     *
+     * @param data Unique identifier for which item to get
+     * @return The bitmap if found in cache, null otherwise
+     */
+    public Bitmap getBitmapFromDiskCache(String data) {
+        //BEGIN_INCLUDE(get_bitmap_from_disk_cache)
+        final String key = hashKeyForDisk(data);
+        Bitmap bitmap = null;
+
+        synchronized (mDiskCacheLock) {
+            while (mDiskCacheStarting) {
+                try {
+                    mDiskCacheLock.wait();
+                } catch (InterruptedException e) {}
+            }
+            if (mDiskLruCache != null) {
+                InputStream inputStream = null;
+                try {
+                    final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
+                    if (snapshot != null) {
+                        if (BuildConfig.DEBUG) {
+                            Log.d(TAG, "Disk cache hit");
+                        }
+                        inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
+                        if (inputStream != null) {
+                            FileDescriptor fd = ((FileInputStream) inputStream).getFD();
+
+                            // Decode bitmap, but we don't want to sample so give
+                            // MAX_VALUE as the target dimensions
+                            bitmap = ImageResizer.decodeSampledBitmapFromDescriptor(
+                                    fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this);
+                        }
+                    }
+                } catch (final IOException e) {
+                    Log.e(TAG, "getBitmapFromDiskCache - " + e);
+                } finally {
+                    try {
+                        if (inputStream != null) {
+                            inputStream.close();
+                        }
+                    } catch (IOException e) {}
+                }
+            }
+            return bitmap;
+        }
+        //END_INCLUDE(get_bitmap_from_disk_cache)
+    }
+
+    /**
+     * @param options - BitmapFactory.Options with out* options populated
+     * @return Bitmap that case be used for inBitmap
+     */
+    protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
+        //BEGIN_INCLUDE(get_bitmap_from_reusable_set)
+        Bitmap bitmap = null;
+
+        if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
+            synchronized (mReusableBitmaps) {
+                final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps.iterator();
+                Bitmap item;
+
+                while (iterator.hasNext()) {
+                    item = iterator.next().get();
+
+                    if (null != item && item.isMutable()) {
+                        // Check to see it the item can be used for inBitmap
+                        if (canUseForInBitmap(item, options)) {
+                            bitmap = item;
+
+                            // Remove from reusable set so it can't be used again
+                            iterator.remove();
+                            break;
+                        }
+                    } else {
+                        // Remove from the set if the reference has been cleared.
+                        iterator.remove();
+                    }
+                }
+            }
+        }
+
+        return bitmap;
+        //END_INCLUDE(get_bitmap_from_reusable_set)
+    }
+
+    /**
+     * Clears both the memory and disk cache associated with this ImageCache object. Note that
+     * this includes disk access so this should not be executed on the main/UI thread.
+     */
+    public void clearCache() {
+        if (mMemoryCache != null) {
+            mMemoryCache.evictAll();
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "Memory cache cleared");
+            }
+        }
+
+        synchronized (mDiskCacheLock) {
+            mDiskCacheStarting = true;
+            if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
+                try {
+                    mDiskLruCache.delete();
+                    if (BuildConfig.DEBUG) {
+                        Log.d(TAG, "Disk cache cleared");
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "clearCache - " + e);
+                }
+                mDiskLruCache = null;
+                initDiskCache();
+            }
+        }
+    }
+
+    /**
+     * Flushes the disk cache associated with this ImageCache object. Note that this includes
+     * disk access so this should not be executed on the main/UI thread.
+     */
+    public void flush() {
+        synchronized (mDiskCacheLock) {
+            if (mDiskLruCache != null) {
+                try {
+                    mDiskLruCache.flush();
+                    if (BuildConfig.DEBUG) {
+                        Log.d(TAG, "Disk cache flushed");
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "flush - " + e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Closes the disk cache associated with this ImageCache object. Note that this includes
+     * disk access so this should not be executed on the main/UI thread.
+     */
+    public void close() {
+        synchronized (mDiskCacheLock) {
+            if (mDiskLruCache != null) {
+                try {
+                    if (!mDiskLruCache.isClosed()) {
+                        mDiskLruCache.close();
+                        mDiskLruCache = null;
+                        if (BuildConfig.DEBUG) {
+                            Log.d(TAG, "Disk cache closed");
+                        }
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "close - " + e);
+                }
+            }
+        }
+    }
+
+    /**
+     * A holder class that contains cache parameters.
+     */
+    public static class ImageCacheParams {
+        public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
+        public int diskCacheSize = DEFAULT_DISK_CACHE_SIZE;
+        public File diskCacheDir;
+        public CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
+        public int compressQuality = DEFAULT_COMPRESS_QUALITY;
+        public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
+        public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED;
+        public boolean initDiskCacheOnCreate = DEFAULT_INIT_DISK_CACHE_ON_CREATE;
+
+        /**
+         * Create a set of image cache parameters that can be provided to
+         * {@link ImageCache#getInstance(android.support.v4.app.FragmentManager, ImageCacheParams)} or
+         * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCacheParams)}.
+         * @param context A context to use.
+         * @param diskCacheDirectoryName A unique subdirectory name that will be appended to the
+         *                               application cache directory. Usually "cache" or "images"
+         *                               is sufficient.
+         */
+        public ImageCacheParams(Context context, String diskCacheDirectoryName) {
+            diskCacheDir = getDiskCacheDir(context, diskCacheDirectoryName);
+        }
+
+        /**
+         * Sets the memory cache size based on a percentage of the max available VM memory.
+         * Eg. setting percent to 0.2 would set the memory cache to one fifth of the available
+         * memory. Throws {@link IllegalArgumentException} if percent is < 0.01 or > .8.
+         * memCacheSize is stored in kilobytes instead of bytes as this will eventually be passed
+         * to construct a LruCache which takes an int in its constructor.
+         *
+         * This value should be chosen carefully based on a number of factors
+         * Refer to the corresponding Android Training class for more discussion:
+         * http://developer.android.com/training/displaying-bitmaps/
+         *
+         * @param percent Percent of available app memory to use to size memory cache
+         */
+        public void setMemCacheSizePercent(float percent) {
+            if (percent < 0.01f || percent > 0.8f) {
+                throw new IllegalArgumentException("setMemCacheSizePercent - percent must be "
+                        + "between 0.01 and 0.8 (inclusive)");
+            }
+            memCacheSize = Math.round(percent * Runtime.getRuntime().maxMemory() / 1024);
+        }
+    }
+
+    /**
+     * @param candidate - Bitmap to check
+     * @param targetOptions - Options that have the out* value populated
+     * @return true if <code>candidate</code> can be used for inBitmap re-use with
+     *      <code>targetOptions</code>
+     */
+    @TargetApi(VERSION_CODES.KITKAT)
+    private static boolean canUseForInBitmap(
+            Bitmap candidate, BitmapFactory.Options targetOptions) {
+        //BEGIN_INCLUDE(can_use_for_inbitmap)
+        if (!Utils.hasKitKat()) {
+            // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
+            return candidate.getWidth() == targetOptions.outWidth
+                    && candidate.getHeight() == targetOptions.outHeight
+                    && targetOptions.inSampleSize == 1;
+        }
+
+        // From Android 4.4 (KitKat) onward we can re-use if the byte size of the new bitmap
+        // is smaller than the reusable bitmap candidate allocation byte count.
+        int width = targetOptions.outWidth / targetOptions.inSampleSize;
+        int height = targetOptions.outHeight / targetOptions.inSampleSize;
+        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
+        return byteCount <= candidate.getAllocationByteCount();
+        //END_INCLUDE(can_use_for_inbitmap)
+    }
+
+    /**
+     * Return the byte usage per pixel of a bitmap based on its configuration.
+     * @param config The bitmap configuration.
+     * @return The byte usage per pixel.
+     */
+    private static int getBytesPerPixel(Config config) {
+        if (config == Config.ARGB_8888) {
+            return 4;
+        } else if (config == Config.RGB_565) {
+            return 2;
+        } else if (config == Config.ARGB_4444) {
+            return 2;
+        } else if (config == Config.ALPHA_8) {
+            return 1;
+        }
+        return 1;
+    }
+
+    /**
+     * Get a usable cache directory (external if available, internal otherwise).
+     *
+     * @param context The context to use
+     * @param uniqueName A unique directory name to append to the cache dir
+     * @return The cache dir
+     */
+    public static File getDiskCacheDir(Context context, String uniqueName) {
+        // Check if media is mounted or storage is built-in, if so, try and use external cache dir
+        // otherwise use internal cache dir
+        final String cachePath =
+                Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
+                        !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
+                                context.getCacheDir().getPath();
+
+        return new File(cachePath + File.separator + uniqueName);
+    }
+
+    /**
+     * A hashing method that changes a string (like a URL) into a hash suitable for using as a
+     * disk filename.
+     */
+    public static String hashKeyForDisk(String key) {
+        String cacheKey;
+        try {
+            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
+            mDigest.update(key.getBytes());
+            cacheKey = bytesToHexString(mDigest.digest());
+        } catch (NoSuchAlgorithmException e) {
+            cacheKey = String.valueOf(key.hashCode());
+        }
+        return cacheKey;
+    }
+
+    private static String bytesToHexString(byte[] bytes) {
+        // http://stackoverflow.com/questions/332079
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(0xFF & bytes[i]);
+            if (hex.length() == 1) {
+                sb.append('0');
+            }
+            sb.append(hex);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Get the size in bytes of a bitmap in a BitmapDrawable. Note that from Android 4.4 (KitKat)
+     * onward this returns the allocated memory size of the bitmap which can be larger than the
+     * actual bitmap data byte count (in the case it was re-used).
+     *
+     * @param value
+     * @return size in bytes
+     */
+    @TargetApi(VERSION_CODES.KITKAT)
+    public static int getBitmapSize(BitmapDrawable value) {
+        Bitmap bitmap = value.getBitmap();
+
+        // From KitKat onward use getAllocationByteCount() as allocated bytes can potentially be
+        // larger than bitmap byte count.
+        if (Utils.hasKitKat()) {
+            return bitmap.getAllocationByteCount();
+        }
+
+        if (Utils.hasHoneycombMR1()) {
+            return bitmap.getByteCount();
+        }
+
+        // Pre HC-MR1
+        return bitmap.getRowBytes() * bitmap.getHeight();
+    }
+
+    /**
+     * Check if external storage is built-in or removable.
+     *
+     * @return True if external storage is removable (like an SD card), false
+     *         otherwise.
+     */
+    @TargetApi(VERSION_CODES.GINGERBREAD)
+    public static boolean isExternalStorageRemovable() {
+        if (Utils.hasGingerbread()) {
+            return Environment.isExternalStorageRemovable();
+        }
+        return true;
+    }
+
+    /**
+     * Get the external app cache directory.
+     *
+     * @param context The context to use
+     * @return The external cache dir
+     */
+    @TargetApi(VERSION_CODES.FROYO)
+    public static File getExternalCacheDir(Context context) {
+        if (Utils.hasFroyo()) {
+            return context.getExternalCacheDir();
+        }
+
+        // Before Froyo we need to construct the external cache dir ourselves
+        final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
+        return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
+    }
+
+    /**
+     * Check how much usable space is available at a given path.
+     *
+     * @param path The path to check
+     * @return The space available in bytes
+     */
+    @TargetApi(VERSION_CODES.GINGERBREAD)
+    public static long getUsableSpace(File path) {
+        if (Utils.hasGingerbread()) {
+            return path.getUsableSpace();
+        }
+        final StatFs stats = new StatFs(path.getPath());
+        return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
+    }
+
+    /**
+     * Locate an existing instance of this Fragment or if not found, create and
+     * add it using FragmentManager.
+     *
+     * @param fm The FragmentManager manager to use.
+     * @return The existing instance of the Fragment or the new instance if just
+     *         created.
+     */
+    private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
+        //BEGIN_INCLUDE(find_create_retain_fragment)
+        // Check to see if we have retained the worker fragment.
+        RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(TAG);
+
+        // If not retained (or first time running), we need to create and add it.
+        if (mRetainFragment == null) {
+            mRetainFragment = new RetainFragment();
+            fm.beginTransaction().add(mRetainFragment, TAG).commitAllowingStateLoss();
+        }
+
+        return mRetainFragment;
+        //END_INCLUDE(find_create_retain_fragment)
+    }
+
+    /**
+     * A simple non-UI Fragment that stores a single Object and is retained over configuration
+     * changes. It will be used to retain the ImageCache object.
+     */
+    public static class RetainFragment extends Fragment {
+        private Object mObject;
+
+        /**
+         * Empty constructor as per the Fragment documentation
+         */
+        public RetainFragment() {}
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            // Make sure this Fragment is retained over a configuration change
+            setRetainInstance(true);
+        }
+
+        /**
+         * Store a single object in this Fragment.
+         *
+         * @param object The object to store
+         */
+        public void setObject(Object object) {
+            mObject = object;
+        }
+
+        /**
+         * Get the stored object.
+         *
+         * @return The stored object
+         */
+        public Object getObject() {
+            return mObject;
+        }
+    }
+
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageFetcher.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageFetcher.java
new file mode 100644
index 0000000..b9ccb68
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageFetcher.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Build;
+import android.widget.Toast;
+
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+import com.example.android.displayingbitmaps.R;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * A simple subclass of {@link ImageResizer} that fetches and resizes images fetched from a URL.
+ */
+public class ImageFetcher extends ImageResizer {
+    private static final String TAG = "ImageFetcher";
+    private static final int HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10MB
+    private static final String HTTP_CACHE_DIR = "http";
+    private static final int IO_BUFFER_SIZE = 8 * 1024;
+
+    private DiskLruCache mHttpDiskCache;
+    private File mHttpCacheDir;
+    private boolean mHttpDiskCacheStarting = true;
+    private final Object mHttpDiskCacheLock = new Object();
+    private static final int DISK_CACHE_INDEX = 0;
+
+    /**
+     * Initialize providing a target image width and height for the processing images.
+     *
+     * @param context
+     * @param imageWidth
+     * @param imageHeight
+     */
+    public ImageFetcher(Context context, int imageWidth, int imageHeight) {
+        super(context, imageWidth, imageHeight);
+        init(context);
+    }
+
+    /**
+     * Initialize providing a single target image size (used for both width and height);
+     *
+     * @param context
+     * @param imageSize
+     */
+    public ImageFetcher(Context context, int imageSize) {
+        super(context, imageSize);
+        init(context);
+    }
+
+    private void init(Context context) {
+        checkConnection(context);
+        mHttpCacheDir = ImageCache.getDiskCacheDir(context, HTTP_CACHE_DIR);
+    }
+
+    @Override
+    protected void initDiskCacheInternal() {
+        super.initDiskCacheInternal();
+        initHttpDiskCache();
+    }
+
+    private void initHttpDiskCache() {
+        if (!mHttpCacheDir.exists()) {
+            mHttpCacheDir.mkdirs();
+        }
+        synchronized (mHttpDiskCacheLock) {
+            if (ImageCache.getUsableSpace(mHttpCacheDir) > HTTP_CACHE_SIZE) {
+                try {
+                    mHttpDiskCache = DiskLruCache.open(mHttpCacheDir, 1, 1, HTTP_CACHE_SIZE);
+                    if (BuildConfig.DEBUG) {
+                        Log.d(TAG, "HTTP cache initialized");
+                    }
+                } catch (IOException e) {
+                    mHttpDiskCache = null;
+                }
+            }
+            mHttpDiskCacheStarting = false;
+            mHttpDiskCacheLock.notifyAll();
+        }
+    }
+
+    @Override
+    protected void clearCacheInternal() {
+        super.clearCacheInternal();
+        synchronized (mHttpDiskCacheLock) {
+            if (mHttpDiskCache != null && !mHttpDiskCache.isClosed()) {
+                try {
+                    mHttpDiskCache.delete();
+                    if (BuildConfig.DEBUG) {
+                        Log.d(TAG, "HTTP cache cleared");
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "clearCacheInternal - " + e);
+                }
+                mHttpDiskCache = null;
+                mHttpDiskCacheStarting = true;
+                initHttpDiskCache();
+            }
+        }
+    }
+
+    @Override
+    protected void flushCacheInternal() {
+        super.flushCacheInternal();
+        synchronized (mHttpDiskCacheLock) {
+            if (mHttpDiskCache != null) {
+                try {
+                    mHttpDiskCache.flush();
+                    if (BuildConfig.DEBUG) {
+                        Log.d(TAG, "HTTP cache flushed");
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "flush - " + e);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void closeCacheInternal() {
+        super.closeCacheInternal();
+        synchronized (mHttpDiskCacheLock) {
+            if (mHttpDiskCache != null) {
+                try {
+                    if (!mHttpDiskCache.isClosed()) {
+                        mHttpDiskCache.close();
+                        mHttpDiskCache = null;
+                        if (BuildConfig.DEBUG) {
+                            Log.d(TAG, "HTTP cache closed");
+                        }
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "closeCacheInternal - " + e);
+                }
+            }
+        }
+    }
+
+    /**
+    * Simple network connection check.
+    *
+    * @param context
+    */
+    private void checkConnection(Context context) {
+        final ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+        if (networkInfo == null || !networkInfo.isConnectedOrConnecting()) {
+            Toast.makeText(context, R.string.no_network_connection_toast, Toast.LENGTH_LONG).show();
+            Log.e(TAG, "checkConnection - no connection found");
+        }
+    }
+
+    /**
+     * The main process method, which will be called by the ImageWorker in the AsyncTask background
+     * thread.
+     *
+     * @param data The data to load the bitmap, in this case, a regular http URL
+     * @return The downloaded and resized bitmap
+     */
+    private Bitmap processBitmap(String data) {
+        if (BuildConfig.DEBUG) {
+            Log.d(TAG, "processBitmap - " + data);
+        }
+
+        final String key = ImageCache.hashKeyForDisk(data);
+        FileDescriptor fileDescriptor = null;
+        FileInputStream fileInputStream = null;
+        DiskLruCache.Snapshot snapshot;
+        synchronized (mHttpDiskCacheLock) {
+            // Wait for disk cache to initialize
+            while (mHttpDiskCacheStarting) {
+                try {
+                    mHttpDiskCacheLock.wait();
+                } catch (InterruptedException e) {}
+            }
+
+            if (mHttpDiskCache != null) {
+                try {
+                    snapshot = mHttpDiskCache.get(key);
+                    if (snapshot == null) {
+                        if (BuildConfig.DEBUG) {
+                            Log.d(TAG, "processBitmap, not found in http cache, downloading...");
+                        }
+                        DiskLruCache.Editor editor = mHttpDiskCache.edit(key);
+                        if (editor != null) {
+                            if (downloadUrlToStream(data,
+                                    editor.newOutputStream(DISK_CACHE_INDEX))) {
+                                editor.commit();
+                            } else {
+                                editor.abort();
+                            }
+                        }
+                        snapshot = mHttpDiskCache.get(key);
+                    }
+                    if (snapshot != null) {
+                        fileInputStream =
+                                (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
+                        fileDescriptor = fileInputStream.getFD();
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "processBitmap - " + e);
+                } catch (IllegalStateException e) {
+                    Log.e(TAG, "processBitmap - " + e);
+                } finally {
+                    if (fileDescriptor == null && fileInputStream != null) {
+                        try {
+                            fileInputStream.close();
+                        } catch (IOException e) {}
+                    }
+                }
+            }
+        }
+
+        Bitmap bitmap = null;
+        if (fileDescriptor != null) {
+            bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth,
+                    mImageHeight, getImageCache());
+        }
+        if (fileInputStream != null) {
+            try {
+                fileInputStream.close();
+            } catch (IOException e) {}
+        }
+        return bitmap;
+    }
+
+    @Override
+    protected Bitmap processBitmap(Object data) {
+        return processBitmap(String.valueOf(data));
+    }
+
+    /**
+     * Download a bitmap from a URL and write the content to an output stream.
+     *
+     * @param urlString The URL to fetch
+     * @return true if successful, false otherwise
+     */
+    public boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
+        disableConnectionReuseIfNecessary();
+        HttpURLConnection urlConnection = null;
+        BufferedOutputStream out = null;
+        BufferedInputStream in = null;
+
+        try {
+            final URL url = new URL(urlString);
+            urlConnection = (HttpURLConnection) url.openConnection();
+            in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
+            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
+
+            int b;
+            while ((b = in.read()) != -1) {
+                out.write(b);
+            }
+            return true;
+        } catch (final IOException e) {
+            Log.e(TAG, "Error in downloadBitmap - " + e);
+        } finally {
+            if (urlConnection != null) {
+                urlConnection.disconnect();
+            }
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (final IOException e) {}
+        }
+        return false;
+    }
+
+    /**
+     * Workaround for bug pre-Froyo, see here for more info:
+     * http://android-developers.blogspot.com/2011/09/androids-http-clients.html
+     */
+    public static void disableConnectionReuseIfNecessary() {
+        // HTTP connection reuse which was buggy pre-froyo
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
+            System.setProperty("http.keepAlive", "false");
+        }
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageResizer.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageResizer.java
new file mode 100644
index 0000000..be91096
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageResizer.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+
+import java.io.FileDescriptor;
+
+/**
+ * A simple subclass of {@link ImageWorker} that resizes images from resources given a target width
+ * and height. Useful for when the input images might be too large to simply load directly into
+ * memory.
+ */
+public class ImageResizer extends ImageWorker {
+    private static final String TAG = "ImageResizer";
+    protected int mImageWidth;
+    protected int mImageHeight;
+
+    /**
+     * Initialize providing a single target image size (used for both width and height);
+     *
+     * @param context
+     * @param imageWidth
+     * @param imageHeight
+     */
+    public ImageResizer(Context context, int imageWidth, int imageHeight) {
+        super(context);
+        setImageSize(imageWidth, imageHeight);
+    }
+
+    /**
+     * Initialize providing a single target image size (used for both width and height);
+     *
+     * @param context
+     * @param imageSize
+     */
+    public ImageResizer(Context context, int imageSize) {
+        super(context);
+        setImageSize(imageSize);
+    }
+
+    /**
+     * Set the target image width and height.
+     *
+     * @param width
+     * @param height
+     */
+    public void setImageSize(int width, int height) {
+        mImageWidth = width;
+        mImageHeight = height;
+    }
+
+    /**
+     * Set the target image size (width and height will be the same).
+     *
+     * @param size
+     */
+    public void setImageSize(int size) {
+        setImageSize(size, size);
+    }
+
+    /**
+     * The main processing method. This happens in a background task. In this case we are just
+     * sampling down the bitmap and returning it from a resource.
+     *
+     * @param resId
+     * @return
+     */
+    private Bitmap processBitmap(int resId) {
+        if (BuildConfig.DEBUG) {
+            Log.d(TAG, "processBitmap - " + resId);
+        }
+        return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,
+                mImageHeight, getImageCache());
+    }
+
+    @Override
+    protected Bitmap processBitmap(Object data) {
+        return processBitmap(Integer.parseInt(String.valueOf(data)));
+    }
+
+    /**
+     * Decode and sample down a bitmap from resources to the requested width and height.
+     *
+     * @param res The resources object containing the image data
+     * @param resId The resource id of the image data
+     * @param reqWidth The requested width of the resulting bitmap
+     * @param reqHeight The requested height of the resulting bitmap
+     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
+     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
+     *         that are equal to or greater than the requested width and height
+     */
+    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
+            int reqWidth, int reqHeight, ImageCache cache) {
+
+        // BEGIN_INCLUDE (read_bitmap_dimensions)
+        // First decode with inJustDecodeBounds=true to check dimensions
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeResource(res, resId, options);
+
+        // Calculate inSampleSize
+        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+        // END_INCLUDE (read_bitmap_dimensions)
+
+        // If we're running on Honeycomb or newer, try to use inBitmap
+        if (Utils.hasHoneycomb()) {
+            addInBitmapOptions(options, cache);
+        }
+
+        // Decode bitmap with inSampleSize set
+        options.inJustDecodeBounds = false;
+        return BitmapFactory.decodeResource(res, resId, options);
+    }
+
+    /**
+     * Decode and sample down a bitmap from a file to the requested width and height.
+     *
+     * @param filename The full path of the file to decode
+     * @param reqWidth The requested width of the resulting bitmap
+     * @param reqHeight The requested height of the resulting bitmap
+     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
+     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
+     *         that are equal to or greater than the requested width and height
+     */
+    public static Bitmap decodeSampledBitmapFromFile(String filename,
+            int reqWidth, int reqHeight, ImageCache cache) {
+
+        // First decode with inJustDecodeBounds=true to check dimensions
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(filename, options);
+
+        // Calculate inSampleSize
+        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+        // If we're running on Honeycomb or newer, try to use inBitmap
+        if (Utils.hasHoneycomb()) {
+            addInBitmapOptions(options, cache);
+        }
+
+        // Decode bitmap with inSampleSize set
+        options.inJustDecodeBounds = false;
+        return BitmapFactory.decodeFile(filename, options);
+    }
+
+    /**
+     * Decode and sample down a bitmap from a file input stream to the requested width and height.
+     *
+     * @param fileDescriptor The file descriptor to read from
+     * @param reqWidth The requested width of the resulting bitmap
+     * @param reqHeight The requested height of the resulting bitmap
+     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
+     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
+     *         that are equal to or greater than the requested width and height
+     */
+    public static Bitmap decodeSampledBitmapFromDescriptor(
+            FileDescriptor fileDescriptor, int reqWidth, int reqHeight, ImageCache cache) {
+
+        // First decode with inJustDecodeBounds=true to check dimensions
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
+
+        // Calculate inSampleSize
+        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+        // Decode bitmap with inSampleSize set
+        options.inJustDecodeBounds = false;
+
+        // If we're running on Honeycomb or newer, try to use inBitmap
+        if (Utils.hasHoneycomb()) {
+            addInBitmapOptions(options, cache);
+        }
+
+        return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
+        //BEGIN_INCLUDE(add_bitmap_options)
+        // inBitmap only works with mutable bitmaps so force the decoder to
+        // return mutable bitmaps.
+        options.inMutable = true;
+
+        if (cache != null) {
+            // Try and find a bitmap to use for inBitmap
+            Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
+
+            if (inBitmap != null) {
+                options.inBitmap = inBitmap;
+            }
+        }
+        //END_INCLUDE(add_bitmap_options)
+    }
+
+    /**
+     * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
+     * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
+     * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
+     * having a width and height equal to or larger than the requested width and height.
+     *
+     * @param options An options object with out* params already populated (run through a decode*
+     *            method with inJustDecodeBounds==true
+     * @param reqWidth The requested width of the resulting bitmap
+     * @param reqHeight The requested height of the resulting bitmap
+     * @return The value to be used for inSampleSize
+     */
+    public static int calculateInSampleSize(BitmapFactory.Options options,
+            int reqWidth, int reqHeight) {
+        // BEGIN_INCLUDE (calculate_sample_size)
+        // Raw height and width of image
+        final int height = options.outHeight;
+        final int width = options.outWidth;
+        int inSampleSize = 1;
+
+        if (height > reqHeight || width > reqWidth) {
+
+            final int halfHeight = height / 2;
+            final int halfWidth = width / 2;
+
+            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+            // height and width larger than the requested height and width.
+            while ((halfHeight / inSampleSize) > reqHeight
+                    && (halfWidth / inSampleSize) > reqWidth) {
+                inSampleSize *= 2;
+            }
+
+            // This offers some additional logic in case the image has a strange
+            // aspect ratio. For example, a panorama may have a much larger
+            // width than height. In these cases the total pixels might still
+            // end up being too large to fit comfortably in memory, so we should
+            // be more aggressive with sample down the image (=larger inSampleSize).
+
+            long totalPixels = width * height / inSampleSize;
+
+            // Anything more than 2x the requested pixels we'll sample down further
+            final long totalReqPixelsCap = reqWidth * reqHeight * 2;
+
+            while (totalPixels > totalReqPixelsCap) {
+                inSampleSize *= 2;
+                totalPixels /= 2;
+            }
+        }
+        return inSampleSize;
+        // END_INCLUDE (calculate_sample_size)
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageWorker.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageWorker.java
new file mode 100644
index 0000000..f44d00d
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageWorker.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.widget.ImageView;
+
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class wraps up completing some arbitrary long running work when loading a bitmap to an
+ * ImageView. It handles things like using a memory and disk cache, running the work in a background
+ * thread and setting a placeholder image.
+ */
+public abstract class ImageWorker {
+    private static final String TAG = "ImageWorker";
+    private static final int FADE_IN_TIME = 200;
+
+    private ImageCache mImageCache;
+    private ImageCache.ImageCacheParams mImageCacheParams;
+    private Bitmap mLoadingBitmap;
+    private boolean mFadeInBitmap = true;
+    private boolean mExitTasksEarly = false;
+    protected boolean mPauseWork = false;
+    private final Object mPauseWorkLock = new Object();
+
+    protected Resources mResources;
+
+    private static final int MESSAGE_CLEAR = 0;
+    private static final int MESSAGE_INIT_DISK_CACHE = 1;
+    private static final int MESSAGE_FLUSH = 2;
+    private static final int MESSAGE_CLOSE = 3;
+
+    protected ImageWorker(Context context) {
+        mResources = context.getResources();
+    }
+
+    /**
+     * Load an image specified by the data parameter into an ImageView (override
+     * {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
+     * disk cache will be used if an {@link ImageCache} has been added using
+     * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCache.ImageCacheParams)}. If the
+     * image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
+     * will be created to asynchronously load the bitmap.
+     *
+     * @param data The URL of the image to download.
+     * @param imageView The ImageView to bind the downloaded image to.
+     */
+    public void loadImage(Object data, ImageView imageView) {
+        if (data == null) {
+            return;
+        }
+
+        BitmapDrawable value = null;
+
+        if (mImageCache != null) {
+            value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
+        }
+
+        if (value != null) {
+            // Bitmap found in memory cache
+            imageView.setImageDrawable(value);
+        } else if (cancelPotentialWork(data, imageView)) {
+            //BEGIN_INCLUDE(execute_background_task)
+            final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
+            final AsyncDrawable asyncDrawable =
+                    new AsyncDrawable(mResources, mLoadingBitmap, task);
+            imageView.setImageDrawable(asyncDrawable);
+
+            // NOTE: This uses a custom version of AsyncTask that has been pulled from the
+            // framework and slightly modified. Refer to the docs at the top of the class
+            // for more info on what was changed.
+            task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR);
+            //END_INCLUDE(execute_background_task)
+        }
+    }
+
+    /**
+     * Set placeholder bitmap that shows when the the background thread is running.
+     *
+     * @param bitmap
+     */
+    public void setLoadingImage(Bitmap bitmap) {
+        mLoadingBitmap = bitmap;
+    }
+
+    /**
+     * Set placeholder bitmap that shows when the the background thread is running.
+     *
+     * @param resId
+     */
+    public void setLoadingImage(int resId) {
+        mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId);
+    }
+
+    /**
+     * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap
+     * caching.
+     * @param fragmentManager
+     * @param cacheParams The cache parameters to use for the image cache.
+     */
+    public void addImageCache(FragmentManager fragmentManager,
+            ImageCache.ImageCacheParams cacheParams) {
+        mImageCacheParams = cacheParams;
+        mImageCache = ImageCache.getInstance(fragmentManager, mImageCacheParams);
+        new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
+    }
+
+    /**
+     * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap
+     * caching.
+     * @param activity
+     * @param diskCacheDirectoryName See
+     * {@link ImageCache.ImageCacheParams#ImageCacheParams(android.content.Context, String)}.
+     */
+    public void addImageCache(FragmentActivity activity, String diskCacheDirectoryName) {
+        mImageCacheParams = new ImageCache.ImageCacheParams(activity, diskCacheDirectoryName);
+        mImageCache = ImageCache.getInstance(activity.getSupportFragmentManager(), mImageCacheParams);
+        new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
+    }
+
+    /**
+     * If set to true, the image will fade-in once it has been loaded by the background thread.
+     */
+    public void setImageFadeIn(boolean fadeIn) {
+        mFadeInBitmap = fadeIn;
+    }
+
+    public void setExitTasksEarly(boolean exitTasksEarly) {
+        mExitTasksEarly = exitTasksEarly;
+        setPauseWork(false);
+    }
+
+    /**
+     * Subclasses should override this to define any processing or work that must happen to produce
+     * the final bitmap. This will be executed in a background thread and be long running. For
+     * example, you could resize a large bitmap here, or pull down an image from the network.
+     *
+     * @param data The data to identify which image to process, as provided by
+     *            {@link ImageWorker#loadImage(Object, android.widget.ImageView)}
+     * @return The processed bitmap
+     */
+    protected abstract Bitmap processBitmap(Object data);
+
+    /**
+     * @return The {@link ImageCache} object currently being used by this ImageWorker.
+     */
+    protected ImageCache getImageCache() {
+        return mImageCache;
+    }
+
+    /**
+     * Cancels any pending work attached to the provided ImageView.
+     * @param imageView
+     */
+    public static void cancelWork(ImageView imageView) {
+        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+        if (bitmapWorkerTask != null) {
+            bitmapWorkerTask.cancel(true);
+            if (BuildConfig.DEBUG) {
+                final Object bitmapData = bitmapWorkerTask.mData;
+                Log.d(TAG, "cancelWork - cancelled work for " + bitmapData);
+            }
+        }
+    }
+
+    /**
+     * Returns true if the current work has been canceled or if there was no work in
+     * progress on this image view.
+     * Returns false if the work in progress deals with the same data. The work is not
+     * stopped in that case.
+     */
+    public static boolean cancelPotentialWork(Object data, ImageView imageView) {
+        //BEGIN_INCLUDE(cancel_potential_work)
+        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+        if (bitmapWorkerTask != null) {
+            final Object bitmapData = bitmapWorkerTask.mData;
+            if (bitmapData == null || !bitmapData.equals(data)) {
+                bitmapWorkerTask.cancel(true);
+                if (BuildConfig.DEBUG) {
+                    Log.d(TAG, "cancelPotentialWork - cancelled work for " + data);
+                }
+            } else {
+                // The same work is already in progress.
+                return false;
+            }
+        }
+        return true;
+        //END_INCLUDE(cancel_potential_work)
+    }
+
+    /**
+     * @param imageView Any imageView
+     * @return Retrieve the currently active work task (if any) associated with this imageView.
+     * null if there is no such task.
+     */
+    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+        if (imageView != null) {
+            final Drawable drawable = imageView.getDrawable();
+            if (drawable instanceof AsyncDrawable) {
+                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+                return asyncDrawable.getBitmapWorkerTask();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The actual AsyncTask that will asynchronously process the image.
+     */
+    private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
+        private Object mData;
+        private final WeakReference<ImageView> imageViewReference;
+
+        public BitmapWorkerTask(Object data, ImageView imageView) {
+            mData = data;
+            imageViewReference = new WeakReference<ImageView>(imageView);
+        }
+
+        /**
+         * Background processing.
+         */
+        @Override
+        protected BitmapDrawable doInBackground(Void... params) {
+            //BEGIN_INCLUDE(load_bitmap_in_background)
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "doInBackground - starting work");
+            }
+
+            final String dataString = String.valueOf(mData);
+            Bitmap bitmap = null;
+            BitmapDrawable drawable = null;
+
+            // Wait here if work is paused and the task is not cancelled
+            synchronized (mPauseWorkLock) {
+                while (mPauseWork && !isCancelled()) {
+                    try {
+                        mPauseWorkLock.wait();
+                    } catch (InterruptedException e) {}
+                }
+            }
+
+            // If the image cache is available and this task has not been cancelled by another
+            // thread and the ImageView that was originally bound to this task is still bound back
+            // to this task and our "exit early" flag is not set then try and fetch the bitmap from
+            // the cache
+            if (mImageCache != null && !isCancelled() && getAttachedImageView() != null
+                    && !mExitTasksEarly) {
+                bitmap = mImageCache.getBitmapFromDiskCache(dataString);
+            }
+
+            // If the bitmap was not found in the cache and this task has not been cancelled by
+            // another thread and the ImageView that was originally bound to this task is still
+            // bound back to this task and our "exit early" flag is not set, then call the main
+            // process method (as implemented by a subclass)
+            if (bitmap == null && !isCancelled() && getAttachedImageView() != null
+                    && !mExitTasksEarly) {
+                bitmap = processBitmap(mData);
+            }
+
+            // If the bitmap was processed and the image cache is available, then add the processed
+            // bitmap to the cache for future use. Note we don't check if the task was cancelled
+            // here, if it was, and the thread is still running, we may as well add the processed
+            // bitmap to our cache as it might be used again in the future
+            if (bitmap != null) {
+                if (Utils.hasHoneycomb()) {
+                    // Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
+                    drawable = new BitmapDrawable(mResources, bitmap);
+                } else {
+                    // Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
+                    // which will recycle automagically
+                    drawable = new RecyclingBitmapDrawable(mResources, bitmap);
+                }
+
+                if (mImageCache != null) {
+                    mImageCache.addBitmapToCache(dataString, drawable);
+                }
+            }
+
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "doInBackground - finished work");
+            }
+
+            return drawable;
+            //END_INCLUDE(load_bitmap_in_background)
+        }
+
+        /**
+         * Once the image is processed, associates it to the imageView
+         */
+        @Override
+        protected void onPostExecute(BitmapDrawable value) {
+            //BEGIN_INCLUDE(complete_background_work)
+            // if cancel was called on this task or the "exit early" flag is set then we're done
+            if (isCancelled() || mExitTasksEarly) {
+                value = null;
+            }
+
+            final ImageView imageView = getAttachedImageView();
+            if (value != null && imageView != null) {
+                if (BuildConfig.DEBUG) {
+                    Log.d(TAG, "onPostExecute - setting bitmap");
+                }
+                setImageDrawable(imageView, value);
+            }
+            //END_INCLUDE(complete_background_work)
+        }
+
+        @Override
+        protected void onCancelled(BitmapDrawable value) {
+            super.onCancelled(value);
+            synchronized (mPauseWorkLock) {
+                mPauseWorkLock.notifyAll();
+            }
+        }
+
+        /**
+         * Returns the ImageView associated with this task as long as the ImageView's task still
+         * points to this task as well. Returns null otherwise.
+         */
+        private ImageView getAttachedImageView() {
+            final ImageView imageView = imageViewReference.get();
+            final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+            if (this == bitmapWorkerTask) {
+                return imageView;
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * A custom Drawable that will be attached to the imageView while the work is in progress.
+     * Contains a reference to the actual worker task, so that it can be stopped if a new binding is
+     * required, and makes sure that only the last started worker process can bind its result,
+     * independently of the finish order.
+     */
+    private static class AsyncDrawable extends BitmapDrawable {
+        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+        public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+            super(res, bitmap);
+            bitmapWorkerTaskReference =
+                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+        }
+
+        public BitmapWorkerTask getBitmapWorkerTask() {
+            return bitmapWorkerTaskReference.get();
+        }
+    }
+
+    /**
+     * Called when the processing is complete and the final drawable should be 
+     * set on the ImageView.
+     *
+     * @param imageView
+     * @param drawable
+     */
+    private void setImageDrawable(ImageView imageView, Drawable drawable) {
+        if (mFadeInBitmap) {
+            // Transition drawable with a transparent drawable and the final drawable
+            final TransitionDrawable td =
+                    new TransitionDrawable(new Drawable[] {
+                            new ColorDrawable(android.R.color.transparent),
+                            drawable
+                    });
+            // Set background to loading bitmap
+            imageView.setBackgroundDrawable(
+                    new BitmapDrawable(mResources, mLoadingBitmap));
+
+            imageView.setImageDrawable(td);
+            td.startTransition(FADE_IN_TIME);
+        } else {
+            imageView.setImageDrawable(drawable);
+        }
+    }
+
+    /**
+     * Pause any ongoing background work. This can be used as a temporary
+     * measure to improve performance. For example background work could
+     * be paused when a ListView or GridView is being scrolled using a
+     * {@link android.widget.AbsListView.OnScrollListener} to keep
+     * scrolling smooth.
+     * <p>
+     * If work is paused, be sure setPauseWork(false) is called again
+     * before your fragment or activity is destroyed (for example during
+     * {@link android.app.Activity#onPause()}), or there is a risk the
+     * background thread will never finish.
+     */
+    public void setPauseWork(boolean pauseWork) {
+        synchronized (mPauseWorkLock) {
+            mPauseWork = pauseWork;
+            if (!mPauseWork) {
+                mPauseWorkLock.notifyAll();
+            }
+        }
+    }
+
+    protected class CacheAsyncTask extends AsyncTask<Object, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Object... params) {
+            switch ((Integer)params[0]) {
+                case MESSAGE_CLEAR:
+                    clearCacheInternal();
+                    break;
+                case MESSAGE_INIT_DISK_CACHE:
+                    initDiskCacheInternal();
+                    break;
+                case MESSAGE_FLUSH:
+                    flushCacheInternal();
+                    break;
+                case MESSAGE_CLOSE:
+                    closeCacheInternal();
+                    break;
+            }
+            return null;
+        }
+    }
+
+    protected void initDiskCacheInternal() {
+        if (mImageCache != null) {
+            mImageCache.initDiskCache();
+        }
+    }
+
+    protected void clearCacheInternal() {
+        if (mImageCache != null) {
+            mImageCache.clearCache();
+        }
+    }
+
+    protected void flushCacheInternal() {
+        if (mImageCache != null) {
+            mImageCache.flush();
+        }
+    }
+
+    protected void closeCacheInternal() {
+        if (mImageCache != null) {
+            mImageCache.close();
+            mImageCache = null;
+        }
+    }
+
+    public void clearCache() {
+        new CacheAsyncTask().execute(MESSAGE_CLEAR);
+    }
+
+    public void flushCache() {
+        new CacheAsyncTask().execute(MESSAGE_FLUSH);
+    }
+
+    public void closeCache() {
+        new CacheAsyncTask().execute(MESSAGE_CLOSE);
+    }
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/RecyclingBitmapDrawable.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/RecyclingBitmapDrawable.java
new file mode 100644
index 0000000..5534a6a
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/RecyclingBitmapDrawable.java
@@ -0,0 +1,109 @@
+/*
+ * 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.displayingbitmaps.util;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+
+/**
+ * A BitmapDrawable that keeps track of whether it is being displayed or cached.
+ * When the drawable is no longer being displayed or cached,
+ * {@link android.graphics.Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
+ */
+public class RecyclingBitmapDrawable extends BitmapDrawable {
+
+    static final String TAG = "CountingBitmapDrawable";
+
+    private int mCacheRefCount = 0;
+    private int mDisplayRefCount = 0;
+
+    private boolean mHasBeenDisplayed;
+
+    public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
+        super(res, bitmap);
+    }
+
+    /**
+     * Notify the drawable that the displayed state has changed. Internally a
+     * count is kept so that the drawable knows when it is no longer being
+     * displayed.
+     *
+     * @param isDisplayed - Whether the drawable is being displayed or not
+     */
+    public void setIsDisplayed(boolean isDisplayed) {
+        //BEGIN_INCLUDE(set_is_displayed)
+        synchronized (this) {
+            if (isDisplayed) {
+                mDisplayRefCount++;
+                mHasBeenDisplayed = true;
+            } else {
+                mDisplayRefCount--;
+            }
+        }
+
+        // Check to see if recycle() can be called
+        checkState();
+        //END_INCLUDE(set_is_displayed)
+    }
+
+    /**
+     * Notify the drawable that the cache state has changed. Internally a count
+     * is kept so that the drawable knows when it is no longer being cached.
+     *
+     * @param isCached - Whether the drawable is being cached or not
+     */
+    public void setIsCached(boolean isCached) {
+        //BEGIN_INCLUDE(set_is_cached)
+        synchronized (this) {
+            if (isCached) {
+                mCacheRefCount++;
+            } else {
+                mCacheRefCount--;
+            }
+        }
+
+        // Check to see if recycle() can be called
+        checkState();
+        //END_INCLUDE(set_is_cached)
+    }
+
+    private synchronized void checkState() {
+        //BEGIN_INCLUDE(check_state)
+        // If the drawable cache and display ref counts = 0, and this drawable
+        // has been displayed, then recycle
+        if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
+                && hasValidBitmap()) {
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "No longer being used or cached so recycling. "
+                        + toString());
+            }
+
+            getBitmap().recycle();
+        }
+        //END_INCLUDE(check_state)
+    }
+
+    private synchronized boolean hasValidBitmap() {
+        Bitmap bitmap = getBitmap();
+        return bitmap != null && !bitmap.isRecycled();
+    }
+
+}
diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/Utils.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/Utils.java
new file mode 100644
index 0000000..505d0ef
--- /dev/null
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/Utils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.displayingbitmaps.util;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.StrictMode;
+
+import com.example.android.displayingbitmaps.ui.ImageDetailActivity;
+import com.example.android.displayingbitmaps.ui.ImageGridActivity;
+
+/**
+ * Class containing some static utility methods.
+ */
+public class Utils {
+    private Utils() {};
+
+
+    @TargetApi(VERSION_CODES.HONEYCOMB)
+    public static void enableStrictMode() {
+        if (Utils.hasGingerbread()) {
+            StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
+                    new StrictMode.ThreadPolicy.Builder()
+                            .detectAll()
+                            .penaltyLog();
+            StrictMode.VmPolicy.Builder vmPolicyBuilder =
+                    new StrictMode.VmPolicy.Builder()
+                            .detectAll()
+                            .penaltyLog();
+
+            if (Utils.hasHoneycomb()) {
+                threadPolicyBuilder.penaltyFlashScreen();
+                vmPolicyBuilder
+                        .setClassInstanceLimit(ImageGridActivity.class, 1)
+                        .setClassInstanceLimit(ImageDetailActivity.class, 1);
+            }
+            StrictMode.setThreadPolicy(threadPolicyBuilder.build());
+            StrictMode.setVmPolicy(vmPolicyBuilder.build());
+        }
+    }
+
+    public static boolean hasFroyo() {
+        // Can use static final constants like FROYO, declared in later versions
+        // of the OS since they are inlined at compile time. This is guaranteed behavior.
+        return Build.VERSION.SDK_INT >= VERSION_CODES.FROYO;
+    }
+
+    public static boolean hasGingerbread() {
+        return Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD;
+    }
+
+    public static boolean hasHoneycomb() {
+        return Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
+    }
+
+    public static boolean hasHoneycombMR1() {
+        return Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1;
+    }
+
+    public static boolean hasJellyBean() {
+        return Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
+    }
+
+    public static boolean hasKitKat() {
+        return Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT;
+    }
+}
diff --git a/samples/browseable/DoneBar/res/values-sw600dp/dimens.xml b/samples/browseable/DoneBar/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/DoneBar/res/values-sw600dp/dimens.xml
rename to samples/browseable/DoneBar/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/DoneBar/res/values-sw600dp/styles.xml b/samples/browseable/DoneBar/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/DoneBar/res/values-sw600dp/styles.xml
rename to samples/browseable/DoneBar/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/DoneBar/res/values-v11/template-styles.xml b/samples/browseable/DoneBar/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/DoneBar/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/DoneBar/res/values/dimens.xml b/samples/browseable/DoneBar/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/DoneBar/res/values/dimens.xml
rename to samples/browseable/DoneBar/res/values/template-dimens.xml
diff --git a/samples/browseable/DoneBar/res/values/styles.xml b/samples/browseable/DoneBar/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/DoneBar/res/values/styles.xml
rename to samples/browseable/DoneBar/res/values/template-styles.xml
diff --git a/samples/browseable/FragmentTransition/AndroidManifest.xml b/samples/browseable/FragmentTransition/AndroidManifest.xml
new file mode 100644
index 0000000..8d02eb0
--- /dev/null
+++ b/samples/browseable/FragmentTransition/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2014 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.fragmenttransition"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name="com.example.android.fragmenttransition.MainActivity"
+            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/browseable/FragmentTransition/_index.jd b/samples/browseable/FragmentTransition/_index.jd
new file mode 100644
index 0000000..f0efe32
--- /dev/null
+++ b/samples/browseable/FragmentTransition/_index.jd
@@ -0,0 +1,8 @@
+
+
+
+page.tags="FragmentTransition"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to start a transition right after a fragment transaction.</p>
diff --git a/samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..dfa1b45
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/FragmentTransition/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/FragmentTransition/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..5f4ae7b
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg
new file mode 100644
index 0000000..10f07ac
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg
new file mode 100644
index 0000000..4272f4c
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg
new file mode 100644
index 0000000..c5722b2
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg
new file mode 100644
index 0000000..ca380ae
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg
new file mode 100644
index 0000000..6fc71e7
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg
new file mode 100644
index 0000000..153c1ff
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg
new file mode 100644
index 0000000..46d6a13
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg
new file mode 100644
index 0000000..89ccb83
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg
new file mode 100644
index 0000000..7e9546d
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg
new file mode 100644
index 0000000..21e25ba
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg
new file mode 100644
index 0000000..79854cb
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5e00f33
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..e061498
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/FragmentTransition/res/layout/activity_main.xml b/samples/browseable/FragmentTransition/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/FragmentTransition/res/layout/fragment_detail.xml b/samples/browseable/FragmentTransition/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..d94256f
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/layout/fragment_detail.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2014 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.
+-->
+<FrameLayout
+    android:id="@+id/container"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"/>
diff --git a/samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml b/samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml
new file mode 100644
index 0000000..2068460
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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.
+-->
+<ScrollView
+    android:id="@+id/frame"
+    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:scrollbars="none">
+
+    <RelativeLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:id="@+id/image"
+            android:layout_width="match_parent"
+            android:layout_height="180dp"
+            android:scaleType="centerCrop"
+            tools:src="@drawable/p1"/>
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignBottom="@id/image"
+            android:layout_alignEnd="@id/image"
+            android:layout_marginEnd="16dp"
+            android:shadowColor="#000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="10"
+            android:textColor="#ffffff"
+            android:textSize="24sp"
+            android:textStyle="bold"
+            tools:text="Image"/>
+
+        <TextView
+            android:id="@+id/body"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/image"
+            android:layout_marginBottom="16dp"
+            android:layout_marginEnd="16dp"
+            android:layout_marginStart="16dp"
+            android:layout_marginTop="16dp"
+            android:text="@string/lorem_ipsum"/>
+
+    </RelativeLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml b/samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml
new file mode 100644
index 0000000..6e1c7a1
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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.
+-->
+<GridView
+    android:id="@+id/grid"
+    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:clipToPadding="false"
+    android:columnWidth="150dp"
+    android:horizontalSpacing="1dp"
+    android:numColumns="auto_fit"
+    android:padding="1dp"
+    android:scrollbars="none"
+    android:stretchMode="columnWidth"
+    android:verticalSpacing="1dp"
+    tools:context="com.example.android.fragmenttransition.FragmentTransitionFragment"/>
diff --git a/samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml b/samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml
new file mode 100644
index 0000000..df34883
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2014 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.
+-->
+<FrameLayout
+    android:id="@+id/frame"
+    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">
+
+    <RelativeLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="150dp"
+        tools:ignore="UselessParent">
+
+        <ImageView
+            android:id="@+id/image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerCrop"
+            tools:src="@drawable/p1"/>
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentEnd="true"
+            android:layout_gravity="bottom|end"
+            android:layout_marginEnd="16dp"
+            android:layout_marginStart="16dp"
+            android:gravity="center_horizontal"
+            android:shadowColor="#000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="10"
+            android:textColor="#ffffff"
+            android:textSize="24sp"
+            android:textStyle="bold"
+            tools:text="Hello"/>
+
+    </RelativeLayout>
+
+</FrameLayout>
diff --git a/samples/browseable/FragmentTransition/res/menu/main.xml b/samples/browseable/FragmentTransition/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml b/samples/browseable/FragmentTransition/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml
copy to samples/browseable/FragmentTransition/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml b/samples/browseable/FragmentTransition/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml
copy to samples/browseable/FragmentTransition/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml b/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/FragmentTransition/res/values/base-strings.xml b/samples/browseable/FragmentTransition/res/values/base-strings.xml
new file mode 100644
index 0000000..933e1f2
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/values/base-strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">FragmentTransition</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+	    This sample demonstrates how to start a transition right after a fragment transaction.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/FragmentTransition/res/values/strings.xml b/samples/browseable/FragmentTransition/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/FragmentTransition/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/FragmentTransition/res/values/template-dimens.xml
diff --git a/samples/browseable/FragmentTransition/res/values/template-styles.xml b/samples/browseable/FragmentTransition/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/FragmentTransition/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java
new file mode 100644
index 0000000..81e7b46
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014 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.fragmenttransition;
+
+import com.example.android.common.logger.Log;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class DetailFragment extends Fragment implements Animation.AnimationListener {
+
+    private static final String TAG = "DetailFragment";
+
+    private static final String ARG_RESOURCE_ID = "resource_id";
+    private static final String ARG_TITLE = "title";
+    private static final String ARG_X = "x";
+    private static final String ARG_Y = "y";
+    private static final String ARG_WIDTH = "width";
+    private static final String ARG_HEIGHT = "height";
+
+    /**
+     * Create a new instance of DetailFragment.
+     *
+     * @param resourceId The resource ID of the Drawable image to show
+     * @param title The title of the image
+     * @param x The horizontal position of the grid item in pixel
+     * @param y The vertical position of the grid item in pixel
+     * @param width The width of the grid item in pixel
+     * @param height The height of the grid item in pixel
+     * @return a new instance of DetailFragment
+     */
+    public static DetailFragment newInstance(int resourceId, String title,
+                                             int x, int y, int width, int height) {
+        DetailFragment fragment = new DetailFragment();
+        Bundle args = new Bundle();
+        args.putInt(ARG_RESOURCE_ID, resourceId);
+        args.putString(ARG_TITLE, title);
+        args.putInt(ARG_X, x);
+        args.putInt(ARG_Y, y);
+        args.putInt(ARG_WIDTH, width);
+        args.putInt(ARG_HEIGHT, height);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    public DetailFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_detail, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        FrameLayout root = (FrameLayout) view;
+        Context context = view.getContext();
+        assert context != null;
+        // This is how the fragment looks at first. Since the transition is one-way, we don't need to make
+        // this a Scene.
+        View item = LayoutInflater.from(context).inflate(R.layout.item_meat_grid, root, false);
+        assert item != null;
+        bind(item);
+        // We adjust the position of the initial image with LayoutParams using the values supplied
+        // as the fragment arguments.
+        Bundle args = getArguments();
+        FrameLayout.LayoutParams params = null;
+        if (args != null) {
+            params = new FrameLayout.LayoutParams(
+                    args.getInt(ARG_WIDTH), args.getInt(ARG_HEIGHT));
+            params.topMargin = args.getInt(ARG_Y);
+            params.leftMargin = args.getInt(ARG_X);
+        }
+        root.addView(item, params);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    /**
+     * Bind the views inside of parent with the fragment arguments.
+     *
+     * @param parent The parent of views to bind.
+     */
+    private void bind(View parent) {
+        Bundle args = getArguments();
+        if (args == null) {
+            return;
+        }
+        ImageView image = (ImageView) parent.findViewById(R.id.image);
+        image.setImageResource(args.getInt(ARG_RESOURCE_ID));
+        TextView title = (TextView) parent.findViewById(R.id.title);
+        title.setText(args.getString(ARG_TITLE));
+    }
+
+    @Override
+    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+        Animation animation = AnimationUtils.loadAnimation(getActivity(),
+                enter ? android.R.anim.fade_in : android.R.anim.fade_out);
+        // We bind a listener for the fragment transaction. We only bind it when
+        // this fragment is entering.
+        if (animation != null && enter) {
+            animation.setAnimationListener(this);
+        }
+        return animation;
+    }
+
+    @Override
+    public void onAnimationStart(Animation animation) {
+        // This method is called at the end of the animation for the fragment transaction.
+        // There is nothing we need to do in this sample.
+    }
+
+    @Override
+    public void onAnimationEnd(Animation animation) {
+        // This method is called at the end of the animation for the fragment transaction,
+        // which is perfect time to start our Transition.
+        Log.i(TAG, "Fragment animation ended. Starting a Transition.");
+        final Scene scene = Scene.getSceneForLayout((ViewGroup) getView(),
+                R.layout.fragment_detail_content, getActivity());
+        TransitionManager.go(scene);
+        // Note that we need to bind views with data after we call TransitionManager.go().
+        bind(scene.getSceneRoot());
+    }
+
+    @Override
+    public void onAnimationRepeat(Animation animation) {
+        // This method is never called in this sample because the animation doesn't repeat.
+    }
+
+}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java
new file mode 100644
index 0000000..c072eb9
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 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.fragmenttransition;
+
+import com.example.android.common.logger.Log;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.AdapterView;
+import android.widget.GridView;
+
+public class FragmentTransitionFragment extends Fragment implements AdapterView.OnItemClickListener {
+
+    private static final String TAG = "FragmentTransitionFragment";
+
+    private MeatAdapter mAdapter;
+
+    public static FragmentTransitionFragment newInstance() {
+        return new FragmentTransitionFragment();
+    }
+
+    public FragmentTransitionFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        // This is the adapter we use to populate the grid.
+        mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
+        // Inflate the layout with a GridView in it.
+        return inflater.inflate(R.layout.fragment_fragment_transition, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        GridView grid = (GridView) view.findViewById(R.id.grid);
+        grid.setAdapter(mAdapter);
+        grid.setOnItemClickListener(this);
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        Meat meat = mAdapter.getItem(position);
+        Log.i(TAG, meat.title + " clicked. Replacing fragment.");
+        // We start the fragment transaction here. It is just an ordinary fragment transaction.
+        getActivity().getSupportFragmentManager()
+                .beginTransaction()
+                .replace(R.id.sample_content_fragment,
+                        DetailFragment.newInstance(meat.resourceId, meat.title,
+                                (int) view.getX(), (int) view.getY(),
+                                view.getWidth(), view.getHeight())
+                )
+                // We push the fragment transaction to back stack. User can go back to the
+                // previous fragment by pressing back button.
+                .addToBackStack("detail")
+                .commit();
+    }
+
+    @Override
+    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+        return AnimationUtils.loadAnimation(getActivity(),
+                enter ? android.R.anim.fade_in : android.R.anim.fade_out);
+    }
+
+}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java
new file mode 100644
index 0000000..7d6ca6f
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.fragmenttransition;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        FragmentTransitionFragment fragment = new FragmentTransitionFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java
new file mode 100644
index 0000000..2f2fdfa
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 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.fragmenttransition;
+
+/**
+ * This represents a sample data.
+ */
+public class Meat {
+
+    public int resourceId;
+    public String title;
+
+    public Meat(int resourceId, String title) {
+        this.resourceId = resourceId;
+        this.title = title;
+    }
+
+    public static final Meat[] MEATS = {
+            new Meat(R.drawable.p1, "First"),
+            new Meat(R.drawable.p2, "Second"),
+            new Meat(R.drawable.p3, "Third"),
+            new Meat(R.drawable.p4, "Fourth"),
+            new Meat(R.drawable.p5, "Fifth"),
+            new Meat(R.drawable.p6, "Sixth"),
+            new Meat(R.drawable.p7, "Seventh"),
+            new Meat(R.drawable.p8, "Eighth"),
+            new Meat(R.drawable.p9, "Ninth"),
+            new Meat(R.drawable.p10, "Tenth"),
+            new Meat(R.drawable.p11, "Eleventh"),
+    };
+
+}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java
new file mode 100644
index 0000000..307fd85
--- /dev/null
+++ b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 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.fragmenttransition;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+class MeatAdapter extends BaseAdapter {
+
+    private final LayoutInflater mLayoutInflater;
+    private final int mResourceId;
+
+    public MeatAdapter(LayoutInflater inflater, int resourceId) {
+        mLayoutInflater = inflater;
+        mResourceId = resourceId;
+    }
+
+    @Override
+    public int getCount() {
+        return Meat.MEATS.length;
+    }
+
+    @Override
+    public Meat getItem(int position) {
+        return Meat.MEATS[position];
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return Meat.MEATS[position].resourceId;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        final View view;
+        final ViewHolder holder;
+        if (null == convertView) {
+            view = mLayoutInflater.inflate(mResourceId, parent, false);
+            holder = new ViewHolder();
+            assert view != null;
+            holder.image = (ImageView) view.findViewById(R.id.image);
+            holder.title = (TextView) view.findViewById(R.id.title);
+            view.setTag(holder);
+        } else {
+            view = convertView;
+            holder = (ViewHolder) view.getTag();
+        }
+        bindView(holder, position);
+        return view;
+    }
+
+    public void bindView(ViewHolder holder, int position) {
+        Meat meat = getItem(position);
+        holder.image.setImageResource(meat.resourceId);
+        holder.title.setText(meat.title);
+    }
+
+    public static class ViewHolder {
+        public ImageView image;
+        public TextView title;
+    }
+
+}
diff --git a/samples/browseable/HorizontalPaging/res/values-sw600dp/dimens.xml b/samples/browseable/HorizontalPaging/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/HorizontalPaging/res/values-sw600dp/dimens.xml
rename to samples/browseable/HorizontalPaging/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/HorizontalPaging/res/values-sw600dp/styles.xml b/samples/browseable/HorizontalPaging/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/HorizontalPaging/res/values-sw600dp/styles.xml
rename to samples/browseable/HorizontalPaging/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml b/samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/HorizontalPaging/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/HorizontalPaging/res/values/dimens.xml b/samples/browseable/HorizontalPaging/res/values/dimens.xml
index 39e710b..47c8224 100644
--- a/samples/browseable/HorizontalPaging/res/values/dimens.xml
+++ b/samples/browseable/HorizontalPaging/res/values/dimens.xml
@@ -1,32 +1,5 @@
-<!--
-  Copyright 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>
-
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
-
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 </resources>
diff --git a/samples/browseable/HorizontalPaging/res/values/styles.xml b/samples/browseable/HorizontalPaging/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/HorizontalPaging/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/BasicNetworking/res/values/dimens.xml b/samples/browseable/HorizontalPaging/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/BasicNetworking/res/values/dimens.xml
copy to samples/browseable/HorizontalPaging/res/values/template-dimens.xml
diff --git a/samples/browseable/HorizontalPaging/res/values/template-styles.xml b/samples/browseable/HorizontalPaging/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/HorizontalPaging/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/ImmersiveMode/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/ImmersiveMode/res/layout-sw600dp-land/activity_main.xml
new file mode 100755
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/ImmersiveMode/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/ImmersiveMode/res/layout-sw600dp/activity_main.xml b/samples/browseable/ImmersiveMode/res/layout-sw600dp/activity_main.xml
new file mode 100755
index 0000000..f6f4157
--- /dev/null
+++ b/samples/browseable/ImmersiveMode/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:id="@+id/sample_main_layout" >
+
+    <TextView android:id="@+id/sample_output"
+        style="@style/Widget.SampleMessage"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/intro_message"
+        android:padding="16dp"
+        android:layout_margin="16dp"/>
+    <fragment
+        android:name="com.example.android.common.logger.LogFragment"
+        android:id="@+id/log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/ImmersiveMode/res/layout/activity_main.xml b/samples/browseable/ImmersiveMode/res/layout/activity_main.xml
index bc5a575..6f41369 100755
--- a/samples/browseable/ImmersiveMode/res/layout/activity_main.xml
+++ b/samples/browseable/ImmersiveMode/res/layout/activity_main.xml
@@ -24,7 +24,8 @@
               android:layout_weight="1"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:text="@string/intro_message"
+              android:padding="16dp" />
     <View
             android:layout_width="fill_parent"
             android:layout_height="1dp"
diff --git a/samples/browseable/ImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/ImmersiveMode/res/values-sw600dp/styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/ImmersiveMode/res/values-sw600dp/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  Copyright 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>
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceLarge</item>
-        <item name="android:lineSpacingMultiplier">1.2</item>
-        <item name="android:shadowDy">-6.5</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/ImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/ImmersiveMode/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/ImmersiveMode/res/values-sw600dp/dimens.xml
rename to samples/browseable/ImmersiveMode/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ImmersiveMode/res/values-sw600dp/template-styles.xml b/samples/browseable/ImmersiveMode/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..b6ea1a0
--- /dev/null
+++ b/samples/browseable/ImmersiveMode/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright 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>
+
+    <style name="Widget.SampleMessage">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+
+        <item name="android:layout_margin">16dp</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+
+</resources>
diff --git a/samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml b/samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/ImmersiveMode/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/ImmersiveMode/res/values/dimens.xml b/samples/browseable/ImmersiveMode/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/ImmersiveMode/res/values/dimens.xml
rename to samples/browseable/ImmersiveMode/res/values/template-dimens.xml
diff --git a/samples/browseable/ImmersiveMode/res/values/styles.xml b/samples/browseable/ImmersiveMode/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/ImmersiveMode/res/values/styles.xml
rename to samples/browseable/ImmersiveMode/res/values/template-styles.xml
diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java b/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java
index fd02112..8fb6e34 100644
--- a/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java
+++ b/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java
@@ -19,6 +19,7 @@
 
 package com.example.android.immersivemode;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
@@ -74,6 +75,9 @@
         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
                 .findFragmentById(R.id.log_fragment);
         msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
 
         Log.i(TAG, "Ready");
     }
diff --git a/samples/browseable/ListPopupMenu/_index.jd b/samples/browseable/ListPopupMenu/_index.jd
deleted file mode 100644
index 36090e0..0000000
--- a/samples/browseable/ListPopupMenu/_index.jd
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-page.tags="ListPopupMenu"
-sample.group=UI
-@jd:body
-
-<p>This sample demonstrates how to use a backward compatible
-{@link android.support.v7.widget.PopupMenu PopupMenu} to create a list, where
-each list item contains a dropdown menu.</p>
-<p>The activity in this sample extends from
-{@link android.support.v7.app.ActionBarActivity}, which provides the
-functionality necessary to display a compatible action bar on devices
-running Android 2.1 and higher.</p>
diff --git a/samples/browseable/ListPopupMenu/res/values/base-strings.xml b/samples/browseable/ListPopupMenu/res/values/base-strings.xml
deleted file mode 100644
index a23ed75..0000000
--- a/samples/browseable/ListPopupMenu/res/values/base-strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 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">ListPopupMenu</string>
-    <string name="intro_message">
-        <![CDATA[
-        
-            
-            This sample shows you how to use {@link android.support.v7.widget.PopupMenu PopupMenu}
-            from ActionBarCompat to create a list, with each item having a dropdown menu.
-            
-        
-        ]]>
-    </string>
-</resources>
diff --git a/samples/browseable/ListPopupMenu/res/values/styles.xml b/samples/browseable/ListPopupMenu/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/ListPopupMenu/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/MediaRecorder/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/MediaRecorder/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/MediaRecorder/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/Basic/res/layout/activity_main.xml
copy to samples/browseable/MediaRecorder/res/layout/activity_main.xml
diff --git a/samples/browseable/DoneBar/res/values-sw600dp/dimens.xml b/samples/browseable/MediaRecorder/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values-sw600dp/dimens.xml
copy to samples/browseable/MediaRecorder/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/DoneBar/res/values-sw600dp/styles.xml b/samples/browseable/MediaRecorder/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values-sw600dp/styles.xml
copy to samples/browseable/MediaRecorder/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/MediaRecorder/res/values-v11/template-styles.xml b/samples/browseable/MediaRecorder/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/MediaRecorder/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/MediaRecorder/res/values/base-strings.xml b/samples/browseable/MediaRecorder/res/values/base-strings.xml
new file mode 100644
index 0000000..f9ade8c
--- /dev/null
+++ b/samples/browseable/MediaRecorder/res/values/base-strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">MediaRecorder</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            This sample uses the camera/camcorder as the A/V source for the MediaRecorder API.
+            A TextureView is used as the camera preview which limits the code to API 14+. This
+            can be easily replaced with a SurfaceView to run on older devices.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/DoneBar/res/values/dimens.xml b/samples/browseable/MediaRecorder/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values/dimens.xml
copy to samples/browseable/MediaRecorder/res/values/template-dimens.xml
diff --git a/samples/browseable/MediaRecorder/res/values/template-styles.xml b/samples/browseable/MediaRecorder/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/MediaRecorder/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/MediaRecorder/src/com.example.android.common.media/CameraHelper.java b/samples/browseable/MediaRecorder/src/com.example.android.common.media/CameraHelper.java
new file mode 100644
index 0000000..1fa8416
--- /dev/null
+++ b/samples/browseable/MediaRecorder/src/com.example.android.common.media/CameraHelper.java
@@ -0,0 +1,182 @@
+/*
+ * 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.common.media;
+
+import android.annotation.TargetApi;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Camera related utilities.
+ */
+public class CameraHelper {
+
+    public static final int MEDIA_TYPE_IMAGE = 1;
+    public static final int MEDIA_TYPE_VIDEO = 2;
+
+    /**
+     * Iterate over supported camera preview sizes to see which one best fits the
+     * dimensions of the given view while maintaining the aspect ratio. If none can,
+     * be lenient with the aspect ratio.
+     *
+     * @param sizes Supported camera preview sizes.
+     * @param w The width of the view.
+     * @param h The height of the view.
+     * @return Best match camera preview size to fit in the view.
+     */
+    public static  Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
+        // Use a very small tolerance because we want an exact match.
+        final double ASPECT_TOLERANCE = 0.1;
+        double targetRatio = (double) w / h;
+        if (sizes == null)
+            return null;
+
+        Camera.Size optimalSize = null;
+
+        // Start with max value and refine as we iterate over available preview sizes. This is the
+        // minimum difference between view and camera height.
+        double minDiff = Double.MAX_VALUE;
+
+        // Target view height
+        int targetHeight = h;
+
+        // Try to find a preview size that matches aspect ratio and the target view size.
+        // Iterate over all available sizes and pick the largest size that can fit in the view and
+        // still maintain the aspect ratio.
+        for (Camera.Size size : sizes) {
+            double ratio = (double) size.width / size.height;
+            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
+                continue;
+            if (Math.abs(size.height - targetHeight) < minDiff) {
+                optimalSize = size;
+                minDiff = Math.abs(size.height - targetHeight);
+            }
+        }
+
+        // Cannot find preview size that matches the aspect ratio, ignore the requirement
+        if (optimalSize == null) {
+            minDiff = Double.MAX_VALUE;
+            for (Camera.Size size : sizes) {
+                if (Math.abs(size.height - targetHeight) < minDiff) {
+                    optimalSize = size;
+                    minDiff = Math.abs(size.height - targetHeight);
+                }
+            }
+        }
+        return optimalSize;
+    }
+
+    /**
+     * @return the default camera on the device. Return null if there is no camera on the device.
+     */
+    public static Camera getDefaultCameraInstance() {
+        return Camera.open();
+    }
+
+
+    /**
+     * @return the default rear/back facing camera on the device. Returns null if camera is not
+     * available.
+     */
+    public static Camera getDefaultBackFacingCameraInstance() {
+        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
+    }
+
+    /**
+     * @return the default front facing camera on the device. Returns null if camera is not
+     * available.
+     */
+    public static Camera getDefaultFrontFacingCameraInstance() {
+        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
+    }
+
+
+    /**
+     *
+     * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT
+     *                 or Camera.CameraInfo.CAMERA_FACING_BACK.
+     * @return the default camera on the device. Returns null if camera is not available.
+     */
+    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+    private static Camera getDefaultCamera(int position) {
+        // Find the total number of cameras available
+        int  mNumberOfCameras = Camera.getNumberOfCameras();
+
+        // Find the ID of the back-facing ("default") camera
+        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+        for (int i = 0; i < mNumberOfCameras; i++) {
+            Camera.getCameraInfo(i, cameraInfo);
+            if (cameraInfo.facing == position) {
+                return Camera.open(i);
+
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory
+     * is persistent and available to other applications like gallery.
+     *
+     * @param type Media type. Can be video or image.
+     * @return A file object pointing to the newly created file.
+     */
+    public  static File getOutputMediaFile(int type){
+        // To be safe, you should check that the SDCard is mounted
+        // using Environment.getExternalStorageState() before doing this.
+        if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
+            return  null;
+        }
+
+        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_PICTURES), "CameraSample");
+        // This location works best if you want the created images to be shared
+        // between applications and persist after your app has been uninstalled.
+
+        // Create the storage directory if it does not exist
+        if (! mediaStorageDir.exists()){
+            if (! mediaStorageDir.mkdirs()) {
+                Log.d("CameraSample", "failed to create directory");
+                return null;
+            }
+        }
+
+        // Create a media file name
+        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+        File mediaFile;
+        if (type == MEDIA_TYPE_IMAGE){
+            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+                    "IMG_"+ timeStamp + ".jpg");
+        } else if(type == MEDIA_TYPE_VIDEO) {
+            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
+                    "VID_"+ timeStamp + ".mp4");
+        } else {
+            return null;
+        }
+
+        return mediaFile;
+    }
+
+}
diff --git a/samples/browseable/MediaRecorder/src/com.example.android.common.media/MediaCodecWrapper.java b/samples/browseable/MediaRecorder/src/com.example.android.common.media/MediaCodecWrapper.java
new file mode 100644
index 0000000..a511221
--- /dev/null
+++ b/samples/browseable/MediaRecorder/src/com.example.android.common.media/MediaCodecWrapper.java
@@ -0,0 +1,386 @@
+/*
+ * 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.common.media;
+
+import android.media.*;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * Simplifies the MediaCodec interface by wrapping around the buffer processing operations.
+ */
+public class MediaCodecWrapper {
+
+    // Handler to use for {@code OutputSampleListener} and {code OutputFormatChangedListener}
+    // callbacks
+    private Handler mHandler;
+
+
+    // Callback when media output format changes.
+    public interface OutputFormatChangedListener {
+        void outputFormatChanged(MediaCodecWrapper sender, MediaFormat newFormat);
+    }
+
+    private OutputFormatChangedListener mOutputFormatChangedListener = null;
+
+    /**
+     * Callback for decodes frames. Observers can register a listener for optional stream
+     * of decoded data
+     */
+    public interface OutputSampleListener {
+        void outputSample(MediaCodecWrapper sender, MediaCodec.BufferInfo info, ByteBuffer buffer);
+    }
+
+    /**
+     * The {@link MediaCodec} that is managed by this class.
+     */
+    private MediaCodec mDecoder;
+
+    // References to the internal buffers managed by the codec. The codec
+    // refers to these buffers by index, never by reference so it's up to us
+    // to keep track of which buffer is which.
+    private ByteBuffer[] mInputBuffers;
+    private ByteBuffer[] mOutputBuffers;
+
+    // Indices of the input buffers that are currently available for writing. We'll
+    // consume these in the order they were dequeued from the codec.
+    private Queue<Integer> mAvailableInputBuffers;
+
+    // Indices of the output buffers that currently hold valid data, in the order
+    // they were produced by the codec.
+    private Queue<Integer> mAvailableOutputBuffers;
+
+    // Information about each output buffer, by index. Each entry in this array
+    // is valid if and only if its index is currently contained in mAvailableOutputBuffers.
+    private MediaCodec.BufferInfo[] mOutputBufferInfo;
+
+    // An (optional) stream that will receive decoded data.
+    private OutputSampleListener mOutputSampleListener;
+
+    private MediaCodecWrapper(MediaCodec codec) {
+        mDecoder = codec;
+        codec.start();
+        mInputBuffers = codec.getInputBuffers();
+        mOutputBuffers = codec.getOutputBuffers();
+        mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length];
+        mAvailableInputBuffers = new ArrayDeque<Integer>(mOutputBuffers.length);
+        mAvailableOutputBuffers = new ArrayDeque<Integer>(mInputBuffers.length);
+    }
+
+    /**
+     * Releases resources and ends the encoding/decoding session.
+     */
+    public void stopAndRelease() {
+        mDecoder.stop();
+        mDecoder.release();
+        mDecoder = null;
+        mHandler = null;
+    }
+
+    /**
+     * Getter for the registered {@link OutputFormatChangedListener}
+     */
+    public OutputFormatChangedListener getOutputFormatChangedListener() {
+        return mOutputFormatChangedListener;
+    }
+
+    /**
+     *
+     * @param outputFormatChangedListener the listener for callback.
+     * @param handler message handler for posting the callback.
+     */
+    public void setOutputFormatChangedListener(final OutputFormatChangedListener
+            outputFormatChangedListener, Handler handler) {
+        mOutputFormatChangedListener = outputFormatChangedListener;
+
+        // Making sure we don't block ourselves due to a bad implementation of the callback by
+        // using a handler provided by client.
+        Looper looper;
+        mHandler = handler;
+        if (outputFormatChangedListener != null && mHandler == null) {
+            if ((looper = Looper.myLooper()) != null) {
+                mHandler = new Handler();
+            } else {
+                throw new IllegalArgumentException(
+                        "Looper doesn't exist in the calling thread");
+            }
+        }
+    }
+
+    /**
+     * Constructs the {@link MediaCodecWrapper} wrapper object around the video codec.
+     * The codec is created using the encapsulated information in the
+     * {@link MediaFormat} object.
+     *
+     * @param trackFormat The format of the media object to be decoded.
+     * @param surface Surface to render the decoded frames.
+     * @return
+     */
+    public static MediaCodecWrapper fromVideoFormat(final MediaFormat trackFormat,
+            Surface surface) {
+        MediaCodecWrapper result = null;
+        MediaCodec videoCodec = null;
+
+        // BEGIN_INCLUDE(create_codec)
+        final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
+
+        // Check to see if this is actually a video mime type. If it is, then create
+        // a codec that can decode this mime type.
+        if (mimeType.contains("video/")) {
+            videoCodec = MediaCodec.createDecoderByType(mimeType);
+            videoCodec.configure(trackFormat, surface, null,  0);
+
+        }
+
+        // If codec creation was successful, then create a wrapper object around the
+        // newly created codec.
+        if (videoCodec != null) {
+            result = new MediaCodecWrapper(videoCodec);
+        }
+        // END_INCLUDE(create_codec)
+
+        return result;
+    }
+
+
+    /**
+     * Write a media sample to the decoder.
+     *
+     * A "sample" here refers to a single atomic access unit in the media stream. The definition
+     * of "access unit" is dependent on the type of encoding used, but it typically refers to
+     * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor}
+     * extracts data from a stream one sample at a time.
+     *
+     * @param input A ByteBuffer containing the input data for one sample. The buffer must be set
+     * up for reading, with its position set to the beginning of the sample data and its limit
+     * set to the end of the sample data.
+     *
+     * @param presentationTimeUs  The time, relative to the beginning of the media stream,
+     * at which this buffer should be rendered.
+     *
+     * @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
+     * int, int, long, int)}
+     *
+     * @throws MediaCodec.CryptoException
+     */
+    public boolean writeSample(final ByteBuffer input,
+            final MediaCodec.CryptoInfo crypto,
+            final long presentationTimeUs,
+            final int flags) throws MediaCodec.CryptoException, WriteException {
+        boolean result = false;
+        int size = input.remaining();
+
+        // check if we have dequed input buffers available from the codec
+        if (size > 0 &&  !mAvailableInputBuffers.isEmpty()) {
+            int index = mAvailableInputBuffers.remove();
+            ByteBuffer buffer = mInputBuffers[index];
+
+            // we can't write our sample to a lesser capacity input buffer.
+            if (size > buffer.capacity()) {
+                throw new MediaCodecWrapper.WriteException(String.format(
+                        "Insufficient capacity in MediaCodec buffer: "
+                            + "tried to write %d, buffer capacity is %d.",
+                        input.remaining(),
+                        buffer.capacity()));
+            }
+
+            buffer.clear();
+            buffer.put(input);
+
+            // Submit the buffer to the codec for decoding. The presentationTimeUs
+            // indicates the position (play time) for the current sample.
+            if (crypto == null) {
+                mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
+            } else {
+                mDecoder.queueSecureInputBuffer(index, 0, crypto, presentationTimeUs, flags);
+            }
+            result = true;
+        }
+        return result;
+    }
+
+    static MediaCodec.CryptoInfo cryptoInfo= new MediaCodec.CryptoInfo();
+
+    /**
+     * Write a media sample to the decoder.
+     *
+     * A "sample" here refers to a single atomic access unit in the media stream. The definition
+     * of "access unit" is dependent on the type of encoding used, but it typically refers to
+     * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor}
+     * extracts data from a stream one sample at a time.
+     *
+     * @param extractor  Instance of {@link android.media.MediaExtractor} wrapping the media.
+     *
+     * @param presentationTimeUs The time, relative to the beginning of the media stream,
+     * at which this buffer should be rendered.
+     *
+     * @param flags  Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
+     * int, int, long, int)}
+     *
+     * @throws MediaCodec.CryptoException
+     */
+    public boolean writeSample(final MediaExtractor extractor,
+            final boolean isSecure,
+            final long presentationTimeUs,
+            int flags) {
+        boolean result = false;
+        boolean isEos = false;
+
+        if (!mAvailableInputBuffers.isEmpty()) {
+            int index = mAvailableInputBuffers.remove();
+            ByteBuffer buffer = mInputBuffers[index];
+
+            // reads the sample from the file using extractor into the buffer
+            int size = extractor.readSampleData(buffer, 0);
+            if (size <= 0) {
+                flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+            }
+
+            // Submit the buffer to the codec for decoding. The presentationTimeUs
+            // indicates the position (play time) for the current sample.
+            if (!isSecure) {
+                mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
+            } else {
+                extractor.getSampleCryptoInfo(cryptoInfo);
+                mDecoder.queueSecureInputBuffer(index, 0, cryptoInfo, presentationTimeUs, flags);
+            }
+
+            result = true;
+        }
+        return result;
+    }
+
+    /**
+     * Performs a peek() operation in the queue to extract media info for the buffer ready to be
+     * released i.e. the head element of the queue.
+     *
+     * @param out_bufferInfo An output var to hold the buffer info.
+     *
+     * @return True, if the peek was successful.
+     */
+    public boolean peekSample(MediaCodec.BufferInfo out_bufferInfo) {
+        // dequeue available buffers and synchronize our data structures with the codec.
+        update();
+        boolean result = false;
+        if (!mAvailableOutputBuffers.isEmpty()) {
+            int index = mAvailableOutputBuffers.peek();
+            MediaCodec.BufferInfo info = mOutputBufferInfo[index];
+            // metadata of the sample
+            out_bufferInfo.set(
+                    info.offset,
+                    info.size,
+                    info.presentationTimeUs,
+                    info.flags);
+            result = true;
+        }
+        return result;
+    }
+
+    /**
+     * Processes, releases and optionally renders the output buffer available at the head of the
+     * queue. All observers are notified with a callback. See {@link
+     * OutputSampleListener#outputSample(MediaCodecWrapper, android.media.MediaCodec.BufferInfo,
+     * java.nio.ByteBuffer)}
+     *
+     * @param render True, if the buffer is to be rendered on the {@link Surface} configured
+     *
+     */
+    public void popSample(boolean render) {
+        // dequeue available buffers and synchronize our data structures with the codec.
+        update();
+        if (!mAvailableOutputBuffers.isEmpty()) {
+            int index = mAvailableOutputBuffers.remove();
+
+            if (render && mOutputSampleListener != null) {
+                ByteBuffer buffer = mOutputBuffers[index];
+                MediaCodec.BufferInfo info = mOutputBufferInfo[index];
+                mOutputSampleListener.outputSample(this, info, buffer);
+            }
+
+            // releases the buffer back to the codec
+            mDecoder.releaseOutputBuffer(index, render);
+        }
+    }
+
+    /**
+     * Synchronize this object's state with the internal state of the wrapped
+     * MediaCodec.
+     */
+    private void update() {
+        // BEGIN_INCLUDE(update_codec_state)
+        int index;
+
+        // Get valid input buffers from the codec to fill later in the same order they were
+        // made available by the codec.
+        while ((index = mDecoder.dequeueInputBuffer(0)) != MediaCodec.INFO_TRY_AGAIN_LATER) {
+            mAvailableInputBuffers.add(index);
+        }
+
+
+        // Likewise with output buffers. If the output buffers have changed, start using the
+        // new set of output buffers. If the output format has changed, notify listeners.
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        while ((index = mDecoder.dequeueOutputBuffer(info, 0)) !=  MediaCodec.INFO_TRY_AGAIN_LATER) {
+            switch (index) {
+                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
+                    mOutputBuffers = mDecoder.getOutputBuffers();
+                    mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length];
+                    mAvailableOutputBuffers.clear();
+                    break;
+                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
+                    if (mOutputFormatChangedListener != null) {
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mOutputFormatChangedListener
+                                        .outputFormatChanged(MediaCodecWrapper.this,
+                                                mDecoder.getOutputFormat());
+
+                            }
+                        });
+                    }
+                    break;
+                default:
+                    // Making sure the index is valid before adding to output buffers. We've already
+                    // handled INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED &
+                    // INFO_OUTPUT_BUFFERS_CHANGED i.e all the other possible return codes but
+                    // asserting index value anyways for future-proofing the code.
+                    if(index >= 0) {
+                        mOutputBufferInfo[index] = info;
+                        mAvailableOutputBuffers.add(index);
+                    } else {
+                        throw new IllegalStateException("Unknown status from dequeueOutputBuffer");
+                    }
+                    break;
+            }
+
+        }
+        // END_INCLUDE(update_codec_state)
+
+    }
+
+    private class WriteException extends Throwable {
+        private WriteException(final String detailMessage) {
+            super(detailMessage);
+        }
+    }
+}
diff --git a/samples/browseable/MediaRouter/AndroidManifest.xml b/samples/browseable/MediaRouter/AndroidManifest.xml
new file mode 100644
index 0000000..0b5bec4
--- /dev/null
+++ b/samples/browseable/MediaRouter/AndroidManifest.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Declare the contents of this Android application.  The namespace
+     attribute brings in the Android platform namespace, and the package
+     supplies a unique name for the application.  When writing your
+     own application, the package name must be changed from "com.example.*"
+     to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.android.mediarouter">
+    <!-- Permission for INTERNET is required for streaming video content
+         from the web, it's not required otherwise. -->
+    <uses-permission android:name="android.permission.INTERNET" />
+    <!-- Permission for SYSTEM_ALERT_WINDOW is only required for emulating
+         remote display using system alert window. -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <uses-sdk android:targetSdkVersion="19"
+        android:minSdkVersion="7"/>
+
+    <!-- The smallest screen this app works on is a phone.  The app will
+         scale its UI to larger screens but doesn't make good use of them
+         so allow the compatibility mode button to be shown (mostly because
+         this is just convenient for testing). -->
+    <supports-screens android:requiresSmallestWidthDp="320"
+                      android:compatibleWidthLimitDp="480" />
+
+    <application android:label="@string/app_name"
+                 android:icon="@drawable/ic_launcher"
+                 android:hardwareAccelerated="true">
+
+        <receiver android:name=".player.SampleMediaButtonReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_BUTTON" />
+            </intent-filter>
+        </receiver>
+        <!-- MediaRouter Support Samples -->
+
+        <activity android:name=".player.MainActivity"
+                  android:label="@string/app_name"
+                  android:theme="@style/Theme.AppCompat.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".provider.SampleMediaRouteProviderService"
+                 android:label="@string/sample_media_route_provider_service"
+                 android:process=":mrp">
+            <intent-filter>
+                <action android:name="android.media.MediaRouteProviderService" />
+            </intent-filter>
+        </service>
+
+    </application>
+</manifest>
diff --git a/samples/browseable/MediaRouter/_index.jd b/samples/browseable/MediaRouter/_index.jd
new file mode 100644
index 0000000..5ca9467
--- /dev/null
+++ b/samples/browseable/MediaRouter/_index.jd
@@ -0,0 +1,10 @@
+
+
+
+page.tags="MediaRouter"
+sample.group=Media
+@jd:body
+
+<p>
+This sample demonstrates how to create a custom media route provider.
+</p>
\ No newline at end of file
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_pause.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_pause.png
new file mode 100644
index 0000000..d30ba3c
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_pause.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_play.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_play.png
new file mode 100644
index 0000000..869f001
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_play.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_stop.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_stop.png
new file mode 100644
index 0000000..2b6c0f4
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_action_stop.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_launcher.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..d340114
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_pause.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_pause.png
new file mode 100644
index 0000000..1d465a4
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_pause.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_play.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_play.png
new file mode 100644
index 0000000..2746d17
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_play.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_stop.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_stop.png
new file mode 100644
index 0000000..a0ff136
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_media_stop.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_menu_add.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..444e8a5
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_menu_add.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-hdpi/ic_menu_delete.png b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_menu_delete.png
new file mode 100644
index 0000000..24d8f6a
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-hdpi/ic_menu_delete.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/MediaRouter/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/MediaRouter/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_pause.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_pause.png
new file mode 100644
index 0000000..2c96c7b
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_pause.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_play.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_play.png
new file mode 100644
index 0000000..5f3bf86
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_play.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_stop.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_stop.png
new file mode 100644
index 0000000..ddaf37a
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_action_stop.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_launcher.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..6af9981
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_pause.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_pause.png
new file mode 100644
index 0000000..3e6b2a1
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_play.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_play.png
new file mode 100644
index 0000000..7966bbc
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_stop.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_stop.png
new file mode 100644
index 0000000..8ea7efe
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_media_stop.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_menu_add.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..361c7c4
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_menu_add.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-mdpi/ic_menu_delete.png b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_menu_delete.png
new file mode 100644
index 0000000..e2c8700
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-mdpi/ic_menu_delete.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_pause.png b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_pause.png
new file mode 100644
index 0000000..504389a
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_pause.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_play.png b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_play.png
new file mode 100644
index 0000000..7f709bb
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_play.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_stop.png b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_stop.png
new file mode 100644
index 0000000..2b07de4
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_action_stop.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..16bd612
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_pause.png b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_pause.png
new file mode 100644
index 0000000..c3b376a
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_pause.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_play.png b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_play.png
new file mode 100644
index 0000000..df59947
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_play.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_stop.png b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_stop.png
new file mode 100644
index 0000000..f42d525
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_action_stop.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..4b1d920
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_suggestions_add.png b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_suggestions_add.png
new file mode 100644
index 0000000..b880d40
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_suggestions_add.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_suggestions_delete.png b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_suggestions_delete.png
new file mode 100644
index 0000000..f9e2702
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable-xxhdpi/ic_suggestions_delete.png
Binary files differ
diff --git a/samples/browseable/MediaRouter/res/drawable/list_background.xml b/samples/browseable/MediaRouter/res/drawable/list_background.xml
new file mode 100644
index 0000000..be7240b
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/drawable/list_background.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+          android:drawable="@color/list_highlight_color" />
+    <item
+          android:drawable="@android:color/transparent" />
+</selector>
diff --git a/samples/browseable/MediaRouter/res/layout-land/grid_layout_2.xml b/samples/browseable/MediaRouter/res/layout-land/grid_layout_2.xml
new file mode 100644
index 0000000..da16123
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/layout-land/grid_layout_2.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<android.support.v7.widget.GridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:useDefaultMargins="true"
+    android:alignmentMode="alignBounds"
+    android:rowOrderPreserved="false"
+    android:columnCount="4"
+    >
+    <TextView
+        android:text="Email setup"
+        android:textSize="32dip"
+        android:layout_columnSpan="4"
+        android:layout_gravity="center_horizontal"
+    />
+    <TextView
+        android:text="You can configure email in a few simple steps:"
+        android:textSize="16dip"
+        android:layout_columnSpan="4"
+        android:layout_gravity="left"
+    />
+    <TextView
+        android:text="Email address:"
+        android:layout_gravity="right"
+    />
+    <EditText
+        android:ems="10"
+    />
+    <TextView
+        android:text="Password:"
+        android:layout_column="0"
+        android:layout_gravity="right"
+    />
+    <EditText
+        android:ems="8"
+    />
+    <Button
+        android:text="Manual setup"
+        android:layout_row="5"
+        android:layout_column="3"
+    />
+    <Button
+        android:text="Next"
+        android:layout_column="3"
+        android:layout_gravity="fill_horizontal"
+    />
+</android.support.v7.widget.GridLayout>
diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/MediaRouter/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/Basic/res/layout/activity_main.xml
copy to samples/browseable/MediaRouter/res/layout/activity_main.xml
diff --git a/samples/browseable/MediaRouter/res/layout/media_item.xml b/samples/browseable/MediaRouter/res/layout/media_item.xml
new file mode 100644
index 0000000..3a14861
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/layout/media_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Layout for list item in Library or Playlist view. Displays ImageButton
+     instead of radio button to the right of the item. -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:clickable="true"
+        android:background="@drawable/list_background"
+        android:gravity="center_vertical">
+
+    <ImageButton android:id="@+id/item_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:background="@null"/>
+
+    <TextView android:id="@+id/item_text"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_centerVertical="true"
+        android:layout_toLeftOf="@id/item_action"
+        android:layout_gravity="left"
+        android:gravity="left"/>
+</RelativeLayout>
diff --git a/samples/browseable/MediaRouter/res/layout/overlay_display_window.xml b/samples/browseable/MediaRouter/res/layout/overlay_display_window.xml
new file mode 100644
index 0000000..36b4a0d
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/layout/overlay_display_window.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:background="#000000">
+    <TextureView android:id="@+id/overlay_display_window_texture"
+               android:layout_width="0px"
+               android:layout_height="0px" />
+    <TextView android:id="@+id/overlay_display_window_title"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_gravity="top|center_horizontal" />
+</FrameLayout>
diff --git a/samples/browseable/MediaRouter/res/layout/sample_media_router.xml b/samples/browseable/MediaRouter/res/layout/sample_media_router.xml
new file mode 100644
index 0000000..1618418
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/layout/sample_media_router.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- See corresponding Java code MainActivity.java. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <!-- Tabs for media library, playlist and statistics -->
+        <TabHost android:id="@+id/tabHost"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_weight="1">
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent">
+                <TabWidget android:id="@android:id/tabs"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content" />
+
+                <FrameLayout android:id="@android:id/tabcontent"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content">
+                    <LinearLayout android:id="@+id/tab1"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical">
+                        <ListView android:id="@+id/media"
+                                  android:listSelector="@drawable/list_background"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1" />
+                    </LinearLayout>
+
+                    <LinearLayout android:id="@+id/tab2"
+                        android:layout_width="fill_parent"
+                        android:layout_height="fill_parent"
+                        android:orientation="vertical">
+                        <ListView android:id="@+id/playlist"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"/>
+                    </LinearLayout>
+
+                    <LinearLayout android:id="@+id/tab3"
+                        android:layout_width="fill_parent"
+                        android:layout_height="fill_parent"
+                        android:orientation="vertical">
+                        <TextView android:id="@+id/info"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:textAppearance="?android:attr/textAppearanceMedium"/>
+                    </LinearLayout>
+                </FrameLayout>
+            </LinearLayout>
+        </TabHost>
+
+        <!-- Control buttons for the currently selected route. -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0">
+
+            <SeekBar android:id="@+id/seekbar"
+                 android:layout_width="fill_parent"
+                 android:layout_height="wrap_content"
+                 style="?android:attr/progressBarStyleHorizontal"
+                 android:max="100"
+                 android:progress="0"
+                 android:layout_gravity="center"
+                 android:layout_weight="1"/>
+
+            <ImageButton android:id="@+id/pause_resume_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="0"
+                android:layout_gravity="right"
+                android:minWidth="48dp"
+                android:minHeight="48dp"
+                android:background="@null"
+                android:src="@drawable/ic_action_pause" />
+
+            <ImageButton android:id="@+id/stop_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="0"
+                android:layout_gravity="right"
+                android:minWidth="48dp"
+                android:minHeight="48dp"
+                android:background="@null"
+                android:src="@drawable/ic_action_stop" />
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <!-- Some content for visual interest in the case where no presentation is showing. -->
+    <FrameLayout android:id="@+id/player"
+        android:background="#ff000000"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center">
+            <SurfaceView android:id="@+id/surface_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+        </LinearLayout>
+        <TextView
+            android:textColor="#ffaaaaaa"
+            android:text="@string/sample_media_route_activity_local"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|center_horizontal" />
+    </FrameLayout>
+</LinearLayout>
diff --git a/samples/browseable/MediaRouter/res/layout/sample_media_router_presentation.xml b/samples/browseable/MediaRouter/res/layout/sample_media_router_presentation.xml
new file mode 100644
index 0000000..f029627
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/layout/sample_media_router_presentation.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- The content that we show on secondary displays.
+     See corresponding Java code PresentationWithMediaRouterActivity.java. -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#ff000000">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center">
+        <SurfaceView android:id="@+id/surface_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </LinearLayout>
+    <TextView
+        android:textColor="#ffaaaaaa"
+        android:text="@string/sample_media_route_activity_presentation"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top|center_horizontal" />
+</FrameLayout>
diff --git a/samples/browseable/MediaRouter/res/menu/sample_media_router_menu.xml b/samples/browseable/MediaRouter/res/menu/sample_media_router_menu.xml
new file mode 100644
index 0000000..8057fa8
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/menu/sample_media_router_menu.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 Google Inc.
+
+     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"
+        xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:id="@+id/media_route_menu_item"
+        android:title="@string/media_route_menu_title"
+        app:showAsAction="always"
+        app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/>
+</menu>
diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml b/samples/browseable/MediaRouter/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml
copy to samples/browseable/MediaRouter/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml b/samples/browseable/MediaRouter/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml
copy to samples/browseable/MediaRouter/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/MediaRouter/res/values-v11/template-styles.xml b/samples/browseable/MediaRouter/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/MediaRouter/res/values/arrays.xml b/samples/browseable/MediaRouter/res/values/arrays.xml
new file mode 100644
index 0000000..8d658eb
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/values/arrays.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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-array name="media_names">
+        <item>Big Buck Bunny</item>
+        <item>Elephants Dream</item>
+        <item>Sintel</item>
+        <item>Tears of Steel</item>
+    </string-array>
+
+    <string-array name="media_uris">
+        <item>http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4</item>
+        <item>http://archive.org/download/ElephantsDream_277/elephant_dreams_640_512kb.mp4</item>
+        <item>http://archive.org/download/Sintel/sintel-2048-stereo_512kb.mp4</item>
+        <item>http://archive.org/download/Tears-of-Steel/tears_of_steel_720p.mp4</item>
+    </string-array>
+</resources>
diff --git a/samples/browseable/MediaRouter/res/values/base-strings.xml b/samples/browseable/MediaRouter/res/values/base-strings.xml
new file mode 100644
index 0000000..8b494cc
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/values/base-strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">MediaRouter</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            Demonstrates how to create a custom media route provider.
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/MediaRouter/res/values/colors.xml b/samples/browseable/MediaRouter/res/values/colors.xml
new file mode 100644
index 0000000..13cf3b5
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <drawable name="blue">#770000ff</drawable>
+    <color name="list_highlight_color">#ccc</color>
+</resources>
diff --git a/samples/browseable/MediaRouter/res/values/strings.xml b/samples/browseable/MediaRouter/res/values/strings.xml
new file mode 100644
index 0000000..c750ce1
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/values/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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="sample_media_router_text">This activity demonstrates how to
+            use MediaRouter from the support library.  Select a route from the action bar.</string>
+    <string name="media_route_menu_title">Play on...</string>
+
+    <string name="library_tab_text">Library</string>
+    <string name="playlist_tab_text">Playlist</string>
+    <string name="statistics_tab_text">Statistics</string>
+
+    <string name="sample_media_route_provider_service">Media Route Provider Service Support Library Sample</string>
+    <string name="fixed_volume_route_name">Fixed Volume Remote Playback Route</string>
+    <string name="variable_volume_basic_route_name">Variable Volume (Basic) Remote Playback Route</string>
+    <string name="variable_volume_queuing_route_name">Variable Volume (Queuing) Remote Playback Route</string>
+    <string name="variable_volume_session_route_name">Variable Volume (Session) Remote Playback Route</string>
+    <string name="sample_route_description">Sample route</string>
+
+    <string name="sample_media_route_provider_remote">Remote Playback (Simulated)</string>
+    <string name="sample_media_route_activity_local">Local Playback</string>
+    <string name="sample_media_route_activity_presentation">Local Playback on Presentation Display</string>
+    <string name="playlist_item_added_text">Added to playlist!</string>
+    <string name="playlist_item_removed_text">Removed from playlist</string>
+
+</resources>
diff --git a/samples/browseable/BasicMediaRouter/res/values/dimens.xml b/samples/browseable/MediaRouter/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/BasicMediaRouter/res/values/dimens.xml
copy to samples/browseable/MediaRouter/res/values/template-dimens.xml
diff --git a/samples/browseable/MediaRouter/res/values/template-styles.xml b/samples/browseable/MediaRouter/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/MediaRouter/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/MediaRouter/src/com.example.android.common.logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/MediaRouter/src/com.example.android.common.logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/MediaRouter/src/com.example.android.common.logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/MediaRouter/src/com.example.android.common.logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/MediaRouter/src/com.example.android.common.logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/MediaRouter/src/com.example.android.common.logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/MediaRouter/src/com.example.android.common.logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/MediaRouter/src/com.example.android.common.logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/MediaRouter/src/com.example.android.common.logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/MediaRouter/src/com.example.android.common.logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/MediaRouter/src/com.example.android.common.logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/MediaRouter/src/com.example.android.common.logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/LocalPlayer.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/LocalPlayer.java
new file mode 100644
index 0000000..86f5100
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/LocalPlayer.java
@@ -0,0 +1,630 @@
+/*
+ * 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.mediarouter.player;
+
+import android.app.Activity;
+import android.app.Presentation;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.media.MediaPlayer;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.support.v7.media.MediaItemStatus;
+import android.support.v7.media.MediaRouter.RouteInfo;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.example.android.mediarouter.R;
+
+import java.io.IOException;
+
+/**
+ * Handles playback of a single media item using MediaPlayer.
+ */
+public abstract class LocalPlayer extends Player implements
+        MediaPlayer.OnPreparedListener,
+        MediaPlayer.OnCompletionListener,
+        MediaPlayer.OnErrorListener,
+        MediaPlayer.OnSeekCompleteListener {
+    private static final String TAG = "LocalPlayer";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final int STATE_IDLE = 0;
+    private static final int STATE_PLAY_PENDING = 1;
+    private static final int STATE_READY = 2;
+    private static final int STATE_PLAYING = 3;
+    private static final int STATE_PAUSED = 4;
+
+    private final Context mContext;
+    private final Handler mHandler = new Handler();
+    private MediaPlayer mMediaPlayer;
+    private int mState = STATE_IDLE;
+    private int mSeekToPos;
+    private int mVideoWidth;
+    private int mVideoHeight;
+    private Surface mSurface;
+    private SurfaceHolder mSurfaceHolder;
+
+    public LocalPlayer(Context context) {
+        mContext = context;
+
+        // reset media player
+        reset();
+    }
+
+    @Override
+    public boolean isRemotePlayback() {
+        return false;
+    }
+
+    @Override
+    public boolean isQueuingSupported() {
+        return false;
+    }
+
+    @Override
+    public void connect(RouteInfo route) {
+        if (DEBUG) {
+            Log.d(TAG, "connecting to: " + route);
+        }
+    }
+
+    @Override
+    public void release() {
+        if (DEBUG) {
+            Log.d(TAG, "releasing");
+        }
+        // release media player
+        if (mMediaPlayer != null) {
+            mMediaPlayer.stop();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+    }
+
+    // Player
+    @Override
+    public void play(final PlaylistItem item) {
+        if (DEBUG) {
+            Log.d(TAG, "play: item=" + item);
+        }
+        reset();
+        mSeekToPos = (int)item.getPosition();
+        try {
+            mMediaPlayer.setDataSource(mContext, item.getUri());
+            mMediaPlayer.prepareAsync();
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "MediaPlayer throws IllegalStateException, uri=" + item.getUri());
+        } catch (IOException e) {
+            Log.e(TAG, "MediaPlayer throws IOException, uri=" + item.getUri());
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "MediaPlayer throws IllegalArgumentException, uri=" + item.getUri());
+        } catch (SecurityException e) {
+            Log.e(TAG, "MediaPlayer throws SecurityException, uri=" + item.getUri());
+        }
+        if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING) {
+            resume();
+        } else {
+            pause();
+        }
+    }
+
+    @Override
+    public void seek(final PlaylistItem item) {
+        if (DEBUG) {
+            Log.d(TAG, "seek: item=" + item);
+        }
+        int pos = (int)item.getPosition();
+        if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
+            mMediaPlayer.seekTo(pos);
+            mSeekToPos = pos;
+        } else if (mState == STATE_IDLE || mState == STATE_PLAY_PENDING) {
+            // Seek before onPrepared() arrives,
+            // need to performed delayed seek in onPrepared()
+            mSeekToPos = pos;
+        }
+    }
+
+    @Override
+    public void getStatus(final PlaylistItem item, final boolean update) {
+        if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
+            // use mSeekToPos if we're currently seeking (mSeekToPos is reset
+            // when seeking is completed)
+            item.setDuration(mMediaPlayer.getDuration());
+            item.setPosition(mSeekToPos > 0 ?
+                    mSeekToPos : mMediaPlayer.getCurrentPosition());
+            item.setTimestamp(SystemClock.elapsedRealtime());
+        }
+        if (update && mCallback != null) {
+            mCallback.onPlaylistReady();
+        }
+    }
+
+    @Override
+    public void pause() {
+        if (DEBUG) {
+            Log.d(TAG, "pause");
+        }
+        if (mState == STATE_PLAYING) {
+            mMediaPlayer.pause();
+            mState = STATE_PAUSED;
+        }
+    }
+
+    @Override
+    public void resume() {
+        if (DEBUG) {
+            Log.d(TAG, "resume");
+        }
+        if (mState == STATE_READY || mState == STATE_PAUSED) {
+            mMediaPlayer.start();
+            mState = STATE_PLAYING;
+        } else if (mState == STATE_IDLE){
+            mState = STATE_PLAY_PENDING;
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (DEBUG) {
+            Log.d(TAG, "stop");
+        }
+        if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
+            mMediaPlayer.stop();
+            mState = STATE_IDLE;
+        }
+    }
+
+    @Override
+    public void enqueue(final PlaylistItem item) {
+        throw new UnsupportedOperationException("LocalPlayer doesn't support enqueue!");
+    }
+
+    @Override
+    public PlaylistItem remove(String iid) {
+        throw new UnsupportedOperationException("LocalPlayer doesn't support remove!");
+    }
+
+    //MediaPlayer Listeners
+    @Override
+    public void onPrepared(MediaPlayer mp) {
+        if (DEBUG) {
+            Log.d(TAG, "onPrepared");
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mState == STATE_IDLE) {
+                    mState = STATE_READY;
+                    updateVideoRect();
+                } else if (mState == STATE_PLAY_PENDING) {
+                    mState = STATE_PLAYING;
+                    updateVideoRect();
+                    if (mSeekToPos > 0) {
+                        if (DEBUG) {
+                            Log.d(TAG, "seek to initial pos: " + mSeekToPos);
+                        }
+                        mMediaPlayer.seekTo(mSeekToPos);
+                    }
+                    mMediaPlayer.start();
+                }
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onCompletion(MediaPlayer mp) {
+        if (DEBUG) {
+            Log.d(TAG, "onCompletion");
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mCallback != null) {
+                    mCallback.onCompletion();
+                }
+            }
+        });
+    }
+
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        if (DEBUG) {
+            Log.d(TAG, "onError");
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mCallback != null) {
+                    mCallback.onError();
+                }
+            }
+        });
+        // return true so that onCompletion is not called
+        return true;
+    }
+
+    @Override
+    public void onSeekComplete(MediaPlayer mp) {
+        if (DEBUG) {
+            Log.d(TAG, "onSeekComplete");
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mSeekToPos = 0;
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+        });
+    }
+
+    protected Context getContext() { return mContext; }
+    protected MediaPlayer getMediaPlayer() { return mMediaPlayer; }
+    protected int getVideoWidth() { return mVideoWidth; }
+    protected int getVideoHeight() { return mVideoHeight; }
+    protected void setSurface(Surface surface) {
+        mSurface = surface;
+        mSurfaceHolder = null;
+        updateSurface();
+    }
+
+    protected void setSurface(SurfaceHolder surfaceHolder) {
+        mSurface = null;
+        mSurfaceHolder = surfaceHolder;
+        updateSurface();
+    }
+
+    protected void removeSurface(SurfaceHolder surfaceHolder) {
+        if (surfaceHolder == mSurfaceHolder) {
+            setSurface((SurfaceHolder)null);
+        }
+    }
+
+    protected void updateSurface() {
+        if (mMediaPlayer == null) {
+            // just return if media player is already gone
+            return;
+        }
+        if (mSurface != null) {
+            // The setSurface API does not exist until V14+.
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                ICSMediaPlayer.setSurface(mMediaPlayer, mSurface);
+            } else {
+                throw new UnsupportedOperationException("MediaPlayer does not support "
+                        + "setSurface() on this version of the platform.");
+            }
+        } else if (mSurfaceHolder != null) {
+            mMediaPlayer.setDisplay(mSurfaceHolder);
+        } else {
+            mMediaPlayer.setDisplay(null);
+        }
+    }
+
+    protected abstract void updateSize();
+
+    private void reset() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.stop();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+        mMediaPlayer = new MediaPlayer();
+        mMediaPlayer.setOnPreparedListener(this);
+        mMediaPlayer.setOnCompletionListener(this);
+        mMediaPlayer.setOnErrorListener(this);
+        mMediaPlayer.setOnSeekCompleteListener(this);
+        updateSurface();
+        mState = STATE_IDLE;
+        mSeekToPos = 0;
+    }
+
+    private void updateVideoRect() {
+        if (mState != STATE_IDLE && mState != STATE_PLAY_PENDING) {
+            int width = mMediaPlayer.getVideoWidth();
+            int height = mMediaPlayer.getVideoHeight();
+            if (width > 0 && height > 0) {
+                mVideoWidth = width;
+                mVideoHeight = height;
+                updateSize();
+            } else {
+                Log.e(TAG, "video rect is 0x0!");
+                mVideoWidth = mVideoHeight = 0;
+            }
+        }
+    }
+
+    private static final class ICSMediaPlayer {
+        public static final void setSurface(MediaPlayer player, Surface surface) {
+            player.setSurface(surface);
+        }
+    }
+
+    /**
+     * Handles playback of a single media item using MediaPlayer in SurfaceView
+     */
+    public static class SurfaceViewPlayer extends LocalPlayer implements
+            SurfaceHolder.Callback {
+        private static final String TAG = "SurfaceViewPlayer";
+        private RouteInfo mRoute;
+        private final SurfaceView mSurfaceView;
+        private final FrameLayout mLayout;
+        private DemoPresentation mPresentation;
+
+        public SurfaceViewPlayer(Context context) {
+            super(context);
+
+            mLayout = (FrameLayout)((Activity)context).findViewById(R.id.player);
+            mSurfaceView = (SurfaceView)((Activity)context).findViewById(R.id.surface_view);
+
+            // add surface holder callback
+            SurfaceHolder holder = mSurfaceView.getHolder();
+            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+            holder.addCallback(this);
+        }
+
+        @Override
+        public void connect(RouteInfo route) {
+            super.connect(route);
+            mRoute = route;
+        }
+
+        @Override
+        public void release() {
+            super.release();
+
+            // dismiss presentation display
+            if (mPresentation != null) {
+                Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");
+                mPresentation.dismiss();
+                mPresentation = null;
+            }
+
+            // remove surface holder callback
+            SurfaceHolder holder = mSurfaceView.getHolder();
+            holder.removeCallback(this);
+
+            // hide the surface view when SurfaceViewPlayer is destroyed
+            mSurfaceView.setVisibility(View.GONE);
+            mLayout.setVisibility(View.GONE);
+        }
+
+        @Override
+        public void updatePresentation() {
+            // Get the current route and its presentation display.
+            Display presentationDisplay = mRoute != null ? mRoute.getPresentationDisplay() : null;
+
+            // Dismiss the current presentation if the display has changed.
+            if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
+                Log.i(TAG, "Dismissing presentation because the current route no longer "
+                        + "has a presentation display.");
+                mPresentation.dismiss();
+                mPresentation = null;
+            }
+
+            // Show a new presentation if needed.
+            if (mPresentation == null && presentationDisplay != null) {
+                Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
+                mPresentation = new DemoPresentation(getContext(), presentationDisplay);
+                mPresentation.setOnDismissListener(mOnDismissListener);
+                try {
+                    mPresentation.show();
+                } catch (WindowManager.InvalidDisplayException ex) {
+                    Log.w(TAG, "Couldn't show presentation!  Display was removed in "
+                              + "the meantime.", ex);
+                    mPresentation = null;
+                }
+            }
+
+            updateContents();
+        }
+
+        // SurfaceHolder.Callback
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format,
+                int width, int height) {
+            if (DEBUG) {
+                Log.d(TAG, "surfaceChanged: " + width + "x" + height);
+            }
+            setSurface(holder);
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            if (DEBUG) {
+                Log.d(TAG, "surfaceCreated");
+            }
+            setSurface(holder);
+            updateSize();
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            if (DEBUG) {
+                Log.d(TAG, "surfaceDestroyed");
+            }
+            removeSurface(holder);
+        }
+
+        @Override
+        protected void updateSize() {
+            int width = getVideoWidth();
+            int height = getVideoHeight();
+            if (width > 0 && height > 0) {
+                if (mPresentation == null) {
+                    int surfaceWidth = mLayout.getWidth();
+                    int surfaceHeight = mLayout.getHeight();
+
+                    // Calculate the new size of mSurfaceView, so that video is centered
+                    // inside the framelayout with proper letterboxing/pillarboxing
+                    ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
+                    if (surfaceWidth * height < surfaceHeight * width) {
+                        // Black bars on top&bottom, mSurfaceView has full layout width,
+                        // while height is derived from video's aspect ratio
+                        lp.width = surfaceWidth;
+                        lp.height = surfaceWidth * height / width;
+                    } else {
+                        // Black bars on left&right, mSurfaceView has full layout height,
+                        // while width is derived from video's aspect ratio
+                        lp.width = surfaceHeight * width / height;
+                        lp.height = surfaceHeight;
+                    }
+                    Log.i(TAG, "video rect is " + lp.width + "x" + lp.height);
+                    mSurfaceView.setLayoutParams(lp);
+                } else {
+                    mPresentation.updateSize(width, height);
+                }
+            }
+        }
+
+        private void updateContents() {
+            // Show either the content in the main activity or the content in the presentation
+            if (mPresentation != null) {
+                mLayout.setVisibility(View.GONE);
+                mSurfaceView.setVisibility(View.GONE);
+            } else {
+                mLayout.setVisibility(View.VISIBLE);
+                mSurfaceView.setVisibility(View.VISIBLE);
+            }
+        }
+
+        // Listens for when presentations are dismissed.
+        private final DialogInterface.OnDismissListener mOnDismissListener =
+                new DialogInterface.OnDismissListener() {
+            @Override
+            public void onDismiss(DialogInterface dialog) {
+                if (dialog == mPresentation) {
+                    Log.i(TAG, "Presentation dismissed.");
+                    mPresentation = null;
+                    updateContents();
+                }
+            }
+        };
+
+        // Presentation
+        private final class DemoPresentation extends Presentation {
+            private SurfaceView mPresentationSurfaceView;
+
+            public DemoPresentation(Context context, Display display) {
+                super(context, display);
+            }
+
+            @Override
+            protected void onCreate(Bundle savedInstanceState) {
+                // Be sure to call the super class.
+                super.onCreate(savedInstanceState);
+
+                // Inflate the layout.
+                setContentView(R.layout.sample_media_router_presentation);
+
+                // Set up the surface view.
+                mPresentationSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
+                SurfaceHolder holder = mPresentationSurfaceView.getHolder();
+                holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+                holder.addCallback(SurfaceViewPlayer.this);
+                Log.i(TAG, "Presentation created");
+            }
+
+            public void updateSize(int width, int height) {
+                int surfaceHeight = getWindow().getDecorView().getHeight();
+                int surfaceWidth = getWindow().getDecorView().getWidth();
+                ViewGroup.LayoutParams lp = mPresentationSurfaceView.getLayoutParams();
+                if (surfaceWidth * height < surfaceHeight * width) {
+                    lp.width = surfaceWidth;
+                    lp.height = surfaceWidth * height / width;
+                } else {
+                    lp.width = surfaceHeight * width / height;
+                    lp.height = surfaceHeight;
+                }
+                Log.i(TAG, "Presentation video rect is " + lp.width + "x" + lp.height);
+                mPresentationSurfaceView.setLayoutParams(lp);
+            }
+        }
+    }
+
+    /**
+     * Handles playback of a single media item using MediaPlayer in
+     * OverlayDisplayWindow.
+     */
+    public static class OverlayPlayer extends LocalPlayer implements
+            OverlayDisplayWindow.OverlayWindowListener {
+        private static final String TAG = "OverlayPlayer";
+        private final OverlayDisplayWindow mOverlay;
+
+        public OverlayPlayer(Context context) {
+            super(context);
+
+            mOverlay = OverlayDisplayWindow.create(getContext(),
+                    getContext().getResources().getString(
+                            R.string.sample_media_route_provider_remote),
+                    1024, 768, Gravity.CENTER);
+
+            mOverlay.setOverlayWindowListener(this);
+        }
+
+        @Override
+        public void connect(RouteInfo route) {
+            super.connect(route);
+            mOverlay.show();
+        }
+
+        @Override
+        public void release() {
+            super.release();
+            mOverlay.dismiss();
+        }
+
+        @Override
+        protected void updateSize() {
+            int width = getVideoWidth();
+            int height = getVideoHeight();
+            if (width > 0 && height > 0) {
+                mOverlay.updateAspectRatio(width, height);
+            }
+        }
+
+        // OverlayDisplayWindow.OverlayWindowListener
+        @Override
+        public void onWindowCreated(Surface surface) {
+            setSurface(surface);
+        }
+
+        @Override
+        public void onWindowCreated(SurfaceHolder surfaceHolder) {
+            setSurface(surfaceHolder);
+        }
+
+        @Override
+        public void onWindowDestroyed() {
+            setSurface((SurfaceHolder)null);
+        }
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/MainActivity.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/MainActivity.java
new file mode 100644
index 0000000..ad283dd
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/MainActivity.java
@@ -0,0 +1,724 @@
+/*
+ * 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.mediarouter.player;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.media.RemoteControlClient;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.MediaRouteActionProvider;
+import android.support.v7.app.MediaRouteDiscoveryFragment;
+import android.support.v7.media.MediaControlIntent;
+import android.support.v7.media.MediaItemStatus;
+import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
+import android.support.v7.media.MediaRouter.Callback;
+import android.support.v7.media.MediaRouter.ProviderInfo;
+import android.support.v7.media.MediaRouter.RouteInfo;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TabHost;
+import android.widget.TabHost.OnTabChangeListener;
+import android.widget.TabHost.TabSpec;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.example.android.mediarouter.R;
+import com.example.android.mediarouter.provider.SampleMediaRouteProvider;
+
+import java.io.File;
+
+/**
+ * <h3>Media Router Support Activity</h3>
+ * <p/>
+ * <p>
+ * This demonstrates how to use the {@link MediaRouter} API to build an
+ * application that allows the user to send content to various rendering
+ * targets.
+ * </p>
+ */
+public class MainActivity extends ActionBarActivity {
+    private static final String TAG = "MainActivity";
+    private static final String DISCOVERY_FRAGMENT_TAG = "DiscoveryFragment";
+
+    private MediaRouter mMediaRouter;
+    private MediaRouteSelector mSelector;
+    private LibraryAdapter mLibraryItems;
+    private PlaylistAdapter mPlayListItems;
+    private TextView mInfoTextView;
+    private ListView mLibraryView;
+    private ListView mPlayListView;
+    private ImageButton mPauseResumeButton;
+    private ImageButton mStopButton;
+    private SeekBar mSeekBar;
+    private boolean mPaused;
+    private boolean mNeedResume;
+    private boolean mSeeking;
+
+    private RemoteControlClient mRemoteControlClient;
+    private ComponentName mEventReceiver;
+    private AudioManager mAudioManager;
+    private PendingIntent mMediaPendingIntent;
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mUpdateSeekRunnable = new Runnable() {
+        @Override
+        public void run() {
+            updateProgress();
+            // update UI every 1 second
+            mHandler.postDelayed(this, 1000);
+        }
+    };
+
+    private final SessionManager mSessionManager = new SessionManager("app");
+    private Player mPlayer;
+
+    private final MediaRouter.Callback mMediaRouterCB = new MediaRouter.Callback() {
+        // Return a custom callback that will simply log all of the route events
+        // for demonstration purposes.
+        @Override
+        public void onRouteAdded(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRouteAdded: route=" + route);
+        }
+
+        @Override
+        public void onRouteChanged(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRouteChanged: route=" + route);
+        }
+
+        @Override
+        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRouteRemoved: route=" + route);
+        }
+
+        @Override
+        public void onRouteSelected(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRouteSelected: route=" + route);
+
+            mPlayer = Player.create(MainActivity.this, route);
+            mPlayer.updatePresentation();
+            mSessionManager.setPlayer(mPlayer);
+            mSessionManager.unsuspend();
+
+            registerRemoteControlClient();
+            updateUi();
+        }
+
+        @Override
+        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRouteUnselected: route=" + route);
+            unregisterRemoteControlClient();
+
+            PlaylistItem item = getCheckedPlaylistItem();
+            if (item != null) {
+                long pos = item.getPosition() +
+                        (mPaused ? 0 : (SystemClock.elapsedRealtime() - item.getTimestamp()));
+                mSessionManager.suspend(pos);
+            }
+            mPlayer.updatePresentation();
+            mPlayer.release();
+        }
+
+        @Override
+        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRouteVolumeChanged: route=" + route);
+        }
+
+        @Override
+        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
+            Log.d(TAG, "onRoutePresentationDisplayChanged: route=" + route);
+            mPlayer.updatePresentation();
+        }
+
+        @Override
+        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
+            Log.d(TAG, "onRouteProviderAdded: provider=" + provider);
+        }
+
+        @Override
+        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
+            Log.d(TAG, "onRouteProviderRemoved: provider=" + provider);
+        }
+
+        @Override
+        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
+            Log.d(TAG, "onRouteProviderChanged: provider=" + provider);
+        }
+    };
+
+    private final OnAudioFocusChangeListener mAfChangeListener = new OnAudioFocusChangeListener() {
+        @Override
+        public void onAudioFocusChange(int focusChange) {
+            if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+                Log.d(TAG, "onAudioFocusChange: LOSS_TRANSIENT");
+            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+                Log.d(TAG, "onAudioFocusChange: AUDIOFOCUS_GAIN");
+            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+                Log.d(TAG, "onAudioFocusChange: AUDIOFOCUS_LOSS");
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        // Be sure to call the super class.
+        super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            mPlayer = (Player) savedInstanceState.getSerializable("mPlayer");
+        }
+
+        // Get the media router service.
+        mMediaRouter = MediaRouter.getInstance(this);
+
+        // Create a route selector for the type of routes that we care about.
+        mSelector =
+                new MediaRouteSelector.Builder().addControlCategory(MediaControlIntent
+                        .CATEGORY_LIVE_AUDIO).addControlCategory(MediaControlIntent
+                        .CATEGORY_LIVE_VIDEO).addControlCategory(MediaControlIntent
+                        .CATEGORY_REMOTE_PLAYBACK).addControlCategory(SampleMediaRouteProvider
+                        .CATEGORY_SAMPLE_ROUTE).build();
+
+        // Add a fragment to take care of media route discovery.
+        // This fragment automatically adds or removes a callback whenever the activity
+        // is started or stopped.
+        FragmentManager fm = getSupportFragmentManager();
+        DiscoveryFragment fragment =
+                (DiscoveryFragment) fm.findFragmentByTag(DISCOVERY_FRAGMENT_TAG);
+        if (fragment == null) {
+            fragment = new DiscoveryFragment(mMediaRouterCB);
+            fragment.setRouteSelector(mSelector);
+            fm.beginTransaction().add(fragment, DISCOVERY_FRAGMENT_TAG).commit();
+        } else {
+            fragment.setCallback(mMediaRouterCB);
+            fragment.setRouteSelector(mSelector);
+        }
+
+        // Populate an array adapter with streaming media items.
+        String[] mediaNames = getResources().getStringArray(R.array.media_names);
+        String[] mediaUris = getResources().getStringArray(R.array.media_uris);
+        mLibraryItems = new LibraryAdapter();
+        for (int i = 0; i < mediaNames.length; i++) {
+            mLibraryItems.add(new MediaItem(
+                    "[streaming] " + mediaNames[i], Uri.parse(mediaUris[i]), "video/mp4"));
+        }
+
+        // Scan local external storage directory for media files.
+        File externalDir = Environment.getExternalStorageDirectory();
+        if (externalDir != null) {
+            File list[] = externalDir.listFiles();
+            if (list != null) {
+                for (int i = 0; i < list.length; i++) {
+                    String filename = list[i].getName();
+                    if (filename.matches(".*\\.(m4v|mp4)")) {
+                        mLibraryItems.add(new MediaItem(
+                                "[local] " + filename, Uri.fromFile(list[i]), "video/mp4"));
+                    }
+                }
+            }
+        }
+
+        mPlayListItems = new PlaylistAdapter();
+
+        // Initialize the layout.
+        setContentView(R.layout.sample_media_router);
+
+        TabHost tabHost = (TabHost) findViewById(R.id.tabHost);
+        tabHost.setup();
+        String tabName = getResources().getString(R.string.library_tab_text);
+        TabSpec spec1 = tabHost.newTabSpec(tabName);
+        spec1.setContent(R.id.tab1);
+        spec1.setIndicator(tabName);
+
+        tabName = getResources().getString(R.string.playlist_tab_text);
+        TabSpec spec2 = tabHost.newTabSpec(tabName);
+        spec2.setIndicator(tabName);
+        spec2.setContent(R.id.tab2);
+
+        tabName = getResources().getString(R.string.statistics_tab_text);
+        TabSpec spec3 = tabHost.newTabSpec(tabName);
+        spec3.setIndicator(tabName);
+        spec3.setContent(R.id.tab3);
+
+        tabHost.addTab(spec1);
+        tabHost.addTab(spec2);
+        tabHost.addTab(spec3);
+        tabHost.setOnTabChangedListener(new OnTabChangeListener() {
+            @Override
+            public void onTabChanged(String arg0) {
+                updateUi();
+            }
+        });
+
+        mLibraryView = (ListView) findViewById(R.id.media);
+        mLibraryView.setAdapter(mLibraryItems);
+        mLibraryView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        mLibraryView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                updateButtons();
+            }
+        });
+
+        mPlayListView = (ListView) findViewById(R.id.playlist);
+        mPlayListView.setAdapter(mPlayListItems);
+        mPlayListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        mPlayListView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                updateButtons();
+            }
+        });
+
+        mInfoTextView = (TextView) findViewById(R.id.info);
+
+        mPauseResumeButton = (ImageButton) findViewById(R.id.pause_resume_button);
+        mPauseResumeButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPaused = !mPaused;
+                if (mPaused) {
+                    mSessionManager.pause();
+                } else {
+                    mSessionManager.resume();
+                }
+            }
+        });
+
+        mStopButton = (ImageButton) findViewById(R.id.stop_button);
+        mStopButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPaused = false;
+                mSessionManager.stop();
+            }
+        });
+
+        mSeekBar = (SeekBar) findViewById(R.id.seekbar);
+        mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                PlaylistItem item = getCheckedPlaylistItem();
+                if (fromUser && item != null && item.getDuration() > 0) {
+                    long pos = progress * item.getDuration() / 100;
+                    mSessionManager.seek(item.getItemId(), pos);
+                    item.setPosition(pos);
+                    item.setTimestamp(SystemClock.elapsedRealtime());
+                }
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+                mSeeking = true;
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                mSeeking = false;
+                updateUi();
+            }
+        });
+
+        // Schedule Ui update
+        mHandler.postDelayed(mUpdateSeekRunnable, 1000);
+
+        // Build the PendingIntent for the remote control client
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        mEventReceiver =
+                new ComponentName(getPackageName(), SampleMediaButtonReceiver.class.getName());
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        mediaButtonIntent.setComponent(mEventReceiver);
+        mMediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
+
+        // Create and register the remote control client
+        registerRemoteControlClient();
+
+        // Set up playback manager and player
+        mPlayer = Player.create(MainActivity.this, mMediaRouter.getSelectedRoute());
+        mSessionManager.setPlayer(mPlayer);
+        mSessionManager.setCallback(new SessionManager.Callback() {
+            @Override
+            public void onStatusChanged() {
+                updateUi();
+            }
+
+            @Override
+            public void onItemChanged(PlaylistItem item) {
+            }
+        });
+
+        updateUi();
+    }
+
+    private void registerRemoteControlClient() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // Create the RCC and register with AudioManager and MediaRouter
+            mAudioManager.requestAudioFocus(mAfChangeListener, AudioManager.STREAM_MUSIC,
+                    AudioManager.AUDIOFOCUS_GAIN);
+            mAudioManager.registerMediaButtonEventReceiver(mEventReceiver);
+            mRemoteControlClient = new RemoteControlClient(mMediaPendingIntent);
+            mAudioManager.registerRemoteControlClient(mRemoteControlClient);
+            mMediaRouter.addRemoteControlClient(mRemoteControlClient);
+            SampleMediaButtonReceiver.setActivity(MainActivity.this);
+            mRemoteControlClient.setTransportControlFlags(RemoteControlClient
+                    .FLAG_KEY_MEDIA_PLAY_PAUSE);
+            mRemoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
+        }
+    }
+
+    private void unregisterRemoteControlClient() {
+        // Unregister the RCC with AudioManager and MediaRouter
+        if (mRemoteControlClient != null) {
+            mRemoteControlClient.setTransportControlFlags(0);
+            mAudioManager.abandonAudioFocus(mAfChangeListener);
+            mAudioManager.unregisterMediaButtonEventReceiver(mEventReceiver);
+            mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
+            mMediaRouter.removeRemoteControlClient(mRemoteControlClient);
+            SampleMediaButtonReceiver.setActivity(null);
+            mRemoteControlClient = null;
+        }
+    }
+
+    public boolean handleMediaKey(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+            switch (event.getKeyCode()) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: {
+                    Log.d(TAG, "Received Play/Pause event from RemoteControlClient");
+                    mPaused = !mPaused;
+                    if (mPaused) {
+                        mSessionManager.pause();
+                    } else {
+                        mSessionManager.resume();
+                    }
+                    return true;
+                }
+                case KeyEvent.KEYCODE_MEDIA_PLAY: {
+                    Log.d(TAG, "Received Play event from RemoteControlClient");
+                    if (mPaused) {
+                        mPaused = false;
+                        mSessionManager.resume();
+                    }
+                    return true;
+                }
+                case KeyEvent.KEYCODE_MEDIA_PAUSE: {
+                    Log.d(TAG, "Received Pause event from RemoteControlClient");
+                    if (!mPaused) {
+                        mPaused = true;
+                        mSessionManager.pause();
+                    }
+                    return true;
+                }
+                case KeyEvent.KEYCODE_MEDIA_STOP: {
+                    Log.d(TAG, "Received Stop event from RemoteControlClient");
+                    mPaused = false;
+                    mSessionManager.stop();
+                    return true;
+                }
+                default:
+                    break;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return handleMediaKey(event) || super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return handleMediaKey(event) || super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public void onStart() {
+        // Be sure to call the super class.
+        super.onStart();
+    }
+
+    @Override
+    public void onPause() {
+        // pause media player for local playback case only
+        if (!mPlayer.isRemotePlayback() && !mPaused) {
+            mNeedResume = true;
+            mSessionManager.pause();
+        }
+        super.onPause();
+    }
+
+    @Override
+    public void onResume() {
+        // resume media player for local playback case only
+        if (!mPlayer.isRemotePlayback() && mNeedResume) {
+            mSessionManager.resume();
+            mNeedResume = false;
+        }
+        super.onResume();
+    }
+
+    @Override
+    public void onDestroy() {
+        // Unregister the remote control client
+        unregisterRemoteControlClient();
+
+        mPaused = false;
+        mSessionManager.stop();
+        mPlayer.release();
+        super.onDestroy();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Be sure to call the super class.
+        super.onCreateOptionsMenu(menu);
+
+        // Inflate the menu and configure the media router action provider.
+        getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
+
+        MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
+        MediaRouteActionProvider mediaRouteActionProvider =
+                (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
+        mediaRouteActionProvider.setRouteSelector(mSelector);
+
+        // Return true to show the menu.
+        return true;
+    }
+
+    private void updateProgress() {
+        // Estimate content position from last status time and elapsed time.
+        // (Note this might be slightly out of sync with remote side, however
+        // it avoids frequent polling the MRP.)
+        int progress = 0;
+        PlaylistItem item = getCheckedPlaylistItem();
+        if (item != null) {
+            int state = item.getState();
+            long duration = item.getDuration();
+            if (duration <= 0) {
+                if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING ||
+                        state == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                    mSessionManager.updateStatus();
+                }
+            } else {
+                long position = item.getPosition();
+                long timeDelta =
+                        mPaused ? 0 : (SystemClock.elapsedRealtime() - item.getTimestamp());
+                progress = (int) (100.0 * (position + timeDelta) / duration);
+            }
+        }
+        mSeekBar.setProgress(progress);
+    }
+
+    private void updateUi() {
+        updatePlaylist();
+        updateRouteDescription();
+        updateButtons();
+    }
+
+    private void updatePlaylist() {
+        mPlayListItems.clear();
+        for (PlaylistItem item : mSessionManager.getPlaylist()) {
+            mPlayListItems.add(item);
+        }
+        mPlayListView.invalidate();
+    }
+
+
+    private void updateRouteDescription() {
+        RouteInfo route = mMediaRouter.getSelectedRoute();
+        mInfoTextView.setText(
+                "Currently selected route:" + "\nName: " + route.getName() + "\nProvider: " +
+                        route.getProvider().getPackageName() + "\nDescription: " +
+                        route.getDescription() + "\nStatistics: " +
+                        mSessionManager.getStatistics());
+    }
+
+    private void updateButtons() {
+        MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute();
+        // show pause or resume icon depending on current state
+        mPauseResumeButton.setImageResource(
+                mPaused ? R.drawable.ic_action_play : R.drawable.ic_action_pause);
+        // disable pause/resume/stop if no session
+        mPauseResumeButton.setEnabled(mSessionManager.hasSession());
+        mStopButton.setEnabled(mSessionManager.hasSession());
+        // only enable seek bar when duration is known
+        PlaylistItem item = getCheckedPlaylistItem();
+        mSeekBar.setEnabled(item != null && item.getDuration() > 0);
+        if (mRemoteControlClient != null) {
+            mRemoteControlClient.setPlaybackState(mPaused ? RemoteControlClient.PLAYSTATE_PAUSED :
+                    RemoteControlClient.PLAYSTATE_PLAYING);
+        }
+    }
+
+    private PlaylistItem getCheckedPlaylistItem() {
+        int count = mPlayListView.getCount();
+        int index = mPlayListView.getCheckedItemPosition();
+        if (count > 0) {
+            if (index < 0 || index >= count) {
+                index = 0;
+                mPlayListView.setItemChecked(0, true);
+            }
+            return mPlayListItems.getItem(index);
+        }
+        return null;
+    }
+
+    public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment {
+        private static final String TAG = "DiscoveryFragment";
+        private Callback mCallback;
+
+        public DiscoveryFragment() {
+            mCallback = null;
+        }
+
+        public DiscoveryFragment(Callback cb) {
+            mCallback = cb;
+        }
+
+        public void setCallback(Callback cb) {
+            mCallback = cb;
+        }
+
+        @Override
+        public Callback onCreateCallback() {
+            return mCallback;
+        }
+
+        @Override
+        public int onPrepareCallbackFlags() {
+            // Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will
+            // observe and log all route events including those that are for routes
+            // that do not match our selector.  This is only for demonstration purposes
+            // and should not be needed by most applications.
+            return super.onPrepareCallbackFlags() | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS;
+        }
+    }
+
+    private static final class MediaItem {
+        public final String mName;
+        public final Uri mUri;
+        public final String mMime;
+
+        public MediaItem(String name, Uri uri, String mime) {
+            mName = name;
+            mUri = uri;
+            mMime = mime;
+        }
+
+        @Override
+        public String toString() {
+            return mName;
+        }
+    }
+
+    private final class LibraryAdapter extends ArrayAdapter<MediaItem> {
+        public LibraryAdapter() {
+            super(MainActivity.this, R.layout.media_item);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View v;
+            if (convertView == null) {
+                v = getLayoutInflater().inflate(R.layout.media_item, null);
+            } else {
+                v = convertView;
+            }
+
+            final MediaItem item = getItem(position);
+
+            TextView tv = (TextView) v.findViewById(R.id.item_text);
+            tv.setText(item.mName);
+
+            ImageButton b = (ImageButton) v.findViewById(R.id.item_action);
+            b.setImageResource(R.drawable.ic_suggestions_add);
+            b.setTag(item);
+            b.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (item != null) {
+                        mSessionManager.add(item.mUri, item.mMime);
+                        Toast.makeText(MainActivity.this, R.string.playlist_item_added_text,
+                                Toast.LENGTH_SHORT).show();
+                    }
+                }
+            });
+
+            return v;
+        }
+    }
+
+    private final class PlaylistAdapter extends ArrayAdapter<PlaylistItem> {
+        public PlaylistAdapter() {
+            super(MainActivity.this, R.layout.media_item);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View v;
+            if (convertView == null) {
+                v = getLayoutInflater().inflate(R.layout.media_item, null);
+            } else {
+                v = convertView;
+            }
+
+            final PlaylistItem item = getItem(position);
+
+            TextView tv = (TextView) v.findViewById(R.id.item_text);
+            tv.setText(item.toString());
+
+            ImageButton b = (ImageButton) v.findViewById(R.id.item_action);
+            b.setImageResource(R.drawable.ic_suggestions_delete);
+            b.setTag(item);
+            b.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (item != null) {
+                        mSessionManager.remove(item.getItemId());
+                        Toast.makeText(MainActivity.this, R.string.playlist_item_removed_text,
+                                Toast.LENGTH_SHORT).show();
+                    }
+                }
+            });
+
+            return v;
+        }
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/OverlayDisplayWindow.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/OverlayDisplayWindow.java
new file mode 100644
index 0000000..5834830
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/OverlayDisplayWindow.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.mediarouter.player;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.example.android.mediarouter.R;
+
+/**
+ * Manages an overlay display window, used for simulating remote playback.
+ */
+public abstract class OverlayDisplayWindow {
+    private static final String TAG = "OverlayDisplayWindow";
+    private static final boolean DEBUG = false;
+
+    private static final float WINDOW_ALPHA = 0.8f;
+    private static final float INITIAL_SCALE = 0.5f;
+    private static final float MIN_SCALE = 0.3f;
+    private static final float MAX_SCALE = 1.0f;
+
+    protected final Context mContext;
+    protected final String mName;
+    protected final int mWidth;
+    protected final int mHeight;
+    protected final int mGravity;
+    protected OverlayWindowListener mListener;
+
+    protected OverlayDisplayWindow(Context context, String name,
+            int width, int height, int gravity) {
+        mContext = context;
+        mName = name;
+        mWidth = width;
+        mHeight = height;
+        mGravity = gravity;
+    }
+
+    public static OverlayDisplayWindow create(Context context, String name,
+            int width, int height, int gravity) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return new JellybeanMr1Impl(context, name, width, height, gravity);
+        } else {
+            return new LegacyImpl(context, name, width, height, gravity);
+        }
+    }
+
+    public void setOverlayWindowListener(OverlayWindowListener listener) {
+        mListener = listener;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public abstract void show();
+
+    public abstract void dismiss();
+
+    public abstract void updateAspectRatio(int width, int height);
+
+    // Watches for significant changes in the overlay display window lifecycle.
+    public interface OverlayWindowListener {
+        public void onWindowCreated(Surface surface);
+        public void onWindowCreated(SurfaceHolder surfaceHolder);
+        public void onWindowDestroyed();
+    }
+
+    /**
+     * Implementation for older versions.
+     */
+    private static final class LegacyImpl extends OverlayDisplayWindow {
+        private final WindowManager mWindowManager;
+
+        private boolean mWindowVisible;
+        private SurfaceView mSurfaceView;
+
+        public LegacyImpl(Context context, String name,
+                int width, int height, int gravity) {
+            super(context, name, width, height, gravity);
+
+            mWindowManager = (WindowManager)context.getSystemService(
+                    Context.WINDOW_SERVICE);
+        }
+
+        @Override
+        public void show() {
+            if (!mWindowVisible) {
+                mSurfaceView = new SurfaceView(mContext);
+
+                Display display = mWindowManager.getDefaultDisplay();
+
+                WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+                params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+                params.alpha = WINDOW_ALPHA;
+                params.gravity = Gravity.LEFT | Gravity.BOTTOM;
+                params.setTitle(mName);
+
+                int width = (int)(display.getWidth() * INITIAL_SCALE);
+                int height = (int)(display.getHeight() * INITIAL_SCALE);
+                if (mWidth > mHeight) {
+                    height = mHeight * width / mWidth;
+                } else {
+                    width = mWidth * height / mHeight;
+                }
+                params.width = width;
+                params.height = height;
+
+                mWindowManager.addView(mSurfaceView, params);
+                mWindowVisible = true;
+
+                SurfaceHolder holder = mSurfaceView.getHolder();
+                holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+                mListener.onWindowCreated(holder);
+            }
+        }
+
+        @Override
+        public void dismiss() {
+            if (mWindowVisible) {
+                mListener.onWindowDestroyed();
+
+                mWindowManager.removeView(mSurfaceView);
+                mWindowVisible = false;
+            }
+        }
+
+        @Override
+        public void updateAspectRatio(int width, int height) {
+        }
+    }
+
+    /**
+     * Implementation for API version 17+.
+     */
+    private static final class JellybeanMr1Impl extends OverlayDisplayWindow {
+        // When true, disables support for moving and resizing the overlay.
+        // The window is made non-touchable, which makes it possible to
+        // directly interact with the content underneath.
+        private static final boolean DISABLE_MOVE_AND_RESIZE = false;
+
+        private final DisplayManager mDisplayManager;
+        private final WindowManager mWindowManager;
+
+        private final Display mDefaultDisplay;
+        private final DisplayMetrics mDefaultDisplayMetrics = new DisplayMetrics();
+
+        private View mWindowContent;
+        private WindowManager.LayoutParams mWindowParams;
+        private TextureView mTextureView;
+        private TextView mNameTextView;
+
+        private GestureDetector mGestureDetector;
+        private ScaleGestureDetector mScaleGestureDetector;
+
+        private boolean mWindowVisible;
+        private int mWindowX;
+        private int mWindowY;
+        private float mWindowScale;
+
+        private float mLiveTranslationX;
+        private float mLiveTranslationY;
+        private float mLiveScale = 1.0f;
+
+        public JellybeanMr1Impl(Context context, String name,
+                int width, int height, int gravity) {
+            super(context, name, width, height, gravity);
+
+            mDisplayManager = (DisplayManager)context.getSystemService(
+                    Context.DISPLAY_SERVICE);
+            mWindowManager = (WindowManager)context.getSystemService(
+                    Context.WINDOW_SERVICE);
+
+            mDefaultDisplay = mWindowManager.getDefaultDisplay();
+            updateDefaultDisplayInfo();
+
+            createWindow();
+        }
+
+        @Override
+        public void show() {
+            if (!mWindowVisible) {
+                mDisplayManager.registerDisplayListener(mDisplayListener, null);
+                if (!updateDefaultDisplayInfo()) {
+                    mDisplayManager.unregisterDisplayListener(mDisplayListener);
+                    return;
+                }
+
+                clearLiveState();
+                updateWindowParams();
+                mWindowManager.addView(mWindowContent, mWindowParams);
+                mWindowVisible = true;
+            }
+        }
+
+        @Override
+        public void dismiss() {
+            if (mWindowVisible) {
+                mDisplayManager.unregisterDisplayListener(mDisplayListener);
+                mWindowManager.removeView(mWindowContent);
+                mWindowVisible = false;
+            }
+        }
+
+        @Override
+        public void updateAspectRatio(int width, int height) {
+            if (mWidth * height < mHeight * width) {
+                mTextureView.getLayoutParams().width = mWidth;
+                mTextureView.getLayoutParams().height = mWidth * height / width;
+            } else {
+                mTextureView.getLayoutParams().width = mHeight * width / height;
+                mTextureView.getLayoutParams().height = mHeight;
+            }
+            relayout();
+        }
+
+        private void relayout() {
+            if (mWindowVisible) {
+                updateWindowParams();
+                mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+            }
+        }
+
+        private boolean updateDefaultDisplayInfo() {
+            mDefaultDisplay.getMetrics(mDefaultDisplayMetrics);
+            return true;
+        }
+
+        private void createWindow() {
+            LayoutInflater inflater = LayoutInflater.from(mContext);
+
+            mWindowContent = inflater.inflate(
+                    R.layout.overlay_display_window, null);
+            mWindowContent.setOnTouchListener(mOnTouchListener);
+
+            mTextureView = (TextureView)mWindowContent.findViewById(
+                    R.id.overlay_display_window_texture);
+            mTextureView.setPivotX(0);
+            mTextureView.setPivotY(0);
+            mTextureView.getLayoutParams().width = mWidth;
+            mTextureView.getLayoutParams().height = mHeight;
+            mTextureView.setOpaque(false);
+            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+            mNameTextView = (TextView)mWindowContent.findViewById(
+                    R.id.overlay_display_window_title);
+            mNameTextView.setText(mName);
+
+            mWindowParams = new WindowManager.LayoutParams(
+                    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+            if (DISABLE_MOVE_AND_RESIZE) {
+                mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+            }
+            mWindowParams.alpha = WINDOW_ALPHA;
+            mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
+            mWindowParams.setTitle(mName);
+
+            mGestureDetector = new GestureDetector(mContext, mOnGestureListener);
+            mScaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
+
+            // Set the initial position and scale.
+            // The position and scale will be clamped when the display is first shown.
+            mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
+                    0 : mDefaultDisplayMetrics.widthPixels;
+            mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
+                    0 : mDefaultDisplayMetrics.heightPixels;
+            Log.d(TAG, mDefaultDisplayMetrics.toString());
+            mWindowScale = INITIAL_SCALE;
+
+            // calculate and save initial settings
+            updateWindowParams();
+            saveWindowParams();
+        }
+
+        private void updateWindowParams() {
+            float scale = mWindowScale * mLiveScale;
+            scale = Math.min(scale, (float)mDefaultDisplayMetrics.widthPixels / mWidth);
+            scale = Math.min(scale, (float)mDefaultDisplayMetrics.heightPixels / mHeight);
+            scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
+
+            float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
+            int width = (int)(mWidth * scale);
+            int height = (int)(mHeight * scale);
+            int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
+            int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
+            x = Math.max(0, Math.min(x, mDefaultDisplayMetrics.widthPixels - width));
+            y = Math.max(0, Math.min(y, mDefaultDisplayMetrics.heightPixels - height));
+
+            if (DEBUG) {
+                Log.d(TAG, "updateWindowParams: scale=" + scale
+                        + ", offsetScale=" + offsetScale
+                        + ", x=" + x + ", y=" + y
+                        + ", width=" + width + ", height=" + height);
+            }
+
+            mTextureView.setScaleX(scale);
+            mTextureView.setScaleY(scale);
+
+            mTextureView.setTranslationX(
+                    (mWidth - mTextureView.getLayoutParams().width) * scale / 2);
+            mTextureView.setTranslationY(
+                    (mHeight - mTextureView.getLayoutParams().height) * scale / 2);
+
+            mWindowParams.x = x;
+            mWindowParams.y = y;
+            mWindowParams.width = width;
+            mWindowParams.height = height;
+        }
+
+        private void saveWindowParams() {
+            mWindowX = mWindowParams.x;
+            mWindowY = mWindowParams.y;
+            mWindowScale = mTextureView.getScaleX();
+            clearLiveState();
+        }
+
+        private void clearLiveState() {
+            mLiveTranslationX = 0f;
+            mLiveTranslationY = 0f;
+            mLiveScale = 1.0f;
+        }
+
+        private final DisplayManager.DisplayListener mDisplayListener =
+                new DisplayManager.DisplayListener() {
+            @Override
+            public void onDisplayAdded(int displayId) {
+            }
+
+            @Override
+            public void onDisplayChanged(int displayId) {
+                if (displayId == mDefaultDisplay.getDisplayId()) {
+                    if (updateDefaultDisplayInfo()) {
+                        relayout();
+                    } else {
+                        dismiss();
+                    }
+                }
+            }
+
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                if (displayId == mDefaultDisplay.getDisplayId()) {
+                    dismiss();
+                }
+            }
+        };
+
+        private final SurfaceTextureListener mSurfaceTextureListener =
+                new SurfaceTextureListener() {
+            @Override
+            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
+                    int width, int height) {
+                if (mListener != null) {
+                    mListener.onWindowCreated(new Surface(surfaceTexture));
+                }
+            }
+
+            @Override
+            public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+                if (mListener != null) {
+                    mListener.onWindowDestroyed();
+                }
+                return true;
+            }
+
+            @Override
+            public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
+                    int width, int height) {
+            }
+
+            @Override
+            public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+            }
+        };
+
+        private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                // Work in screen coordinates.
+                final float oldX = event.getX();
+                final float oldY = event.getY();
+                event.setLocation(event.getRawX(), event.getRawY());
+
+                mGestureDetector.onTouchEvent(event);
+                mScaleGestureDetector.onTouchEvent(event);
+
+                switch (event.getActionMasked()) {
+                    case MotionEvent.ACTION_UP:
+                    case MotionEvent.ACTION_CANCEL:
+                        saveWindowParams();
+                        break;
+                }
+
+                // Revert to window coordinates.
+                event.setLocation(oldX, oldY);
+                return true;
+            }
+        };
+
+        private final GestureDetector.OnGestureListener mOnGestureListener =
+                new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                    float distanceX, float distanceY) {
+                mLiveTranslationX -= distanceX;
+                mLiveTranslationY -= distanceY;
+                relayout();
+                return true;
+            }
+        };
+
+        private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
+                new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+            @Override
+            public boolean onScale(ScaleGestureDetector detector) {
+                mLiveScale *= detector.getScaleFactor();
+                relayout();
+                return true;
+            }
+        };
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/Player.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/Player.java
new file mode 100644
index 0000000..f842cf6
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/Player.java
@@ -0,0 +1,81 @@
+/*
+ * 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.mediarouter.player;
+
+import android.content.Context;
+import android.support.v7.media.MediaControlIntent;
+import android.support.v7.media.MediaRouter.RouteInfo;
+
+/**
+ * Abstraction of common playback operations of media items, such as play,
+ * seek, etc. Used by PlaybackManager as a backend to handle actual playback
+ * of media items.
+ */
+public abstract class Player {
+    protected Callback mCallback;
+
+    public abstract boolean isRemotePlayback();
+    public abstract boolean isQueuingSupported();
+
+    public abstract void connect(RouteInfo route);
+    public abstract void release();
+
+    // basic operations that are always supported
+    public abstract void play(final PlaylistItem item);
+    public abstract void seek(final PlaylistItem item);
+    public abstract void getStatus(final PlaylistItem item, final boolean update);
+    public abstract void pause();
+    public abstract void resume();
+    public abstract void stop();
+
+    // advanced queuing (enqueue & remove) are only supported
+    // if isQueuingSupported() returns true
+    public abstract void enqueue(final PlaylistItem item);
+    public abstract PlaylistItem remove(String iid);
+
+    // route statistics
+    public void updateStatistics() {}
+    public String getStatistics() { return ""; }
+
+    // presentation display
+    public void updatePresentation() {}
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    public static Player create(Context context, RouteInfo route) {
+        Player player;
+        if (route != null && route.supportsControlCategory(
+                MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
+            player = new RemotePlayer(context);
+        } else if (route != null) {
+            player = new LocalPlayer.SurfaceViewPlayer(context);
+        } else {
+            player = new LocalPlayer.OverlayPlayer(context);
+        }
+        player.connect(route);
+        return player;
+    }
+
+    public interface Callback {
+        void onError();
+        void onCompletion();
+        void onPlaylistChanged();
+        void onPlaylistReady();
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/PlaylistItem.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/PlaylistItem.java
new file mode 100644
index 0000000..a560538
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/PlaylistItem.java
@@ -0,0 +1,130 @@
+/*
+ * 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.mediarouter.player;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.support.v7.media.MediaItemStatus;
+
+/**
+ * PlaylistItem helps keep track of the current status of an media item.
+ */
+public final class PlaylistItem {
+    // immutables
+    private final String mSessionId;
+    private final String mItemId;
+    private final Uri mUri;
+    private final String mMime;
+    private final PendingIntent mUpdateReceiver;
+    // changeable states
+    private int mPlaybackState = MediaItemStatus.PLAYBACK_STATE_PENDING;
+    private long mContentPosition;
+    private long mContentDuration;
+    private long mTimestamp;
+    private String mRemoteItemId;
+
+    public PlaylistItem(String qid, String iid, Uri uri, String mime, PendingIntent pi) {
+        mSessionId = qid;
+        mItemId = iid;
+        mUri = uri;
+        mMime = mime;
+        mUpdateReceiver = pi;
+        setTimestamp(SystemClock.elapsedRealtime());
+    }
+
+    public void setRemoteItemId(String riid) {
+        mRemoteItemId = riid;
+    }
+
+    public void setState(int state) {
+        mPlaybackState = state;
+    }
+
+    public void setPosition(long pos) {
+        mContentPosition = pos;
+    }
+
+    public void setTimestamp(long ts) {
+        mTimestamp = ts;
+    }
+
+    public void setDuration(long duration) {
+        mContentDuration = duration;
+    }
+
+    public String getSessionId() {
+        return mSessionId;
+    }
+
+    public String getItemId() {
+        return mItemId;
+    }
+
+    public String getRemoteItemId() {
+        return mRemoteItemId;
+    }
+
+    public Uri getUri() {
+        return mUri;
+    }
+
+    public PendingIntent getUpdateReceiver() {
+        return mUpdateReceiver;
+    }
+
+    public int getState() {
+        return mPlaybackState;
+    }
+
+    public long getPosition() {
+        return mContentPosition;
+    }
+
+    public long getDuration() {
+        return mContentDuration;
+    }
+
+    public long getTimestamp() {
+        return mTimestamp;
+    }
+
+    public MediaItemStatus getStatus() {
+        return new MediaItemStatus.Builder(mPlaybackState)
+            .setContentPosition(mContentPosition)
+            .setContentDuration(mContentDuration)
+            .setTimestamp(mTimestamp)
+            .build();
+    }
+
+    @Override
+    public String toString() {
+        String state[] = {
+            "PENDING",
+            "PLAYING",
+            "PAUSED",
+            "BUFFERING",
+            "FINISHED",
+            "CANCELED",
+            "INVALIDATED",
+            "ERROR"
+        };
+        return "[" + mSessionId + "|" + mItemId + "|"
+            + (mRemoteItemId != null ? mRemoteItemId : "-") + "|"
+            + state[mPlaybackState] + "] " + mUri.toString();
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/RemotePlayer.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/RemotePlayer.java
new file mode 100644
index 0000000..6726718
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/RemotePlayer.java
@@ -0,0 +1,482 @@
+/*
+ * 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.mediarouter.player;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.media.MediaItemStatus;
+import android.support.v7.media.MediaRouter.ControlRequestCallback;
+import android.support.v7.media.MediaRouter.RouteInfo;
+import android.support.v7.media.MediaSessionStatus;
+import android.support.v7.media.RemotePlaybackClient;
+import android.support.v7.media.RemotePlaybackClient.ItemActionCallback;
+import android.support.v7.media.RemotePlaybackClient.SessionActionCallback;
+import android.support.v7.media.RemotePlaybackClient.StatusCallback;
+import android.util.Log;
+
+import com.example.android.mediarouter.player.Player;
+import com.example.android.mediarouter.player.PlaylistItem;
+import com.example.android.mediarouter.provider.SampleMediaRouteProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles playback of media items using a remote route.
+ *
+ * This class is used as a backend by PlaybackManager to feed media items to
+ * the remote route. When the remote route doesn't support queuing, media items
+ * are fed one-at-a-time; otherwise media items are enqueued to the remote side.
+ */
+public class RemotePlayer extends Player {
+    private static final String TAG = "RemotePlayer";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private Context mContext;
+    private RouteInfo mRoute;
+    private boolean mEnqueuePending;
+    private String mStatsInfo = "";
+    private List<PlaylistItem> mTempQueue = new ArrayList<PlaylistItem>();
+
+    private RemotePlaybackClient mClient;
+    private StatusCallback mStatusCallback = new StatusCallback() {
+        @Override
+        public void onItemStatusChanged(Bundle data,
+                String sessionId, MediaSessionStatus sessionStatus,
+                String itemId, MediaItemStatus itemStatus) {
+            logStatus("onItemStatusChanged", sessionId, sessionStatus, itemId, itemStatus);
+            if (mCallback != null) {
+                if (itemStatus.getPlaybackState() ==
+                        MediaItemStatus.PLAYBACK_STATE_FINISHED) {
+                    mCallback.onCompletion();
+                } else if (itemStatus.getPlaybackState() ==
+                        MediaItemStatus.PLAYBACK_STATE_ERROR) {
+                    mCallback.onError();
+                }
+            }
+        }
+
+        @Override
+        public void onSessionStatusChanged(Bundle data,
+                String sessionId, MediaSessionStatus sessionStatus) {
+            logStatus("onSessionStatusChanged", sessionId, sessionStatus, null, null);
+            if (mCallback != null) {
+                mCallback.onPlaylistChanged();
+            }
+        }
+
+        @Override
+        public void onSessionChanged(String sessionId) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionChanged: sessionId=" + sessionId);
+            }
+        }
+    };
+
+    public RemotePlayer(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public boolean isRemotePlayback() {
+        return true;
+    }
+
+    @Override
+    public boolean isQueuingSupported() {
+        return mClient.isQueuingSupported();
+    }
+
+    @Override
+    public void connect(RouteInfo route) {
+        mRoute = route;
+        mClient = new RemotePlaybackClient(mContext, route);
+        mClient.setStatusCallback(mStatusCallback);
+
+        if (DEBUG) {
+            Log.d(TAG, "connected to: " + route
+                    + ", isRemotePlaybackSupported: " + mClient.isRemotePlaybackSupported()
+                    + ", isQueuingSupported: "+ mClient.isQueuingSupported());
+        }
+    }
+
+    @Override
+    public void release() {
+        mClient.release();
+
+        if (DEBUG) {
+            Log.d(TAG, "released.");
+        }
+    }
+
+    // basic playback operations that are always supported
+    @Override
+    public void play(final PlaylistItem item) {
+        if (DEBUG) {
+            Log.d(TAG, "play: item=" + item);
+        }
+        mClient.play(item.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                logStatus("play: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+                item.setRemoteItemId(itemId);
+                if (item.getPosition() > 0) {
+                    seekInternal(item);
+                }
+                if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                    pause();
+                }
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("play: failed", error, code);
+            }
+        });
+    }
+
+    @Override
+    public void seek(final PlaylistItem item) {
+        seekInternal(item);
+    }
+
+    @Override
+    public void getStatus(final PlaylistItem item, final boolean update) {
+        if (!mClient.hasSession() || item.getRemoteItemId() == null) {
+            // if session is not valid or item id not assigend yet.
+            // just return, it's not fatal
+            return;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "getStatus: item=" + item + ", update=" + update);
+        }
+        mClient.getStatus(item.getRemoteItemId(), null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                logStatus("getStatus: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+                int state = itemStatus.getPlaybackState();
+                if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING
+                        || state == MediaItemStatus.PLAYBACK_STATE_PAUSED
+                        || state == MediaItemStatus.PLAYBACK_STATE_PENDING) {
+                    item.setState(state);
+                    item.setPosition(itemStatus.getContentPosition());
+                    item.setDuration(itemStatus.getContentDuration());
+                    item.setTimestamp(itemStatus.getTimestamp());
+                }
+                if (update && mCallback != null) {
+                    mCallback.onPlaylistReady();
+                }
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("getStatus: failed", error, code);
+                if (update && mCallback != null) {
+                    mCallback.onPlaylistReady();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void pause() {
+        if (!mClient.hasSession()) {
+            // ignore if no session
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "pause");
+        }
+        mClient.pause(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+                logStatus("pause: succeeded", sessionId, sessionStatus, null, null);
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("pause: failed", error, code);
+            }
+        });
+    }
+
+    @Override
+    public void resume() {
+        if (!mClient.hasSession()) {
+            // ignore if no session
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "resume");
+        }
+        mClient.resume(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+                logStatus("resume: succeeded", sessionId, sessionStatus, null, null);
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("resume: failed", error, code);
+            }
+        });
+    }
+
+    @Override
+    public void stop() {
+        if (!mClient.hasSession()) {
+            // ignore if no session
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "stop");
+        }
+        mClient.stop(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+                logStatus("stop: succeeded", sessionId, sessionStatus, null, null);
+                if (mClient.isSessionManagementSupported()) {
+                    endSession();
+                }
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("stop: failed", error, code);
+            }
+        });
+    }
+
+    // enqueue & remove are only supported if isQueuingSupported() returns true
+    @Override
+    public void enqueue(final PlaylistItem item) {
+        throwIfQueuingUnsupported();
+
+        if (!mClient.hasSession() && !mEnqueuePending) {
+            mEnqueuePending = true;
+            if (mClient.isSessionManagementSupported()) {
+                startSession(item);
+            } else {
+                enqueueInternal(item);
+            }
+        } else if (mEnqueuePending){
+            mTempQueue.add(item);
+        } else {
+            enqueueInternal(item);
+        }
+    }
+
+    @Override
+    public PlaylistItem remove(String itemId) {
+        throwIfNoSession();
+        throwIfQueuingUnsupported();
+
+        if (DEBUG) {
+            Log.d(TAG, "remove: itemId=" + itemId);
+        }
+        mClient.remove(itemId, null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                logStatus("remove: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("remove: failed", error, code);
+            }
+        });
+
+        return null;
+    }
+
+    @Override
+    public void updateStatistics() {
+        // clear stats info first
+        mStatsInfo = "";
+
+        Intent intent = new Intent(SampleMediaRouteProvider.ACTION_GET_STATISTICS);
+        intent.addCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE);
+
+        if (mRoute != null && mRoute.supportsControlRequest(intent)) {
+            ControlRequestCallback callback = new ControlRequestCallback() {
+                @Override
+                public void onResult(Bundle data) {
+                    if (DEBUG) {
+                        Log.d(TAG, "getStatistics: succeeded: data=" + data);
+                    }
+                    if (data != null) {
+                        int playbackCount = data.getInt(
+                                SampleMediaRouteProvider.DATA_PLAYBACK_COUNT, -1);
+                        mStatsInfo = "Total playback count: " + playbackCount;
+                    }
+                }
+
+                @Override
+                public void onError(String error, Bundle data) {
+                    Log.d(TAG, "getStatistics: failed: error=" + error + ", data=" + data);
+                }
+            };
+
+            mRoute.sendControlRequest(intent, callback);
+        }
+    }
+
+    @Override
+    public String getStatistics() {
+        return mStatsInfo;
+    }
+
+    private void enqueueInternal(final PlaylistItem item) {
+        throwIfQueuingUnsupported();
+
+        if (DEBUG) {
+            Log.d(TAG, "enqueue: item=" + item);
+        }
+        mClient.enqueue(item.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                logStatus("enqueue: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+                item.setRemoteItemId(itemId);
+                if (item.getPosition() > 0) {
+                    seekInternal(item);
+                }
+                if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                    pause();
+                }
+                if (mEnqueuePending) {
+                    mEnqueuePending = false;
+                    for (PlaylistItem item : mTempQueue) {
+                        enqueueInternal(item);
+                    }
+                    mTempQueue.clear();
+                }
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("enqueue: failed", error, code);
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
+        });
+    }
+
+    private void seekInternal(final PlaylistItem item) {
+        throwIfNoSession();
+
+        if (DEBUG) {
+            Log.d(TAG, "seek: item=" + item);
+        }
+        mClient.seek(item.getRemoteItemId(), item.getPosition(), null, new ItemActionCallback() {
+           @Override
+           public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
+                   String itemId, MediaItemStatus itemStatus) {
+               logStatus("seek: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+               if (mCallback != null) {
+                   mCallback.onPlaylistChanged();
+               }
+           }
+
+           @Override
+           public void onError(String error, int code, Bundle data) {
+               logError("seek: failed", error, code);
+           }
+        });
+    }
+
+    private void startSession(final PlaylistItem item) {
+        mClient.startSession(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+                logStatus("startSession: succeeded", sessionId, sessionStatus, null, null);
+                enqueueInternal(item);
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("startSession: failed", error, code);
+            }
+        });
+    }
+
+    private void endSession() {
+        mClient.endSession(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+                logStatus("endSession: succeeded", sessionId, sessionStatus, null, null);
+            }
+
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("endSession: failed", error, code);
+            }
+        });
+    }
+
+    private void logStatus(String message,
+            String sessionId, MediaSessionStatus sessionStatus,
+            String itemId, MediaItemStatus itemStatus) {
+        if (DEBUG) {
+            String result = "";
+            if (sessionId != null && sessionStatus != null) {
+                result += "sessionId=" + sessionId + ", sessionStatus=" + sessionStatus;
+            }
+            if (itemId != null & itemStatus != null) {
+                result += (result.isEmpty() ? "" : ", ")
+                        + "itemId=" + itemId + ", itemStatus=" + itemStatus;
+            }
+            Log.d(TAG, message + ": " + result);
+        }
+    }
+
+    private void logError(String message, String error, int code) {
+        Log.d(TAG, message + ": error=" + error + ", code=" + code);
+    }
+
+    private void throwIfNoSession() {
+        if (!mClient.hasSession()) {
+            throw new IllegalStateException("Session is invalid");
+        }
+    }
+
+    private void throwIfQueuingUnsupported() {
+        if (!isQueuingSupported()) {
+            throw new UnsupportedOperationException("Queuing is unsupported");
+        }
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/SampleMediaButtonReceiver.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/SampleMediaButtonReceiver.java
new file mode 100644
index 0000000..855bc1e
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/SampleMediaButtonReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * 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.mediarouter.player;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.view.KeyEvent;
+
+/**
+ * Broadcast receiver for handling ACTION_MEDIA_BUTTON.
+ *
+ * This is needed to create the RemoteControlClient for controlling
+ * remote route volume in lock screen. It routes media key events back
+ * to main app activity MainActivity.
+ */
+public class SampleMediaButtonReceiver extends BroadcastReceiver {
+    private static final String TAG = "SampleMediaButtonReceiver";
+    private static MainActivity mActivity;
+
+    public static void setActivity(MainActivity activity) {
+        mActivity = activity;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (mActivity != null && Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
+            mActivity.handleMediaKey(
+                    (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT));
+        }
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/SessionManager.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/SessionManager.java
new file mode 100644
index 0000000..b6c5a46
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/player/SessionManager.java
@@ -0,0 +1,419 @@
+/*
+ * 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.mediarouter.player;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.support.v7.media.MediaItemStatus;
+import android.support.v7.media.MediaSessionStatus;
+import android.util.Log;
+
+import com.example.android.mediarouter.player.Player.Callback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SessionManager manages a media session as a queue. It supports common
+ * queuing behaviors such as enqueue/remove of media items, pause/resume/stop,
+ * etc.
+ *
+ * Actual playback of a single media item is abstracted into a Player interface,
+ * and is handled outside this class.
+ */
+public class SessionManager implements Callback {
+    private static final String TAG = "SessionManager";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private String mName;
+    private int mSessionId;
+    private int mItemId;
+    private boolean mPaused;
+    private boolean mSessionValid;
+    private Player mPlayer;
+    private Callback mCallback;
+    private List<PlaylistItem> mPlaylist = new ArrayList<PlaylistItem>();
+
+    public SessionManager(String name) {
+        mName = name;
+    }
+
+    public boolean hasSession() {
+        return mSessionValid;
+    }
+
+    public String getSessionId() {
+        return mSessionValid ? Integer.toString(mSessionId) : null;
+    }
+
+    public PlaylistItem getCurrentItem() {
+        return mPlaylist.isEmpty() ? null : mPlaylist.get(0);
+    }
+
+    // Get the cached statistic info from the player (will not update it)
+    public String getStatistics() {
+        checkPlayer();
+        return mPlayer.getStatistics();
+    }
+
+    // Returns the cached playlist (note this is not responsible for updating it)
+    public List<PlaylistItem> getPlaylist() {
+        return mPlaylist;
+    }
+
+    // Updates the playlist asynchronously, calls onPlaylistReady() when finished.
+    public void updateStatus() {
+        if (DEBUG) {
+            log("updateStatus");
+        }
+        checkPlayer();
+        // update the statistics first, so that the stats string is valid when
+        // onPlaylistReady() gets called in the end
+        mPlayer.updateStatistics();
+
+        if (mPlaylist.isEmpty()) {
+            // If queue is empty, don't forget to call onPlaylistReady()!
+            onPlaylistReady();
+        } else if (mPlayer.isQueuingSupported()) {
+            // If player supports queuing, get status of each item. Player is
+            // responsible to call onPlaylistReady() after last getStatus().
+            // (update=1 requires player to callback onPlaylistReady())
+            for (int i = 0; i < mPlaylist.size(); i++) {
+                PlaylistItem item = mPlaylist.get(i);
+                mPlayer.getStatus(item, (i == mPlaylist.size() - 1) /* update */);
+            }
+        } else {
+            // Otherwise, only need to get status for current item. Player is
+            // responsible to call onPlaylistReady() when finished.
+            mPlayer.getStatus(getCurrentItem(), true /* update */);
+        }
+    }
+
+    public PlaylistItem add(Uri uri, String mime) {
+        return add(uri, mime, null);
+    }
+
+    public PlaylistItem add(Uri uri, String mime, PendingIntent receiver) {
+        if (DEBUG) {
+            log("add: uri=" + uri + ", receiver=" + receiver);
+        }
+        // create new session if needed
+        startSession();
+        checkPlayerAndSession();
+
+        // append new item with initial status PLAYBACK_STATE_PENDING
+        PlaylistItem item = new PlaylistItem(
+                Integer.toString(mSessionId), Integer.toString(mItemId), uri, mime, receiver);
+        mPlaylist.add(item);
+        mItemId++;
+
+        // if player supports queuing, enqueue the item now
+        if (mPlayer.isQueuingSupported()) {
+            mPlayer.enqueue(item);
+        }
+        updatePlaybackState();
+        return item;
+    }
+
+    public PlaylistItem remove(String iid) {
+        if (DEBUG) {
+            log("remove: iid=" + iid);
+        }
+        checkPlayerAndSession();
+        return removeItem(iid, MediaItemStatus.PLAYBACK_STATE_CANCELED);
+    }
+
+    public PlaylistItem seek(String iid, long pos) {
+        if (DEBUG) {
+            log("seek: iid=" + iid +", pos=" + pos);
+        }
+        checkPlayerAndSession();
+        // seeking on pending items are not yet supported
+        checkItemCurrent(iid);
+
+        PlaylistItem item = getCurrentItem();
+        if (pos != item.getPosition()) {
+            item.setPosition(pos);
+            if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
+                    || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                mPlayer.seek(item);
+            }
+        }
+        return item;
+    }
+
+    public PlaylistItem getStatus(String iid) {
+        checkPlayerAndSession();
+
+        // This should only be called for local player. Remote player is
+        // asynchronous, need to use updateStatus() instead.
+        if (mPlayer.isRemotePlayback()) {
+            throw new IllegalStateException(
+                    "getStatus should not be called on remote player!");
+        }
+
+        for (PlaylistItem item : mPlaylist) {
+            if (item.getItemId().equals(iid)) {
+                if (item == getCurrentItem()) {
+                    mPlayer.getStatus(item, false);
+                }
+                return item;
+            }
+        }
+        return null;
+    }
+
+    public void pause() {
+        if (DEBUG) {
+            log("pause");
+        }
+        mPaused = true;
+        updatePlaybackState();
+    }
+
+    public void resume() {
+        if (DEBUG) {
+            log("resume");
+        }
+        mPaused = false;
+        updatePlaybackState();
+    }
+
+    public void stop() {
+        if (DEBUG) {
+            log("stop");
+        }
+        mPlayer.stop();
+        mPlaylist.clear();
+        mPaused = false;
+        updateStatus();
+    }
+
+    public String startSession() {
+        if (!mSessionValid) {
+            mSessionId++;
+            mItemId = 0;
+            mPaused = false;
+            mSessionValid = true;
+            return Integer.toString(mSessionId);
+        }
+        return null;
+    }
+
+    public boolean endSession() {
+        if (mSessionValid) {
+            mSessionValid = false;
+            return true;
+        }
+        return false;
+    }
+
+    public MediaSessionStatus getSessionStatus(String sid) {
+        int sessionState = (sid != null && sid.equals(mSessionId)) ?
+                MediaSessionStatus.SESSION_STATE_ACTIVE :
+                    MediaSessionStatus.SESSION_STATE_INVALIDATED;
+
+        return new MediaSessionStatus.Builder(sessionState)
+                .setQueuePaused(mPaused)
+                .build();
+    }
+
+    // Suspend the playback manager. Put the current item back into PENDING
+    // state, and remember the current playback position. Called when switching
+    // to a different player (route).
+    public void suspend(long pos) {
+        for (PlaylistItem item : mPlaylist) {
+            item.setRemoteItemId(null);
+            item.setDuration(0);
+        }
+        PlaylistItem item = getCurrentItem();
+        if (DEBUG) {
+            log("suspend: item=" + item + ", pos=" + pos);
+        }
+        if (item != null) {
+            if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
+                    || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                item.setState(MediaItemStatus.PLAYBACK_STATE_PENDING);
+                item.setPosition(pos);
+            }
+        }
+    }
+
+    // Unsuspend the playback manager. Restart playback on new player (route).
+    // This will resume playback of current item. Furthermore, if the new player
+    // supports queuing, playlist will be re-established on the remote player.
+    public void unsuspend() {
+        if (DEBUG) {
+            log("unsuspend");
+        }
+        if (mPlayer.isQueuingSupported()) {
+            for (PlaylistItem item : mPlaylist) {
+                mPlayer.enqueue(item);
+            }
+        }
+        updatePlaybackState();
+    }
+
+    // Player.Callback
+    @Override
+    public void onError() {
+        finishItem(true);
+    }
+
+    @Override
+    public void onCompletion() {
+        finishItem(false);
+    }
+
+    @Override
+    public void onPlaylistChanged() {
+        // Playlist has changed, update the cached playlist
+        updateStatus();
+    }
+
+    @Override
+    public void onPlaylistReady() {
+        // Notify activity to update Ui
+        if (mCallback != null) {
+            mCallback.onStatusChanged();
+        }
+    }
+
+    private void log(String message) {
+        Log.d(TAG, mName + ": " + message);
+    }
+
+    private void checkPlayer() {
+        if (mPlayer == null) {
+            throw new IllegalStateException("Player not set!");
+        }
+    }
+
+    private void checkSession() {
+        if (!mSessionValid) {
+            throw new IllegalStateException("Session not set!");
+        }
+    }
+
+    private void checkPlayerAndSession() {
+        checkPlayer();
+        checkSession();
+    }
+
+    private void checkItemCurrent(String iid) {
+        PlaylistItem item = getCurrentItem();
+        if (item == null || !item.getItemId().equals(iid)) {
+            throw new IllegalArgumentException("Item is not current!");
+        }
+    }
+
+    private void updatePlaybackState() {
+        PlaylistItem item = getCurrentItem();
+        if (item != null) {
+            if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PENDING) {
+                item.setState(mPaused ? MediaItemStatus.PLAYBACK_STATE_PAUSED
+                        : MediaItemStatus.PLAYBACK_STATE_PLAYING);
+                if (!mPlayer.isQueuingSupported()) {
+                    mPlayer.play(item);
+                }
+            } else if (mPaused && item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING) {
+                mPlayer.pause();
+                item.setState(MediaItemStatus.PLAYBACK_STATE_PAUSED);
+            } else if (!mPaused && item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                mPlayer.resume();
+                item.setState(MediaItemStatus.PLAYBACK_STATE_PLAYING);
+            }
+            // notify client that item playback status has changed
+            if (mCallback != null) {
+                mCallback.onItemChanged(item);
+            }
+        }
+        updateStatus();
+    }
+
+    private PlaylistItem removeItem(String iid, int state) {
+        checkPlayerAndSession();
+        List<PlaylistItem> queue =
+                new ArrayList<PlaylistItem>(mPlaylist.size());
+        PlaylistItem found = null;
+        for (PlaylistItem item : mPlaylist) {
+            if (iid.equals(item.getItemId())) {
+                if (mPlayer.isQueuingSupported()) {
+                    mPlayer.remove(item.getRemoteItemId());
+                } else if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
+                        || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED){
+                    mPlayer.stop();
+                }
+                item.setState(state);
+                found = item;
+                // notify client that item is now removed
+                if (mCallback != null) {
+                    mCallback.onItemChanged(found);
+                }
+            } else {
+                queue.add(item);
+            }
+        }
+        if (found != null) {
+            mPlaylist = queue;
+            updatePlaybackState();
+        } else {
+            log("item not found");
+        }
+        return found;
+    }
+
+    private void finishItem(boolean error) {
+        PlaylistItem item = getCurrentItem();
+        if (item != null) {
+            removeItem(item.getItemId(), error ?
+                    MediaItemStatus.PLAYBACK_STATE_ERROR :
+                        MediaItemStatus.PLAYBACK_STATE_FINISHED);
+            updateStatus();
+        }
+    }
+
+    // set the Player that this playback manager will interact with
+    public void setPlayer(Player player) {
+        mPlayer = player;
+        checkPlayer();
+        mPlayer.setCallback(this);
+    }
+
+    // provide a callback interface to tell the UI when significant state changes occur
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    @Override
+    public String toString() {
+        String result = "Media Queue: ";
+        if (!mPlaylist.isEmpty()) {
+            for (PlaylistItem item : mPlaylist) {
+                result += "\n" + item.toString();
+            }
+        } else {
+            result += "<empty>";
+        }
+        return result;
+    }
+
+    public interface Callback {
+        void onStatusChanged();
+        void onItemChanged(PlaylistItem item);
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/provider/SampleMediaRouteProvider.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/provider/SampleMediaRouteProvider.java
new file mode 100644
index 0000000..739e3ba
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/provider/SampleMediaRouteProvider.java
@@ -0,0 +1,602 @@
+/*
+ * 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.mediarouter.provider;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentFilter.MalformedMimeTypeException;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.MediaRouter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.media.MediaControlIntent;
+import android.support.v7.media.MediaRouteDescriptor;
+import android.support.v7.media.MediaRouteProvider;
+import android.support.v7.media.MediaRouteProviderDescriptor;
+import android.support.v7.media.MediaRouter.ControlRequestCallback;
+import android.support.v7.media.MediaSessionStatus;
+import android.util.Log;
+
+import com.example.android.mediarouter.player.Player;
+import com.example.android.mediarouter.player.PlaylistItem;
+import com.example.android.mediarouter.R;
+import com.example.android.mediarouter.player.SessionManager;
+
+import java.util.ArrayList;
+
+/**
+ * Demonstrates how to create a custom media route provider.
+ *
+ * @see SampleMediaRouteProviderService
+ */
+public final class SampleMediaRouteProvider extends MediaRouteProvider {
+    private static final String TAG = "SampleMediaRouteProvider";
+
+    private static final String FIXED_VOLUME_ROUTE_ID = "fixed";
+    private static final String VARIABLE_VOLUME_BASIC_ROUTE_ID = "variable_basic";
+    private static final String VARIABLE_VOLUME_QUEUING_ROUTE_ID = "variable_queuing";
+    private static final String VARIABLE_VOLUME_SESSION_ROUTE_ID = "variable_session";
+    private static final int VOLUME_MAX = 10;
+
+    /**
+     * A custom media control intent category for special requests that are
+     * supported by this provider's routes.
+     */
+    public static final String CATEGORY_SAMPLE_ROUTE =
+            "com.example.android.mediarouteprovider.CATEGORY_SAMPLE_ROUTE";
+
+    /**
+     * A custom media control intent action for special requests that are
+     * supported by this provider's routes.
+     * <p>
+     * This particular request is designed to return a bundle of not very
+     * interesting statistics for demonstration purposes.
+     * </p>
+     *
+     * @see #DATA_PLAYBACK_COUNT
+     */
+    public static final String ACTION_GET_STATISTICS =
+            "com.example.android.mediarouteprovider.ACTION_GET_STATISTICS";
+
+    /**
+     * {@link #ACTION_GET_STATISTICS} result data: Number of times the
+     * playback action was invoked.
+     */
+    public static final String DATA_PLAYBACK_COUNT =
+            "com.example.android.mediarouteprovider.EXTRA_PLAYBACK_COUNT";
+
+    private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
+    private static final ArrayList<IntentFilter> CONTROL_FILTERS_QUEUING;
+    private static final ArrayList<IntentFilter> CONTROL_FILTERS_SESSION;
+
+    static {
+        IntentFilter f1 = new IntentFilter();
+        f1.addCategory(CATEGORY_SAMPLE_ROUTE);
+        f1.addAction(ACTION_GET_STATISTICS);
+
+        IntentFilter f2 = new IntentFilter();
+        f2.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+        f2.addAction(MediaControlIntent.ACTION_PLAY);
+        f2.addDataScheme("http");
+        f2.addDataScheme("https");
+        f2.addDataScheme("rtsp");
+        f2.addDataScheme("file");
+        addDataTypeUnchecked(f2, "video/*");
+
+        IntentFilter f3 = new IntentFilter();
+        f3.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+        f3.addAction(MediaControlIntent.ACTION_SEEK);
+        f3.addAction(MediaControlIntent.ACTION_GET_STATUS);
+        f3.addAction(MediaControlIntent.ACTION_PAUSE);
+        f3.addAction(MediaControlIntent.ACTION_RESUME);
+        f3.addAction(MediaControlIntent.ACTION_STOP);
+
+        IntentFilter f4 = new IntentFilter();
+        f4.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+        f4.addAction(MediaControlIntent.ACTION_ENQUEUE);
+        f4.addDataScheme("http");
+        f4.addDataScheme("https");
+        f4.addDataScheme("rtsp");
+        f4.addDataScheme("file");
+        addDataTypeUnchecked(f4, "video/*");
+
+        IntentFilter f5 = new IntentFilter();
+        f5.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+        f5.addAction(MediaControlIntent.ACTION_REMOVE);
+
+        IntentFilter f6 = new IntentFilter();
+        f6.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+        f6.addAction(MediaControlIntent.ACTION_START_SESSION);
+        f6.addAction(MediaControlIntent.ACTION_GET_SESSION_STATUS);
+        f6.addAction(MediaControlIntent.ACTION_END_SESSION);
+
+        CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
+        CONTROL_FILTERS_BASIC.add(f1);
+        CONTROL_FILTERS_BASIC.add(f2);
+        CONTROL_FILTERS_BASIC.add(f3);
+
+        CONTROL_FILTERS_QUEUING =
+                new ArrayList<IntentFilter>(CONTROL_FILTERS_BASIC);
+        CONTROL_FILTERS_QUEUING.add(f4);
+        CONTROL_FILTERS_QUEUING.add(f5);
+
+        CONTROL_FILTERS_SESSION =
+                new ArrayList<IntentFilter>(CONTROL_FILTERS_QUEUING);
+        CONTROL_FILTERS_SESSION.add(f6);
+    }
+
+    private static void addDataTypeUnchecked(IntentFilter filter, String type) {
+        try {
+            filter.addDataType(type);
+        } catch (MalformedMimeTypeException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    private int mVolume = 5;
+    private int mEnqueueCount;
+
+    public SampleMediaRouteProvider(Context context) {
+        super(context);
+
+        publishRoutes();
+    }
+
+    @Override
+    public RouteController onCreateRouteController(String routeId) {
+        return new SampleRouteController(routeId);
+    }
+
+    private void publishRoutes() {
+        Resources r = getContext().getResources();
+
+        MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder(
+                FIXED_VOLUME_ROUTE_ID,
+                r.getString(R.string.fixed_volume_route_name))
+                .setDescription(r.getString(R.string.sample_route_description))
+                .addControlFilters(CONTROL_FILTERS_BASIC)
+                .setPlaybackStream(AudioManager.STREAM_MUSIC)
+                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
+                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED)
+                .setVolume(VOLUME_MAX)
+                .build();
+
+        MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder(
+                VARIABLE_VOLUME_BASIC_ROUTE_ID,
+                r.getString(R.string.variable_volume_basic_route_name))
+                .setDescription(r.getString(R.string.sample_route_description))
+                .addControlFilters(CONTROL_FILTERS_BASIC)
+                .setPlaybackStream(AudioManager.STREAM_MUSIC)
+                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
+                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
+                .setVolumeMax(VOLUME_MAX)
+                .setVolume(mVolume)
+                .build();
+
+        MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder(
+                VARIABLE_VOLUME_QUEUING_ROUTE_ID,
+                r.getString(R.string.variable_volume_queuing_route_name))
+                .setDescription(r.getString(R.string.sample_route_description))
+                .addControlFilters(CONTROL_FILTERS_QUEUING)
+                .setPlaybackStream(AudioManager.STREAM_MUSIC)
+                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
+                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
+                .setVolumeMax(VOLUME_MAX)
+                .setVolume(mVolume)
+                .build();
+
+        MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder(
+                VARIABLE_VOLUME_SESSION_ROUTE_ID,
+                r.getString(R.string.variable_volume_session_route_name))
+                .setDescription(r.getString(R.string.sample_route_description))
+                .addControlFilters(CONTROL_FILTERS_SESSION)
+                .setPlaybackStream(AudioManager.STREAM_MUSIC)
+                .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
+                .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
+                .setVolumeMax(VOLUME_MAX)
+                .setVolume(mVolume)
+                .build();
+
+        MediaRouteProviderDescriptor providerDescriptor =
+                new MediaRouteProviderDescriptor.Builder()
+                .addRoute(routeDescriptor1)
+                .addRoute(routeDescriptor2)
+                .addRoute(routeDescriptor3)
+                .addRoute(routeDescriptor4)
+                .build();
+        setDescriptor(providerDescriptor);
+    }
+
+    private final class SampleRouteController extends MediaRouteProvider.RouteController {
+        private final String mRouteId;
+        private final SessionManager mSessionManager = new SessionManager("mrp");
+        private final Player mPlayer;
+        private PendingIntent mSessionReceiver;
+
+        public SampleRouteController(String routeId) {
+            mRouteId = routeId;
+            mPlayer = Player.create(getContext(), null);
+            mSessionManager.setPlayer(mPlayer);
+            mSessionManager.setCallback(new SessionManager.Callback() {
+                @Override
+                public void onStatusChanged() {
+                }
+
+                @Override
+                public void onItemChanged(PlaylistItem item) {
+                    handleStatusChange(item);
+                }
+            });
+            Log.d(TAG, mRouteId + ": Controller created");
+        }
+
+        @Override
+        public void onRelease() {
+            Log.d(TAG, mRouteId + ": Controller released");
+            mPlayer.release();
+        }
+
+        @Override
+        public void onSelect() {
+            Log.d(TAG, mRouteId + ": Selected");
+            mPlayer.connect(null);
+        }
+
+        @Override
+        public void onUnselect() {
+            Log.d(TAG, mRouteId + ": Unselected");
+            mPlayer.release();
+        }
+
+        @Override
+        public void onSetVolume(int volume) {
+            Log.d(TAG, mRouteId + ": Set volume to " + volume);
+            if (!mRouteId.equals(FIXED_VOLUME_ROUTE_ID)) {
+                setVolumeInternal(volume);
+            }
+        }
+
+        @Override
+        public void onUpdateVolume(int delta) {
+            Log.d(TAG, mRouteId + ": Update volume by " + delta);
+            if (!mRouteId.equals(FIXED_VOLUME_ROUTE_ID)) {
+                setVolumeInternal(mVolume + delta);
+            }
+        }
+
+        @Override
+        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
+            Log.d(TAG, mRouteId + ": Received control request " + intent);
+            String action = intent.getAction();
+            if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
+                boolean success = false;
+                if (action.equals(MediaControlIntent.ACTION_PLAY)) {
+                    success = handlePlay(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
+                    success = handleEnqueue(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_REMOVE)) {
+                    success = handleRemove(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_SEEK)) {
+                    success = handleSeek(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_GET_STATUS)) {
+                    success = handleGetStatus(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_PAUSE)) {
+                    success = handlePause(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_RESUME)) {
+                    success = handleResume(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_STOP)) {
+                    success = handleStop(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_START_SESSION)) {
+                    success = handleStartSession(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_GET_SESSION_STATUS)) {
+                    success = handleGetSessionStatus(intent, callback);
+                } else if (action.equals(MediaControlIntent.ACTION_END_SESSION)) {
+                    success = handleEndSession(intent, callback);
+                }
+                Log.d(TAG, mSessionManager.toString());
+                return success;
+            }
+
+            if (action.equals(ACTION_GET_STATISTICS)
+                    && intent.hasCategory(CATEGORY_SAMPLE_ROUTE)) {
+                Bundle data = new Bundle();
+                data.putInt(DATA_PLAYBACK_COUNT, mEnqueueCount);
+                if (callback != null) {
+                    callback.onResult(data);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        private void setVolumeInternal(int volume) {
+            if (volume >= 0 && volume <= VOLUME_MAX) {
+                mVolume = volume;
+                Log.d(TAG, mRouteId + ": New volume is " + mVolume);
+                AudioManager audioManager =
+                        (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
+                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+                publishRoutes();
+            }
+        }
+
+        private boolean handlePlay(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            if (sid != null && !sid.equals(mSessionManager.getSessionId())) {
+                Log.d(TAG, "handlePlay fails because of bad sid="+sid);
+                return false;
+            }
+            if (mSessionManager.hasSession()) {
+                mSessionManager.stop();
+            }
+            return handleEnqueue(intent, callback);
+        }
+
+        private boolean handleEnqueue(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            if (sid != null && !sid.equals(mSessionManager.getSessionId())) {
+                Log.d(TAG, "handleEnqueue fails because of bad sid="+sid);
+                return false;
+            }
+
+            Uri uri = intent.getData();
+            if (uri == null) {
+                Log.d(TAG, "handleEnqueue fails because of bad uri="+uri);
+                return false;
+            }
+
+            boolean enqueue = intent.getAction().equals(MediaControlIntent.ACTION_ENQUEUE);
+            String mime = intent.getType();
+            long pos = intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
+            Bundle metadata = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_METADATA);
+            Bundle headers = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_HTTP_HEADERS);
+            PendingIntent receiver = (PendingIntent)intent.getParcelableExtra(
+                    MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER);
+
+            Log.d(TAG, mRouteId + ": Received " + (enqueue?"enqueue":"play") + " request"
+                    + ", uri=" + uri
+                    + ", mime=" + mime
+                    + ", sid=" + sid
+                    + ", pos=" + pos
+                    + ", metadata=" + metadata
+                    + ", headers=" + headers
+                    + ", receiver=" + receiver);
+            PlaylistItem item = mSessionManager.add(uri, mime, receiver);
+            if (callback != null) {
+                if (item != null) {
+                    Bundle result = new Bundle();
+                    result.putString(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId());
+                    result.putString(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId());
+                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
+                            item.getStatus().asBundle());
+                    callback.onResult(result);
+                } else {
+                    callback.onError("Failed to open " + uri.toString(), null);
+                }
+            }
+            mEnqueueCount +=1;
+            return true;
+        }
+
+        private boolean handleRemove(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            if (sid == null || !sid.equals(mSessionManager.getSessionId())) {
+                return false;
+            }
+
+            String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
+            PlaylistItem item = mSessionManager.remove(iid);
+            if (callback != null) {
+                if (item != null) {
+                    Bundle result = new Bundle();
+                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
+                            item.getStatus().asBundle());
+                    callback.onResult(result);
+                } else {
+                    callback.onError("Failed to remove" +
+                            ", sid=" + sid + ", iid=" + iid, null);
+                }
+            }
+            return (item != null);
+        }
+
+        private boolean handleSeek(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            if (sid == null || !sid.equals(mSessionManager.getSessionId())) {
+                return false;
+            }
+
+            String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
+            long pos = intent.getLongExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
+            Log.d(TAG, mRouteId + ": Received seek request, pos=" + pos);
+            PlaylistItem item = mSessionManager.seek(iid, pos);
+            if (callback != null) {
+                if (item != null) {
+                    Bundle result = new Bundle();
+                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
+                            item.getStatus().asBundle());
+                    callback.onResult(result);
+                } else {
+                    callback.onError("Failed to seek" +
+                            ", sid=" + sid + ", iid=" + iid + ", pos=" + pos, null);
+                }
+            }
+            return (item != null);
+        }
+
+        private boolean handleGetStatus(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            String iid = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
+            Log.d(TAG, mRouteId + ": Received getStatus request, sid=" + sid + ", iid=" + iid);
+            PlaylistItem item = mSessionManager.getStatus(iid);
+            if (callback != null) {
+                if (item != null) {
+                    Bundle result = new Bundle();
+                    result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS,
+                            item.getStatus().asBundle());
+                    callback.onResult(result);
+                } else {
+                    callback.onError("Failed to get status" +
+                            ", sid=" + sid + ", iid=" + iid, null);
+                }
+            }
+            return (item != null);
+        }
+
+        private boolean handlePause(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
+            mSessionManager.pause();
+            if (callback != null) {
+                if (success) {
+                    callback.onResult(new Bundle());
+                    handleSessionStatusChange(sid);
+                } else {
+                    callback.onError("Failed to pause, sid=" + sid, null);
+                }
+            }
+            return success;
+        }
+
+        private boolean handleResume(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
+            mSessionManager.resume();
+            if (callback != null) {
+                if (success) {
+                    callback.onResult(new Bundle());
+                    handleSessionStatusChange(sid);
+                } else {
+                    callback.onError("Failed to resume, sid=" + sid, null);
+                }
+            }
+            return success;
+        }
+
+        private boolean handleStop(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId());
+            mSessionManager.stop();
+            if (callback != null) {
+                if (success) {
+                    callback.onResult(new Bundle());
+                    handleSessionStatusChange(sid);
+                } else {
+                    callback.onError("Failed to stop, sid=" + sid, null);
+                }
+            }
+            return success;
+        }
+
+        private boolean handleStartSession(Intent intent, ControlRequestCallback callback) {
+            String sid = mSessionManager.startSession();
+            Log.d(TAG, "StartSession returns sessionId "+sid);
+            if (callback != null) {
+                if (sid != null) {
+                    Bundle result = new Bundle();
+                    result.putString(MediaControlIntent.EXTRA_SESSION_ID, sid);
+                    result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS,
+                            mSessionManager.getSessionStatus(sid).asBundle());
+                    callback.onResult(result);
+                    mSessionReceiver = (PendingIntent)intent.getParcelableExtra(
+                            MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER);
+                    handleSessionStatusChange(sid);
+                } else {
+                    callback.onError("Failed to start session.", null);
+                }
+            }
+            return (sid != null);
+        }
+
+        private boolean handleGetSessionStatus(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+
+            MediaSessionStatus sessionStatus = mSessionManager.getSessionStatus(sid);
+            if (callback != null) {
+                if (sessionStatus != null) {
+                    Bundle result = new Bundle();
+                    result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS,
+                            mSessionManager.getSessionStatus(sid).asBundle());
+                    callback.onResult(result);
+                } else {
+                    callback.onError("Failed to get session status, sid=" + sid, null);
+                }
+            }
+            return (sessionStatus != null);
+        }
+
+        private boolean handleEndSession(Intent intent, ControlRequestCallback callback) {
+            String sid = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
+            boolean success = (sid != null) && sid.equals(mSessionManager.getSessionId())
+                    && mSessionManager.endSession();
+            if (callback != null) {
+                if (success) {
+                    Bundle result = new Bundle();
+                    MediaSessionStatus sessionStatus = new MediaSessionStatus.Builder(
+                            MediaSessionStatus.SESSION_STATE_ENDED).build();
+                    result.putBundle(MediaControlIntent.EXTRA_SESSION_STATUS, sessionStatus.asBundle());
+                    callback.onResult(result);
+                    handleSessionStatusChange(sid);
+                    mSessionReceiver = null;
+                } else {
+                    callback.onError("Failed to end session, sid=" + sid, null);
+                }
+            }
+            return success;
+        }
+
+        private void handleStatusChange(PlaylistItem item) {
+            if (item == null) {
+                item = mSessionManager.getCurrentItem();
+            }
+            if (item != null) {
+                PendingIntent receiver = item.getUpdateReceiver();
+                if (receiver != null) {
+                    Intent intent = new Intent();
+                    intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, item.getSessionId());
+                    intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, item.getItemId());
+                    intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS,
+                            item.getStatus().asBundle());
+                    try {
+                        receiver.send(getContext(), 0, intent);
+                        Log.d(TAG, mRouteId + ": Sending status update from provider");
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.d(TAG, mRouteId + ": Failed to send status update!");
+                    }
+                }
+            }
+        }
+
+        private void handleSessionStatusChange(String sid) {
+            if (mSessionReceiver != null) {
+                Intent intent = new Intent();
+                intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sid);
+                intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS,
+                        mSessionManager.getSessionStatus(sid).asBundle());
+                try {
+                    mSessionReceiver.send(getContext(), 0, intent);
+                    Log.d(TAG, mRouteId + ": Sending session status update from provider");
+                } catch (PendingIntent.CanceledException e) {
+                    Log.d(TAG, mRouteId + ": Failed to send session status update!");
+                }
+            }
+        }
+    }
+}
diff --git a/samples/browseable/MediaRouter/src/com.example.android.mediarouter/provider/SampleMediaRouteProviderService.java b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/provider/SampleMediaRouteProviderService.java
new file mode 100644
index 0000000..41a6cbd
--- /dev/null
+++ b/samples/browseable/MediaRouter/src/com.example.android.mediarouter/provider/SampleMediaRouteProviderService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.mediarouter.provider;
+
+import android.support.v7.media.MediaRouteProvider;
+import android.support.v7.media.MediaRouteProviderService;
+
+import com.example.android.mediarouter.provider.SampleMediaRouteProvider;
+
+/**
+ * Demonstrates how to register a custom media route provider service
+ * using the support library.
+ *
+ * @see com.example.android.mediarouter.provider.SampleMediaRouteProvider
+ */
+public class SampleMediaRouteProviderService extends MediaRouteProviderService {
+    @Override
+    public MediaRouteProvider onCreateMediaRouteProvider() {
+        return new SampleMediaRouteProvider(this);
+    }
+}
diff --git a/samples/browseable/NetworkConnect/res/values-sw600dp/dimens.xml b/samples/browseable/NetworkConnect/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/NetworkConnect/res/values-sw600dp/dimens.xml
rename to samples/browseable/NetworkConnect/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/NetworkConnect/res/values-sw600dp/styles.xml b/samples/browseable/NetworkConnect/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/NetworkConnect/res/values-sw600dp/styles.xml
rename to samples/browseable/NetworkConnect/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/NetworkConnect/res/values-v11/template-styles.xml b/samples/browseable/NetworkConnect/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/NetworkConnect/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/NetworkConnect/res/values/styles.xml b/samples/browseable/NetworkConnect/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/NetworkConnect/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/NetworkConnect/res/values/dimens.xml b/samples/browseable/NetworkConnect/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/NetworkConnect/res/values/dimens.xml
rename to samples/browseable/NetworkConnect/res/values/template-dimens.xml
diff --git a/samples/browseable/NetworkConnect/res/values/template-styles.xml b/samples/browseable/NetworkConnect/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/NetworkConnect/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/AndroidManifest.xml b/samples/browseable/RenderScriptIntrinsic/AndroidManifest.xml
new file mode 100644
index 0000000..566ef8a
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?><!--
+ Copyright 2014 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.
+-->
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.renderscriptintrinsic"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk
+        android:minSdkVersion="8"
+        android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:label="RenderScriptIntrinsic"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity
+            android:name=".MainActivity"
+            android:label="RenderScriptIntrinsic"
+            android:theme="@style/FullscreenTheme">
+            <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/browseable/RenderScriptIntrinsic/_index.jd b/samples/browseable/RenderScriptIntrinsic/_index.jd
new file mode 100644
index 0000000..2724c1a
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/_index.jd
@@ -0,0 +1,15 @@
+
+
+
+page.tags="RenderScriptIntrinsic"
+sample.group=RenderScript
+@jd:body
+
+<p>
+  This sample demonstrates how to use <a href=
+  "http://android-developers.blogspot.com/2013/08/renderscript-intrinsics.html">
+  RenderScript intrinsics</a>. The app creates several RenderScript intrinsics
+  and shows a filtering result with various parameters. The sample also shows
+  how to extend {@link android.widget.RadioButton} with {@link
+  android.graphics.drawable.StateListDrawable}.
+</p>
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png b/samples/browseable/RenderScriptIntrinsic/res/drawable-hdpi/ic_launcher.png
old mode 100644
new mode 100755
similarity index 100%
copy from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png
copy to samples/browseable/RenderScriptIntrinsic/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/RenderScriptIntrinsic/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/RenderScriptIntrinsic/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/RenderScriptIntrinsic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/RenderScriptIntrinsic/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..4ccd98e
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/RenderScriptIntrinsic/res/drawable-nodpi/data.jpg b/samples/browseable/RenderScriptIntrinsic/res/drawable-nodpi/data.jpg
new file mode 100644
index 0000000..48e48e6
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/drawable-nodpi/data.jpg
Binary files differ
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/RenderScriptIntrinsic/res/drawable-xhdpi/ic_launcher.png
old mode 100644
new mode 100755
similarity index 100%
copy from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png
copy to samples/browseable/RenderScriptIntrinsic/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/RenderScriptIntrinsic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/RenderScriptIntrinsic/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..3c45f51
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/RenderScriptIntrinsic/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/Basic/res/layout/activity_main.xml
copy to samples/browseable/RenderScriptIntrinsic/res/layout/activity_main.xml
diff --git a/samples/browseable/RenderScriptIntrinsic/res/layout/main_layout.xml b/samples/browseable/RenderScriptIntrinsic/res/layout/main_layout.xml
new file mode 100644
index 0000000..13516d8
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/layout/main_layout.xml
@@ -0,0 +1,51 @@
+<RelativeLayout 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:background="#0099cc"
+    tools:context=".MainActivity">
+
+    <ImageView
+        android:id="@+id/imageView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="centerCrop"
+        android:src="@drawable/data" />
+
+    <RadioGroup
+        android:id="@+id/radioGroup1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:orientation="horizontal"
+        android:layout_above="@+id/seekBar1"
+        android:layout_marginBottom="8dp">
+
+        <com.example.android.renderscriptintrinsic.ThumbnailRadioButton
+            android:id="@+id/radio0"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:checked="true"
+            android:text="Blur" />
+
+        <com.example.android.renderscriptintrinsic.ThumbnailRadioButton
+            android:id="@+id/radio1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Emboss" />
+
+        <com.example.android.renderscriptintrinsic.ThumbnailRadioButton
+            android:id="@+id/radio2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Hue" />
+    </RadioGroup>
+
+    <SeekBar
+        android:id="@+id/seekBar1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_marginBottom="16dp" />
+
+</RelativeLayout>
diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/RenderScriptIntrinsic/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/dimens.xml
copy to samples/browseable/RenderScriptIntrinsic/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/RenderScriptIntrinsic/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/styles.xml
copy to samples/browseable/RenderScriptIntrinsic/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values-v11/styles.xml b/samples/browseable/RenderScriptIntrinsic/res/values-v11/styles.xml
new file mode 100644
index 0000000..f3a90c6
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values-v11/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+    <style name="FullscreenTheme" parent="android:Theme.Holo">
+        <item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="android:windowBackground">@null</item>
+        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
+        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
+    </style>
+
+    <style name="FullscreenActionBarStyle" parent="android:Widget.Holo.ActionBar">
+        <item name="android:background">@color/black_overlay</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values-v11/template-styles.xml b/samples/browseable/RenderScriptIntrinsic/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values-v14/styles.xml b/samples/browseable/RenderScriptIntrinsic/res/values-v14/styles.xml
new file mode 100644
index 0000000..a91fd03
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values/attrs.xml b/samples/browseable/RenderScriptIntrinsic/res/values/attrs.xml
new file mode 100644
index 0000000..e67df0a
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values/attrs.xml
@@ -0,0 +1,14 @@
+<resources>
+
+    <!--
+         Declare custom theme attributes that allow changing which styles are
+         used for button bars depending on the API level.
+         ?android:attr/buttonBarStyle is new as of API 11 so this is
+         necessary to support previous API levels.
+    -->
+    <declare-styleable name="ButtonBarContainerTheme">
+        <attr name="buttonBarStyle" format="reference" />
+        <attr name="buttonBarButtonStyle" format="reference" />
+    </declare-styleable>
+
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values/base-strings.xml b/samples/browseable/RenderScriptIntrinsic/res/values/base-strings.xml
new file mode 100644
index 0000000..c8488be
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values/base-strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">RenderScriptIntrinsic</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            RenderScriptIntrinsic sample that demonstrates how to use RenderScript intrinsics.
+            Creates several RenderScript intrinsics and shows a filtering result with various parameters.
+            Also shows how to extends RedioButton with StateListDrawable.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values/colors.xml b/samples/browseable/RenderScriptIntrinsic/res/values/colors.xml
new file mode 100644
index 0000000..327c060
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values/colors.xml
@@ -0,0 +1,5 @@
+<resources>
+
+    <color name="black_overlay">#66000000</color>
+
+</resources>
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values/styles.xml b/samples/browseable/RenderScriptIntrinsic/res/values/styles.xml
new file mode 100644
index 0000000..12eb930
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <style name="FullscreenTheme" parent="android:Theme.NoTitleBar">
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowBackground">@null</item>
+        <item name="buttonBarStyle">@style/ButtonBar</item>
+        <item name="buttonBarButtonStyle">@style/ButtonBarButton</item>
+    </style>
+
+    <style name="ButtonBar">
+        <item name="android:paddingLeft">2dp</item>
+        <item name="android:paddingTop">5dp</item>
+        <item name="android:paddingRight">2dp</item>
+        <item name="android:paddingBottom">0dp</item>
+        <item name="android:background">@android:drawable/bottom_bar</item>
+    </style>
+
+    <style name="ButtonBarButton" />
+
+</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/RenderScriptIntrinsic/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/RenderScriptIntrinsic/res/values/template-dimens.xml
diff --git a/samples/browseable/RenderScriptIntrinsic/res/values/template-styles.xml b/samples/browseable/RenderScriptIntrinsic/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/RenderScriptIntrinsic/src/com.example.android.common.logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/RenderScriptIntrinsic/src/com.example.android.renderscriptintrinsic/MainActivity.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.renderscriptintrinsic/MainActivity.java
new file mode 100644
index 0000000..4b6f5ce
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.renderscriptintrinsic/MainActivity.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2014 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.renderscriptintrinsic;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.support.v8.renderscript.*;
+
+public class MainActivity extends Activity {
+    /* Number of bitmaps that is used for renderScript thread and UI thread synchronization.
+       Ideally, this can be reduced to 2, however in some devices, 2 buffers still showing tierings on UI.
+       Investigating a root cause.
+     */
+    private final int NUM_BITMAPS = 3;
+    private int mCurrentBitmap = 0;
+    private Bitmap mBitmapIn;
+    private Bitmap[] mBitmapsOut;
+    private ImageView mImageView;
+
+    private RenderScript mRS;
+    private Allocation mInAllocation;
+    private Allocation[] mOutAllocations;
+
+    private ScriptIntrinsicBlur mScriptBlur;
+    private ScriptIntrinsicConvolve5x5 mScriptConvolve;
+    private ScriptIntrinsicColorMatrix mScriptMatrix;
+
+    private final int MODE_BLUR = 0;
+    private final int MODE_CONVOLVE = 1;
+    private final int MODE_COLORMATRIX = 2;
+
+    private int mFilterMode = MODE_BLUR;
+
+    private RenderScriptTask mLatestTask = null;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main_layout);
+
+        /*
+         * Initialize UI
+         */
+
+        //Set up main image view
+        mBitmapIn = loadBitmap(R.drawable.data);
+        mBitmapsOut = new Bitmap[NUM_BITMAPS];
+        for (int i = 0; i < NUM_BITMAPS; ++i) {
+            mBitmapsOut[i] = Bitmap.createBitmap(mBitmapIn.getWidth(),
+                    mBitmapIn.getHeight(), mBitmapIn.getConfig());
+        }
+
+        mImageView = (ImageView) findViewById(R.id.imageView);
+        mImageView.setImageBitmap(mBitmapsOut[mCurrentBitmap]);
+        mCurrentBitmap += (mCurrentBitmap + 1) % NUM_BITMAPS;
+
+        //Set up seekbar
+        final SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar1);
+        seekbar.setProgress(50);
+        seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+            public void onProgressChanged(SeekBar seekBar, int progress,
+                                          boolean fromUser) {
+                updateImage(progress);
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+            }
+        });
+
+        //Setup effect selector
+        RadioButton radio0 = (RadioButton) findViewById(R.id.radio0);
+        radio0.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    mFilterMode = MODE_BLUR;
+                    updateImage(seekbar.getProgress());
+                }
+            }
+        });
+        RadioButton radio1 = (RadioButton) findViewById(R.id.radio1);
+        radio1.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    mFilterMode = MODE_CONVOLVE;
+                    updateImage(seekbar.getProgress());
+                }
+            }
+        });
+        RadioButton radio2 = (RadioButton) findViewById(R.id.radio2);
+        radio2.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    mFilterMode = MODE_COLORMATRIX;
+                    updateImage(seekbar.getProgress());
+                }
+            }
+        });
+
+        /*
+         * Create renderScript
+         */
+        createScript();
+
+        /*
+         * Create thumbnails
+         */
+        createThumbnail();
+
+
+        /*
+         * Invoke renderScript kernel and update imageView
+         */
+        mFilterMode = MODE_BLUR;
+        updateImage(50);
+    }
+
+    private void createScript() {
+        mRS = RenderScript.create(this);
+
+        mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn);
+
+        mOutAllocations = new Allocation[NUM_BITMAPS];
+        for (int i = 0; i < NUM_BITMAPS; ++i) {
+            mOutAllocations[i] = Allocation.createFromBitmap(mRS, mBitmapsOut[i]);
+        }
+
+        /*
+        Create intrinsics.
+        RenderScript has built-in features such as blur, convolve filter etc.
+        These intrinsics are handy for specific operations without writing RenderScript kernel.
+        In the sample, it's creating blur, convolve and matrix intrinsics.
+         */
+
+        mScriptBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+        mScriptConvolve = ScriptIntrinsicConvolve5x5.create(mRS,
+                Element.U8_4(mRS));
+        mScriptMatrix = ScriptIntrinsicColorMatrix.create(mRS,
+                Element.U8_4(mRS));
+    }
+
+    private void performFilter(Allocation inAllocation,
+                               Allocation outAllocation, Bitmap bitmapOut, float value) {
+        switch (mFilterMode) {
+            case MODE_BLUR:
+            /*
+             * Set blur kernel size
+             */
+                mScriptBlur.setRadius(value);
+
+            /*
+             * Invoke filter kernel
+             */
+                mScriptBlur.setInput(inAllocation);
+                mScriptBlur.forEach(outAllocation);
+                break;
+            case MODE_CONVOLVE: {
+                float f1 = value;
+                float f2 = 1.0f - f1;
+
+                // Emboss filter kernel
+                float coefficients[] = {-f1 * 2, 0, -f1, 0, 0, 0, -f2 * 2, -f2, 0,
+                        0, -f1, -f2, 1, f2, f1, 0, 0, f2, f2 * 2, 0, 0, 0, f1, 0,
+                        f1 * 2,};
+            /*
+             * Set kernel parameter
+             */
+                mScriptConvolve.setCoefficients(coefficients);
+
+            /*
+             * Invoke filter kernel
+             */
+                mScriptConvolve.setInput(inAllocation);
+                mScriptConvolve.forEach(outAllocation);
+                break;
+            }
+            case MODE_COLORMATRIX: {
+            /*
+             * Set HUE rotation matrix
+             * The matrix below performs a combined operation of,
+             * RGB->HSV transform * HUE rotation * HSV->RGB transform
+             */
+                float cos = (float) Math.cos((double) value);
+                float sin = (float) Math.sin((double) value);
+                Matrix3f mat = new Matrix3f();
+                mat.set(0, 0, (float) (.299 + .701 * cos + .168 * sin));
+                mat.set(1, 0, (float) (.587 - .587 * cos + .330 * sin));
+                mat.set(2, 0, (float) (.114 - .114 * cos - .497 * sin));
+                mat.set(0, 1, (float) (.299 - .299 * cos - .328 * sin));
+                mat.set(1, 1, (float) (.587 + .413 * cos + .035 * sin));
+                mat.set(2, 1, (float) (.114 - .114 * cos + .292 * sin));
+                mat.set(0, 2, (float) (.299 - .3 * cos + 1.25 * sin));
+                mat.set(1, 2, (float) (.587 - .588 * cos - 1.05 * sin));
+                mat.set(2, 2, (float) (.114 + .886 * cos - .203 * sin));
+                mScriptMatrix.setColorMatrix(mat);
+
+            /*
+             * Invoke filter kernel
+             */
+                mScriptMatrix.forEach(inAllocation, outAllocation);
+            }
+            break;
+        }
+
+        /*
+         * Copy to bitmap and invalidate image view
+         */
+        outAllocation.copyTo(bitmapOut);
+    }
+
+    /*
+    Convert seekBar progress parameter (0-100 in range) to parameter for each intrinsic filter.
+    (e.g. 1.0-25.0 in Blur filter)
+     */
+    private float getFilterParameter(int i) {
+        float f = 0.f;
+        switch (mFilterMode) {
+            case MODE_BLUR: {
+                final float max = 25.0f;
+                final float min = 1.f;
+                f = (float) ((max - min) * (i / 100.0) + min);
+            }
+            break;
+            case MODE_CONVOLVE: {
+                final float max = 2.f;
+                final float min = 0.f;
+                f = (float) ((max - min) * (i / 100.0) + min);
+            }
+            break;
+            case MODE_COLORMATRIX: {
+                final float max = (float) Math.PI;
+                final float min = (float) -Math.PI;
+                f = (float) ((max - min) * (i / 100.0) + min);
+            }
+            break;
+        }
+        return f;
+
+    }
+
+    /*
+     * In the AsyncTask, it invokes RenderScript intrinsics to do a filtering.
+     * After the filtering is done, an operation blocks at Allication.copyTo() in AsyncTask thread.
+     * Once all operation is finished at onPostExecute() in UI thread, it can invalidate and update ImageView UI.
+     */
+    private class RenderScriptTask extends AsyncTask<Float, Integer, Integer> {
+        Boolean issued = false;
+
+        protected Integer doInBackground(Float... values) {
+            int index = -1;
+            if (isCancelled() == false) {
+                issued = true;
+                index = mCurrentBitmap;
+
+                performFilter(mInAllocation, mOutAllocations[index], mBitmapsOut[index], values[0]);
+                mCurrentBitmap = (mCurrentBitmap + 1) % NUM_BITMAPS;
+            }
+            return index;
+        }
+
+        void updateView(Integer result) {
+            if (result != -1) {
+                // Request UI update
+                mImageView.setImageBitmap(mBitmapsOut[result]);
+                mImageView.invalidate();
+            }
+        }
+
+        protected void onPostExecute(Integer result) {
+            updateView(result);
+        }
+
+        protected void onCancelled(Integer result) {
+            if (issued) {
+                updateView(result);
+            }
+        }
+    }
+
+    /*
+    Invoke AsynchTask and cancel previous task.
+    When AsyncTasks are piled up (typically in slow device with heavy kernel),
+    Only the latest (and already started) task invokes RenderScript operation.
+     */
+    private void updateImage(int progress) {
+        float f = getFilterParameter(progress);
+
+        if (mLatestTask != null)
+            mLatestTask.cancel(false);
+        mLatestTask = new RenderScriptTask();
+
+        mLatestTask.execute(f);
+    }
+
+    /*
+    Helper to load Bitmap from resource
+     */
+    private Bitmap loadBitmap(int resource) {
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        return BitmapFactory.decodeResource(getResources(), resource, options);
+    }
+
+    /*
+    Create thumbNail for UI. It invokes RenderScript kernel synchronously in UI-thread,
+    which is OK for small thumbnail (but not ideal).
+     */
+    private void createThumbnail() {
+        int width = 72;
+        int height = 96;
+        float scale = getResources().getDisplayMetrics().density;
+        int pixelsWidth = (int) (width * scale + 0.5f);
+        int pixelsHeight = (int) (height * scale + 0.5f);
+
+        //Temporary image
+        Bitmap tempBitmap = Bitmap.createScaledBitmap(mBitmapIn, pixelsWidth, pixelsHeight, false);
+        Allocation inAllocation = Allocation.createFromBitmap(mRS, tempBitmap);
+
+        //Create thumbnail with each RS intrinsic and set it to radio buttons
+        int[] modes = {MODE_BLUR, MODE_CONVOLVE, MODE_COLORMATRIX};
+        int[] ids = {R.id.radio0, R.id.radio1, R.id.radio2};
+        int[] parameter = {50, 100, 25};
+        for (int mode : modes) {
+            mFilterMode = mode;
+            float f = getFilterParameter(parameter[mode]);
+
+            Bitmap destBitpmap = Bitmap.createBitmap(tempBitmap.getWidth(),
+                    tempBitmap.getHeight(), tempBitmap.getConfig());
+            Allocation outAllocation = Allocation.createFromBitmap(mRS, destBitpmap);
+            performFilter(inAllocation, outAllocation, destBitpmap, f);
+
+            ThumbnailRadioButton button = (ThumbnailRadioButton) findViewById(ids[mode]);
+            button.setThumbnail(destBitpmap);
+        }
+    }
+}
diff --git a/samples/browseable/RenderScriptIntrinsic/src/com.example.android.renderscriptintrinsic/ThumbnailRadioButton.java b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.renderscriptintrinsic/ThumbnailRadioButton.java
new file mode 100644
index 0000000..160e970
--- /dev/null
+++ b/samples/browseable/RenderScriptIntrinsic/src/com.example.android.renderscriptintrinsic/ThumbnailRadioButton.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 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.renderscriptintrinsic;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import android.os.Build;
+import android.view.Gravity;
+import android.widget.RadioButton;
+import android.content.Context;
+import android.util.AttributeSet;
+
+/*
+ A button with Thumbnail which extends Radio Button.
+ The widget override a background drawable of Radio Button with a StateList Drawable.
+ Each state has a LayerDrawable with a Thumbnail image and a Focus rectangle.
+ It's using original Radio Buttons text as a label, because LayerDrawable showed some issues with Canvas.drawText().
+ */
+public class ThumbnailRadioButton extends RadioButton {
+    public ThumbnailRadioButton(Context context) {
+        super(context);
+        init();
+    }
+
+    public ThumbnailRadioButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public ThumbnailRadioButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    private void init() {
+        setButtonDrawable(android.R.color.transparent);
+    }
+
+    public void setThumbnail(Bitmap bitmap) {
+        //Bitmap drawable
+        BitmapDrawable bmp = new BitmapDrawable(getResources(), bitmap);
+        bmp.setGravity(Gravity.CENTER);
+
+        int strokeWidth = 24;
+        //Checked state
+        ShapeDrawable rectChecked = new ShapeDrawable(new RectShape());
+        rectChecked.getPaint().setColor(0xFFFFFFFF);
+        rectChecked.getPaint().setStyle(Paint.Style.STROKE);
+        rectChecked.getPaint().setStrokeWidth(strokeWidth);
+        rectChecked.setIntrinsicWidth(bitmap.getWidth() + strokeWidth);
+        rectChecked.setIntrinsicHeight(bitmap.getHeight() + strokeWidth);
+        Drawable drawableArray[] = new Drawable[]{bmp, rectChecked};
+        LayerDrawable layerChecked = new LayerDrawable(drawableArray);
+
+        //Unchecked state
+        ShapeDrawable rectUnchecked = new ShapeDrawable(new RectShape());
+        rectUnchecked.getPaint().setColor(0x0);
+        rectUnchecked.getPaint().setStyle(Paint.Style.STROKE);
+        rectUnchecked.getPaint().setStrokeWidth(strokeWidth);
+        rectUnchecked.setIntrinsicWidth(bitmap.getWidth() + strokeWidth);
+        rectUnchecked.setIntrinsicHeight(bitmap.getHeight() + strokeWidth);
+        Drawable drawableArray2[] = new Drawable[]{bmp, rectUnchecked};
+        LayerDrawable layerUnchecked = new LayerDrawable(drawableArray2);
+
+        //Statelist drawable
+        StateListDrawable states = new StateListDrawable();
+        states.addState(new int[]{android.R.attr.state_checked},
+                layerChecked);
+        states.addState(new int[]{},
+                layerUnchecked);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+            setBackground(states);
+        else
+            setBackgroundDrawable(states);
+
+        //Offset text to center/bottom of the checkbox
+        Paint paint = new Paint();
+        paint.setAntiAlias(true);
+        paint.setTextSize(getTextSize());
+        paint.setTypeface(getTypeface());
+        float w = paint.measureText(getText(), 0, getText().length());
+        setPadding(getPaddingLeft() + (int) ((bitmap.getWidth() - w) / 2.f + .5f),
+                getPaddingTop() + (int) (bitmap.getHeight() * 0.70),
+                getPaddingRight(),
+                getPaddingBottom());
+
+        setShadowLayer(5, 0, 0, Color.BLACK);
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/AndroidManifest.xml b/samples/browseable/RepeatingAlarm/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/repeatingAlarm/AndroidManifest.xml
rename to samples/browseable/RepeatingAlarm/AndroidManifest.xml
diff --git a/samples/browseable/RepeatingAlarm/_index.jd b/samples/browseable/RepeatingAlarm/_index.jd
new file mode 100644
index 0000000..69f7ee1
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/_index.jd
@@ -0,0 +1,9 @@
+
+
+
+page.tags="RepeatingAlarm"
+sample.group=Background
+@jd:body
+	
+		
+<p>This sample demonstrates how to implement a repeating alarm using an	{@link android.app.AlarmManager}.</p>
diff --git a/samples/browseable/repeatingAlarm/res/drawable-hdpi/ic_launcher.png b/samples/browseable/RepeatingAlarm/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/RepeatingAlarm/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/repeatingAlarm/res/drawable-hdpi/tile.9.png b/samples/browseable/RepeatingAlarm/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/drawable-hdpi/tile.9.png
rename to samples/browseable/RepeatingAlarm/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/repeatingAlarm/res/drawable-mdpi/ic_launcher.png b/samples/browseable/RepeatingAlarm/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/RepeatingAlarm/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/repeatingAlarm/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/RepeatingAlarm/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/RepeatingAlarm/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/repeatingAlarm/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/RepeatingAlarm/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/RepeatingAlarm/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/RepeatingAlarm/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/RepeatingAlarm/res/layout-sw600dp-land/activity_main.xml
new file mode 100755
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/RepeatingAlarm/res/layout-sw600dp/activity_main.xml b/samples/browseable/RepeatingAlarm/res/layout-sw600dp/activity_main.xml
new file mode 100755
index 0000000..f6f4157
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:id="@+id/sample_main_layout" >
+
+    <TextView android:id="@+id/sample_output"
+        style="@style/Widget.SampleMessage"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/intro_message"
+        android:padding="16dp"
+        android:layout_margin="16dp"/>
+    <fragment
+        android:name="com.example.android.common.logger.LogFragment"
+        android:id="@+id/log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/RepeatingAlarm/res/layout/activity_main.xml b/samples/browseable/RepeatingAlarm/res/layout/activity_main.xml
new file mode 100755
index 0000000..6f41369
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/res/layout/activity_main.xml
@@ -0,0 +1,39 @@
+<!--
+  Copyright 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:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:padding="16dp" />
+    <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dp"
+            android:background="@android:color/darker_gray"/>
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+</LinearLayout>
diff --git a/samples/browseable/repeatingAlarm/res/menu/main.xml b/samples/browseable/RepeatingAlarm/res/menu/main.xml
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/menu/main.xml
rename to samples/browseable/RepeatingAlarm/res/menu/main.xml
diff --git a/samples/browseable/repeatingAlarm/res/values-sw600dp/dimens.xml b/samples/browseable/RepeatingAlarm/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/values-sw600dp/dimens.xml
rename to samples/browseable/RepeatingAlarm/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/RepeatingAlarm/res/values-sw600dp/template-styles.xml b/samples/browseable/RepeatingAlarm/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..b6ea1a0
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright 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>
+
+    <style name="Widget.SampleMessage">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+
+        <item name="android:layout_margin">16dp</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+
+</resources>
diff --git a/samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml b/samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/RepeatingAlarm/res/values/base-strings.xml b/samples/browseable/RepeatingAlarm/res/values/base-strings.xml
new file mode 100644
index 0000000..6b89192
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">RepeatingAlarm</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+                Introductory text that explains what the sample is intended to demonstrate. Edit
+                in template-params.xml.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/repeatingAlarm/res/values/strings.xml b/samples/browseable/RepeatingAlarm/res/values/strings.xml
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/values/strings.xml
rename to samples/browseable/RepeatingAlarm/res/values/strings.xml
diff --git a/samples/browseable/repeatingAlarm/res/values/dimens.xml b/samples/browseable/RepeatingAlarm/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/values/dimens.xml
rename to samples/browseable/RepeatingAlarm/res/values/template-dimens.xml
diff --git a/samples/browseable/repeatingAlarm/res/values/styles.xml b/samples/browseable/RepeatingAlarm/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/repeatingAlarm/res/values/styles.xml
rename to samples/browseable/RepeatingAlarm/res/values/template-styles.xml
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/Log.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogNode.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogView.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/RepeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java b/samples/browseable/RepeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java
new file mode 100644
index 0000000..147f576
--- /dev/null
+++ b/samples/browseable/RepeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java
@@ -0,0 +1,84 @@
+/*
+* Copyright 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.repeatingalarm;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description
+ * and a few action bar buttons.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    public static final String FRAGTAG = "RepeatingAlarmFragment";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            RepeatingAlarmFragment fragment = new RepeatingAlarmFragment();
+            transaction.add(fragment, FRAGTAG);
+            transaction.commit();
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java b/samples/browseable/RepeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java
rename to samples/browseable/RepeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java
diff --git a/samples/browseable/ShareActionProvider/_index.jd b/samples/browseable/ShareActionProvider/_index.jd
deleted file mode 100644
index 31d855e..0000000
--- a/samples/browseable/ShareActionProvider/_index.jd
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-page.tags="ShareActionProvider"
-sample.group=UI
-@jd:body
-
-<p>This sample demonstrates how to use a
-context-sensitive {@link android.support.v7.widget.ShareActionProvider} that is
-backward compatible.</p>
-<p>The activity in this sample extends from
-{@link android.support.v7.app.ActionBarActivity}, which provides the
-functionality necessary to display a compatible action bar on devices
-running Android 2.1 and higher.</p>
diff --git a/samples/browseable/ShareActionProvider/res/values/base-strings.xml b/samples/browseable/ShareActionProvider/res/values/base-strings.xml
deleted file mode 100644
index 4ca9558..0000000
--- a/samples/browseable/ShareActionProvider/res/values/base-strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 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">ShareActionProvider</string>
-    <string name="intro_message">
-        <![CDATA[
-        
-            
-            This sample shows you how a provide a context-sensitive ShareActionProvider with
-            ActionBarCompat, backwards compatible to API v7.
-            
-        
-        ]]>
-    </string>
-</resources>
diff --git a/samples/browseable/ShareActionProvider/res/values/styles.xml b/samples/browseable/ShareActionProvider/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/ShareActionProvider/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/SlidingTabsBasic/AndroidManifest.xml b/samples/browseable/SlidingTabsBasic/AndroidManifest.xml
new file mode 100644
index 0000000..31cbfb8
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.slidingtabsbasic"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="19" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/SlidingTabsBasic/_index.jd b/samples/browseable/SlidingTabsBasic/_index.jd
new file mode 100644
index 0000000..261885c
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/_index.jd
@@ -0,0 +1,14 @@
+
+
+
+page.tags="SlidingTabsBasic"
+sample.group=UI
+@jd:body
+
+<p>
+  This sample shows how to use <code><a href=
+  "{@docRoot}samples/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.html">
+  SlidingTabLayout</a></code> to display a custom {@link
+  android.support.v4.view.ViewPager ViewPager} title strip that gives
+  continuous feedback to the user when scrolling.
+</p>
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsBasic/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SlidingTabsBasic/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..53ebb57
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/SlidingTabsBasic/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/SlidingTabsBasic/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsBasic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SlidingTabsBasic/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..33aa87a
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsBasic/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SlidingTabsBasic/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6a4ba00
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsBasic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SlidingTabsBasic/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..c3744cd
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsBasic/res/layout-w720dp/activity_main.xml b/samples/browseable/SlidingTabsBasic/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/SlidingTabsBasic/res/layout/activity_main.xml b/samples/browseable/SlidingTabsBasic/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/SlidingTabsBasic/res/layout/fragment_sample.xml b/samples/browseable/SlidingTabsBasic/res/layout/fragment_sample.xml
new file mode 100644
index 0000000..6ac3690
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/layout/fragment_sample.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:orientation="vertical">
+
+    <com.example.android.common.view.SlidingTabLayout
+          android:id="@+id/sliding_tabs"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content" />
+
+    <android.support.v4.view.ViewPager
+          android:id="@+id/viewpager"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1"
+          android:background="@android:color/white"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsBasic/res/layout/pager_item.xml b/samples/browseable/SlidingTabsBasic/res/layout/pager_item.xml
new file mode 100644
index 0000000..ce4413f
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/layout/pager_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:orientation="vertical"
+      android:gravity="center">
+
+    <TextView
+          android:id="@+id/item_subtitle"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:textAppearance="?android:attr/textAppearanceLarge"
+          android:text="Page:"/>
+
+    <TextView
+          android:id="@+id/item_title"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:textSize="80sp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsBasic/res/menu/main.xml b/samples/browseable/SlidingTabsBasic/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/SlidingTabsBasic/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/dimens.xml
copy to samples/browseable/SlidingTabsBasic/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/SlidingTabsBasic/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/styles.xml
copy to samples/browseable/SlidingTabsBasic/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/SlidingTabsBasic/res/values-v11/template-styles.xml b/samples/browseable/SlidingTabsBasic/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/SlidingTabsBasic/res/values/base-strings.xml b/samples/browseable/SlidingTabsBasic/res/values/base-strings.xml
new file mode 100644
index 0000000..c7f26bf
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">SlidingTabsBasic</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            A basic sample which shows how to use SlidingTabLayout to display a custom
+            ViewPager title strip which gives continuous feedback to the user when scrolling.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/SlidingTabsBasic/res/values/strings.xml b/samples/browseable/SlidingTabsBasic/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/SlidingTabsBasic/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/SlidingTabsBasic/res/values/template-dimens.xml
diff --git a/samples/browseable/SlidingTabsBasic/res/values/template-styles.xml b/samples/browseable/SlidingTabsBasic/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/SlidingTabsBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/MainActivity.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/MainActivity.java
new file mode 100644
index 0000000..ac405d1
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.slidingtabsbasic;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        SlidingTabsBasicFragment fragment = new SlidingTabsBasicFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/SlidingTabsBasicFragment.java b/samples/browseable/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/SlidingTabsBasicFragment.java
new file mode 100644
index 0000000..80f7b10
--- /dev/null
+++ b/samples/browseable/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/SlidingTabsBasicFragment.java
@@ -0,0 +1,160 @@
+/*
+ * 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.slidingtabsbasic;
+
+import com.example.android.common.logger.Log;
+import com.example.android.common.view.SlidingTabLayout;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * A basic sample which shows how to use {@link com.example.android.common.view.SlidingTabLayout}
+ * to display a custom {@link ViewPager} title strip which gives continuous feedback to the user
+ * when scrolling.
+ */
+public class SlidingTabsBasicFragment extends Fragment {
+
+    static final String LOG_TAG = "SlidingTabsBasicFragment";
+
+    /**
+     * A custom {@link ViewPager} title strip which looks much like Tabs present in Android v4.0 and
+     * above, but is designed to give continuous feedback to the user when scrolling.
+     */
+    private SlidingTabLayout mSlidingTabLayout;
+
+    /**
+     * A {@link ViewPager} which will be used in conjunction with the {@link SlidingTabLayout} above.
+     */
+    private ViewPager mViewPager;
+
+    /**
+     * Inflates the {@link View} which will be displayed by this {@link Fragment}, from the app's
+     * resources.
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_sample, container, false);
+    }
+
+    // BEGIN_INCLUDE (fragment_onviewcreated)
+    /**
+     * This is called after the {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} has finished.
+     * Here we can pick out the {@link View}s we need to configure from the content view.
+     *
+     * We set the {@link ViewPager}'s adapter to be an instance of {@link SamplePagerAdapter}. The
+     * {@link SlidingTabLayout} is then given the {@link ViewPager} so that it can populate itself.
+     *
+     * @param view View created in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
+     */
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        // BEGIN_INCLUDE (setup_viewpager)
+        // Get the ViewPager and set it's PagerAdapter so that it can display items
+        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
+        mViewPager.setAdapter(new SamplePagerAdapter());
+        // END_INCLUDE (setup_viewpager)
+
+        // BEGIN_INCLUDE (setup_slidingtablayout)
+        // Give the SlidingTabLayout the ViewPager, this must be done AFTER the ViewPager has had
+        // it's PagerAdapter set.
+        mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
+        mSlidingTabLayout.setViewPager(mViewPager);
+        // END_INCLUDE (setup_slidingtablayout)
+    }
+    // END_INCLUDE (fragment_onviewcreated)
+
+    /**
+     * The {@link android.support.v4.view.PagerAdapter} used to display pages in this sample.
+     * The individual pages are simple and just display two lines of text. The important section of
+     * this class is the {@link #getPageTitle(int)} method which controls what is displayed in the
+     * {@link SlidingTabLayout}.
+     */
+    class SamplePagerAdapter extends PagerAdapter {
+
+        /**
+         * @return the number of pages to display
+         */
+        @Override
+        public int getCount() {
+            return 10;
+        }
+
+        /**
+         * @return true if the value returned from {@link #instantiateItem(ViewGroup, int)} is the
+         * same object as the {@link View} added to the {@link ViewPager}.
+         */
+        @Override
+        public boolean isViewFromObject(View view, Object o) {
+            return o == view;
+        }
+
+        // BEGIN_INCLUDE (pageradapter_getpagetitle)
+        /**
+         * Return the title of the item at {@code position}. This is important as what this method
+         * returns is what is displayed in the {@link SlidingTabLayout}.
+         * <p>
+         * Here we construct one using the position value, but for real application the title should
+         * refer to the item's contents.
+         */
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return "Item " + (position + 1);
+        }
+        // END_INCLUDE (pageradapter_getpagetitle)
+
+        /**
+         * Instantiate the {@link View} which should be displayed at {@code position}. Here we
+         * inflate a layout from the apps resources and then change the text view to signify the position.
+         */
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            // Inflate a new layout from our resources
+            View view = getActivity().getLayoutInflater().inflate(R.layout.pager_item,
+                    container, false);
+            // Add the newly created View to the ViewPager
+            container.addView(view);
+
+            // Retrieve a TextView from the inflated View, and update it's text
+            TextView title = (TextView) view.findViewById(R.id.item_title);
+            title.setText(String.valueOf(position + 1));
+
+            Log.i(LOG_TAG, "instantiateItem() [position: " + position + "]");
+
+            // Return the View
+            return view;
+        }
+
+        /**
+         * Destroy the item from the {@link ViewPager}. In our case this is simply removing the
+         * {@link View}.
+         */
+        @Override
+        public void destroyItem(ViewGroup container, int position, Object object) {
+            container.removeView((View) object);
+            Log.i(LOG_TAG, "destroyItem() [position: " + position + "]");
+        }
+
+    }
+}
diff --git a/samples/browseable/SlidingTabsColors/AndroidManifest.xml b/samples/browseable/SlidingTabsColors/AndroidManifest.xml
new file mode 100644
index 0000000..be4a43a
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.slidingtabscolors"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="19" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/SlidingTabsColors/_index.jd b/samples/browseable/SlidingTabsColors/_index.jd
new file mode 100644
index 0000000..342ee15
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/_index.jd
@@ -0,0 +1,14 @@
+
+
+
+page.tags="SlidingTabsColors"
+sample.group=UI
+@jd:body
+
+<p>
+  This sample shows a more advanced example of how to use a <code><a href=
+  "{@docRoot}samples/SlidingTabsColors/src/com.example.android.common/view/SlidingTabLayout.html">
+  SlidingTabLayout</a></code> to display a custom {@link
+  android.support.v4.view.ViewPager ViewPager} title strip, with custom
+  coloring for each tab.
+</p>
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsColors/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SlidingTabsColors/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..66542ee
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/SlidingTabsColors/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/SlidingTabsColors/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsColors/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SlidingTabsColors/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..56e4726
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsColors/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SlidingTabsColors/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..ba41664
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsColors/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SlidingTabsColors/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..c9a51f6
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SlidingTabsColors/res/layout-w720dp/activity_main.xml b/samples/browseable/SlidingTabsColors/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/SlidingTabsColors/res/layout/activity_main.xml b/samples/browseable/SlidingTabsColors/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/SlidingTabsColors/res/layout/fragment_sample.xml b/samples/browseable/SlidingTabsColors/res/layout/fragment_sample.xml
new file mode 100644
index 0000000..605cba7
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/layout/fragment_sample.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <com.example.android.common.view.SlidingTabLayout
+          android:id="@+id/sliding_tabs"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content" />
+
+    <android.support.v4.view.ViewPager
+          android:id="@+id/viewpager"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1"
+          android:background="@android:color/white" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsColors/res/layout/pager_item.xml b/samples/browseable/SlidingTabsColors/res/layout/pager_item.xml
new file mode 100644
index 0000000..039ceb1
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/layout/pager_item.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:gravity="center">
+
+    <TextView
+          android:id="@+id/item_title"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <TextView
+          android:id="@+id/item_indicator_color"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <TextView
+          android:id="@+id/item_divider_color"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsColors/res/menu/main.xml b/samples/browseable/SlidingTabsColors/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml b/samples/browseable/SlidingTabsColors/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml
copy to samples/browseable/SlidingTabsColors/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml b/samples/browseable/SlidingTabsColors/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml
copy to samples/browseable/SlidingTabsColors/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/SlidingTabsColors/res/values-v11/template-styles.xml b/samples/browseable/SlidingTabsColors/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/SlidingTabsColors/res/values/base-strings.xml b/samples/browseable/SlidingTabsColors/res/values/base-strings.xml
new file mode 100644
index 0000000..89cac2d
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/values/base-strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">SlidingTabsColors</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            A more advanced sample which shows how to use SlidingTabLayout to display a custom
+            ViewPager title strip, with custom coloring for each tab.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/SlidingTabsColors/res/values/strings.xml b/samples/browseable/SlidingTabsColors/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/SlidingTabsColors/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/SlidingTabsColors/res/values/template-dimens.xml
diff --git a/samples/browseable/SlidingTabsColors/res/values/template-styles.xml b/samples/browseable/SlidingTabsColors/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/SlidingTabsColors/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/SlidingTabsColors/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/SlidingTabsColors/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SlidingTabsColors/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/ContentFragment.java b/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/ContentFragment.java
new file mode 100644
index 0000000..4715fd7
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/ContentFragment.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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.slidingtabscolors;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Simple Fragment used to display some meaningful content for each page in the sample's
+ * {@link android.support.v4.view.ViewPager}.
+ */
+public class ContentFragment extends Fragment {
+
+    private static final String KEY_TITLE = "title";
+    private static final String KEY_INDICATOR_COLOR = "indicator_color";
+    private static final String KEY_DIVIDER_COLOR = "divider_color";
+
+    /**
+     * @return a new instance of {@link ContentFragment}, adding the parameters into a bundle and
+     * setting them as arguments.
+     */
+    public static ContentFragment newInstance(CharSequence title, int indicatorColor,
+            int dividerColor) {
+        Bundle bundle = new Bundle();
+        bundle.putCharSequence(KEY_TITLE, title);
+        bundle.putInt(KEY_INDICATOR_COLOR, indicatorColor);
+        bundle.putInt(KEY_DIVIDER_COLOR, dividerColor);
+
+        ContentFragment fragment = new ContentFragment();
+        fragment.setArguments(bundle);
+
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.pager_item, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        Bundle args = getArguments();
+
+        if (args != null) {
+            TextView title = (TextView) view.findViewById(R.id.item_title);
+            title.setText("Title: " + args.getCharSequence(KEY_TITLE));
+
+            int indicatorColor = args.getInt(KEY_INDICATOR_COLOR);
+            TextView indicatorColorView = (TextView) view.findViewById(R.id.item_indicator_color);
+            indicatorColorView.setText("Indicator: #" + Integer.toHexString(indicatorColor));
+            indicatorColorView.setTextColor(indicatorColor);
+
+            int dividerColor = args.getInt(KEY_DIVIDER_COLOR);
+            TextView dividerColorView = (TextView) view.findViewById(R.id.item_divider_color);
+            dividerColorView.setText("Divider: #" + Integer.toHexString(dividerColor));
+            dividerColorView.setTextColor(dividerColor);
+        }
+    }
+}
diff --git a/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/MainActivity.java b/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/MainActivity.java
new file mode 100644
index 0000000..d3d7567
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.slidingtabscolors;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        SlidingTabsColorsFragment fragment = new SlidingTabsColorsFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/SlidingTabsColorsFragment.java b/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/SlidingTabsColorsFragment.java
new file mode 100644
index 0000000..1e5c3e3
--- /dev/null
+++ b/samples/browseable/SlidingTabsColors/src/com.example.android.slidingtabscolors/SlidingTabsColorsFragment.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 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.slidingtabscolors;
+
+import com.example.android.common.view.SlidingTabLayout;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A basic sample which shows how to use {@link com.example.android.common.view.SlidingTabLayout}
+ * to display a custom {@link ViewPager} title strip which gives continuous feedback to the user
+ * when scrolling.
+ */
+public class SlidingTabsColorsFragment extends Fragment {
+
+    /**
+     * This class represents a tab to be displayed by {@link ViewPager} and it's associated
+     * {@link SlidingTabLayout}.
+     */
+    static class SamplePagerItem {
+        private final CharSequence mTitle;
+        private final int mIndicatorColor;
+        private final int mDividerColor;
+
+        SamplePagerItem(CharSequence title, int indicatorColor, int dividerColor) {
+            mTitle = title;
+            mIndicatorColor = indicatorColor;
+            mDividerColor = dividerColor;
+        }
+
+        /**
+         * @return A new {@link Fragment} to be displayed by a {@link ViewPager}
+         */
+        Fragment createFragment() {
+            return ContentFragment.newInstance(mTitle, mIndicatorColor, mDividerColor);
+        }
+
+        /**
+         * @return the title which represents this tab. In this sample this is used directly by
+         * {@link android.support.v4.view.PagerAdapter#getPageTitle(int)}
+         */
+        CharSequence getTitle() {
+            return mTitle;
+        }
+
+        /**
+         * @return the color to be used for indicator on the {@link SlidingTabLayout}
+         */
+        int getIndicatorColor() {
+            return mIndicatorColor;
+        }
+
+        /**
+         * @return the color to be used for right divider on the {@link SlidingTabLayout}
+         */
+        int getDividerColor() {
+            return mDividerColor;
+        }
+    }
+
+    static final String LOG_TAG = "SlidingTabsColorsFragment";
+
+    /**
+     * A custom {@link ViewPager} title strip which looks much like Tabs present in Android v4.0 and
+     * above, but is designed to give continuous feedback to the user when scrolling.
+     */
+    private SlidingTabLayout mSlidingTabLayout;
+
+    /**
+     * A {@link ViewPager} which will be used in conjunction with the {@link SlidingTabLayout} above.
+     */
+    private ViewPager mViewPager;
+
+    /**
+     * List of {@link SamplePagerItem} which represent this sample's tabs.
+     */
+    private List<SamplePagerItem> mTabs = new ArrayList<SamplePagerItem>();
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // BEGIN_INCLUDE (populate_tabs)
+        /**
+         * Populate our tab list with tabs. Each item contains a title, indicator color and divider
+         * color, which are used by {@link SlidingTabLayout}.
+         */
+        mTabs.add(new SamplePagerItem(
+                getString(R.string.tab_stream), // Title
+                Color.BLUE, // Indicator color
+                Color.GRAY // Divider color
+        ));
+
+        mTabs.add(new SamplePagerItem(
+                getString(R.string.tab_messages), // Title
+                Color.RED, // Indicator color
+                Color.GRAY // Divider color
+        ));
+
+        mTabs.add(new SamplePagerItem(
+                getString(R.string.tab_photos), // Title
+                Color.YELLOW, // Indicator color
+                Color.GRAY // Divider color
+        ));
+
+        mTabs.add(new SamplePagerItem(
+                getString(R.string.tab_notifications), // Title
+                Color.GREEN, // Indicator color
+                Color.GRAY // Divider color
+        ));
+        // END_INCLUDE (populate_tabs)
+    }
+
+    /**
+     * Inflates the {@link View} which will be displayed by this {@link Fragment}, from the app's
+     * resources.
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_sample, container, false);
+    }
+
+    // BEGIN_INCLUDE (fragment_onviewcreated)
+    /**
+     * This is called after the {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} has finished.
+     * Here we can pick out the {@link View}s we need to configure from the content view.
+     *
+     * We set the {@link ViewPager}'s adapter to be an instance of
+     * {@link SampleFragmentPagerAdapter}. The {@link SlidingTabLayout} is then given the
+     * {@link ViewPager} so that it can populate itself.
+     *
+     * @param view View created in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
+     */
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        // BEGIN_INCLUDE (setup_viewpager)
+        // Get the ViewPager and set it's PagerAdapter so that it can display items
+        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
+        mViewPager.setAdapter(new SampleFragmentPagerAdapter(getChildFragmentManager()));
+        // END_INCLUDE (setup_viewpager)
+
+        // BEGIN_INCLUDE (setup_slidingtablayout)
+        // Give the SlidingTabLayout the ViewPager, this must be done AFTER the ViewPager has had
+        // it's PagerAdapter set.
+        mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
+        mSlidingTabLayout.setViewPager(mViewPager);
+
+        // BEGIN_INCLUDE (tab_colorizer)
+        // Set a TabColorizer to customize the indicator and divider colors. Here we just retrieve
+        // the tab at the position, and return it's set color
+        mSlidingTabLayout.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
+
+            @Override
+            public int getIndicatorColor(int position) {
+                return mTabs.get(position).getIndicatorColor();
+            }
+
+            @Override
+            public int getDividerColor(int position) {
+                return mTabs.get(position).getDividerColor();
+            }
+
+        });
+        // END_INCLUDE (tab_colorizer)
+        // END_INCLUDE (setup_slidingtablayout)
+    }
+    // END_INCLUDE (fragment_onviewcreated)
+
+    /**
+     * The {@link FragmentPagerAdapter} used to display pages in this sample. The individual pages
+     * are instances of {@link ContentFragment} which just display three lines of text. Each page is
+     * created by the relevant {@link SamplePagerItem} for the requested position.
+     * <p>
+     * The important section of this class is the {@link #getPageTitle(int)} method which controls
+     * what is displayed in the {@link SlidingTabLayout}.
+     */
+    class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
+
+        SampleFragmentPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        /**
+         * Return the {@link android.support.v4.app.Fragment} to be displayed at {@code position}.
+         * <p>
+         * Here we return the value returned from {@link SamplePagerItem#createFragment()}.
+         */
+        @Override
+        public Fragment getItem(int i) {
+            return mTabs.get(i).createFragment();
+        }
+
+        @Override
+        public int getCount() {
+            return mTabs.size();
+        }
+
+        // BEGIN_INCLUDE (pageradapter_getpagetitle)
+        /**
+         * Return the title of the item at {@code position}. This is important as what this method
+         * returns is what is displayed in the {@link SlidingTabLayout}.
+         * <p>
+         * Here we return the value returned from {@link SamplePagerItem#getTitle()}.
+         */
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return mTabs.get(position).getTitle();
+        }
+        // END_INCLUDE (pageradapter_getpagetitle)
+
+    }
+
+}
\ No newline at end of file
diff --git a/samples/browseable/StorageClient/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/StorageClient/res/layout-sw600dp-land/activity_main.xml
new file mode 100755
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/StorageClient/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/StorageClient/res/layout-sw600dp/activity_main.xml b/samples/browseable/StorageClient/res/layout-sw600dp/activity_main.xml
new file mode 100755
index 0000000..f6f4157
--- /dev/null
+++ b/samples/browseable/StorageClient/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:id="@+id/sample_main_layout" >
+
+    <TextView android:id="@+id/sample_output"
+        style="@style/Widget.SampleMessage"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/intro_message"
+        android:padding="16dp"
+        android:layout_margin="16dp"/>
+    <fragment
+        android:name="com.example.android.common.logger.LogFragment"
+        android:id="@+id/log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/StorageClient/res/layout/activity_main.xml b/samples/browseable/StorageClient/res/layout/activity_main.xml
index bc5a575..6f41369 100755
--- a/samples/browseable/StorageClient/res/layout/activity_main.xml
+++ b/samples/browseable/StorageClient/res/layout/activity_main.xml
@@ -24,7 +24,8 @@
               android:layout_weight="1"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:text="@string/intro_message"
+              android:padding="16dp" />
     <View
             android:layout_width="fill_parent"
             android:layout_height="1dp"
diff --git a/samples/browseable/StorageClient/res/values-sw600dp/dimens.xml b/samples/browseable/StorageClient/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/StorageClient/res/values-sw600dp/dimens.xml
rename to samples/browseable/StorageClient/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/StorageClient/res/values-sw600dp/template-styles.xml b/samples/browseable/StorageClient/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..b6ea1a0
--- /dev/null
+++ b/samples/browseable/StorageClient/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright 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>
+
+    <style name="Widget.SampleMessage">
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+
+        <item name="android:layout_margin">16dp</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+
+</resources>
diff --git a/samples/browseable/StorageClient/res/values-v11/template-styles.xml b/samples/browseable/StorageClient/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/StorageClient/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/StorageClient/res/values/dimens.xml b/samples/browseable/StorageClient/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/StorageClient/res/values/dimens.xml
rename to samples/browseable/StorageClient/res/values/template-dimens.xml
diff --git a/samples/browseable/StorageClient/res/values/styles.xml b/samples/browseable/StorageClient/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/StorageClient/res/values/styles.xml
rename to samples/browseable/StorageClient/res/values/template-styles.xml
diff --git a/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java b/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java
index 69c75eb..106f26b 100644
--- a/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java
+++ b/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java
@@ -19,6 +19,7 @@
 
 package com.example.android.storageclient;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
@@ -74,6 +75,9 @@
         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
                 .findFragmentById(R.id.log_fragment);
         msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
 
         Log.i(TAG, "Ready");
     }
diff --git a/samples/browseable/StorageProvider/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/StorageProvider/res/layout-sw600dp-land/activity_main.xml
new file mode 100755
index 0000000..653454b
--- /dev/null
+++ b/samples/browseable/StorageProvider/res/layout-sw600dp-land/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:id="@+id/sample_main_layout">
+    <TextView android:id="@+id/sample_output"
+              style="@style/Widget.SampleMessage"
+              android:background="@android:color/white"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:text="@string/intro_message"
+              android:layout_margin="16dp" />
+    <fragment
+            android:name="com.example.android.common.logger.LogFragment"
+            android:id="@+id/log_fragment"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/StorageProvider/res/layout-sw600dp/activity_main.xml b/samples/browseable/StorageProvider/res/layout-sw600dp/activity_main.xml
new file mode 100755
index 0000000..f6f4157
--- /dev/null
+++ b/samples/browseable/StorageProvider/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,37 @@
+<!--
+  Copyright 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:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:id="@+id/sample_main_layout" >
+
+    <TextView android:id="@+id/sample_output"
+        style="@style/Widget.SampleMessage"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/intro_message"
+        android:padding="16dp"
+        android:layout_margin="16dp"/>
+    <fragment
+        android:name="com.example.android.common.logger.LogFragment"
+        android:id="@+id/log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="16dp" />
+</LinearLayout>
diff --git a/samples/browseable/StorageProvider/res/layout/activity_main.xml b/samples/browseable/StorageProvider/res/layout/activity_main.xml
index bc5a575..6f41369 100755
--- a/samples/browseable/StorageProvider/res/layout/activity_main.xml
+++ b/samples/browseable/StorageProvider/res/layout/activity_main.xml
@@ -24,7 +24,8 @@
               android:layout_weight="1"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:text="@string/intro_message" />
+              android:text="@string/intro_message"
+              android:padding="16dp" />
     <View
             android:layout_width="fill_parent"
             android:layout_height="1dp"
diff --git a/samples/browseable/StorageProvider/res/values-sw600dp/template-styles.xml b/samples/browseable/StorageProvider/res/values-sw600dp/template-styles.xml
index 03d1974..b6ea1a0 100644
--- a/samples/browseable/StorageProvider/res/values-sw600dp/template-styles.xml
+++ b/samples/browseable/StorageProvider/res/values-sw600dp/template-styles.xml
@@ -17,9 +17,17 @@
 <resources>
 
     <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceLarge</item>
-        <item name="android:lineSpacingMultiplier">1.2</item>
+        <item name="android:padding">@dimen/margin_medium</item>
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+
+        <item name="android:layout_margin">16dp</item>
         <item name="android:shadowDy">-6.5</item>
     </style>
 
+    <style name="Log" parent="Widget.SampleOutput">
+        <item name="android:typeface">monospace</item>
+    </style>
+
+
 </resources>
diff --git a/samples/browseable/StorageProvider/res/values-v11/template-styles.xml b/samples/browseable/StorageProvider/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/StorageProvider/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/StorageProvider/src/com.example.android.storageprovider/MainActivity.java b/samples/browseable/StorageProvider/src/com.example.android.storageprovider/MainActivity.java
index 5f04a62..3108c1f 100644
--- a/samples/browseable/StorageProvider/src/com.example.android.storageprovider/MainActivity.java
+++ b/samples/browseable/StorageProvider/src/com.example.android.storageprovider/MainActivity.java
@@ -19,6 +19,7 @@
 
 package com.example.android.storageprovider;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v4.app.FragmentTransaction;
 import android.view.Menu;
@@ -74,6 +75,9 @@
         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
                 .findFragmentById(R.id.log_fragment);
         msgFilter.setNext(logFragment.getLogView());
+        logFragment.getLogView().setTextAppearance(this, R.style.Log);
+        logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
 
         Log.i(TAG, "Ready");
     }
diff --git a/samples/browseable/Styled/_index.jd b/samples/browseable/Styled/_index.jd
deleted file mode 100644
index 0816197..0000000
--- a/samples/browseable/Styled/_index.jd
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-page.tags="Styled ActionBarCompat"
-sample.group=UI
-@jd:body
-
-<p>This sample demonstrates how to use a backward compatible
-{@link android.support.v7.app.ActionBar} with a customized theme.</p>
-<p>The activity in this sample extends from
-{@link android.support.v7.app.ActionBarActivity}, which provides the
-functionality necessary to display a compatible action bar on devices
-running Android 2.1 and higher.</p>
diff --git a/samples/browseable/Styled/res/values/base-strings.xml b/samples/browseable/Styled/res/values/base-strings.xml
deleted file mode 100644
index 985b433..0000000
--- a/samples/browseable/Styled/res/values/base-strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 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">Styled</string>
-    <string name="intro_message">
-        <![CDATA[
-        
-            
-            This sample shows you how to use ActionBarCompat with a customized theme. It utilizes a
-            split action bar when running on a device with a narrow display, and show three tabs.
-            
-        
-        ]]>
-    </string>
-</resources>
diff --git a/samples/browseable/Styled/res/values/styles.xml b/samples/browseable/Styled/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/Styled/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/AndroidManifest.xml b/samples/browseable/SwipeRefreshLayoutBasic/AndroidManifest.xml
new file mode 100644
index 0000000..fa75453
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.swiperefreshlayoutbasic"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/SwipeRefreshLayoutBasic/_index.jd b/samples/browseable/SwipeRefreshLayoutBasic/_index.jd
new file mode 100644
index 0000000..bfbaddb
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/_index.jd
@@ -0,0 +1,11 @@
+
+
+
+page.tags="SwipeRefreshLayoutBasic"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to use {@link android.support.v4.widget.SwipeRefreshLayout} to add
+the <em>swipe-to-refresh</em> gesture to a {@link android.view.View}, which enables you to trigger
+a refresh by swiping down on the {@link android.view.View}. In this sample, the view that can
+be freshed is a {@link android.widget.ListView}.</p>
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..da15ae2
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/SwipeRefreshLayoutBasic/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..34c3f1c
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..83ac286
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..02802a3
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/layout-w720dp/activity_main.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/layout/activity_main.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/layout/fragment_sample.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/layout/fragment_sample.xml
new file mode 100644
index 0000000..8900f82
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/layout/fragment_sample.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+
+<android.support.v4.widget.SwipeRefreshLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:id="@+id/swiperefresh"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent">
+
+    <ListView
+          android:id="@android:id/list"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent" />
+
+</android.support.v4.widget.SwipeRefreshLayout>
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/menu/main.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/menu/main_menu.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/menu/main_menu.xml
new file mode 100644
index 0000000..de0c5f2
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/menu/main_menu.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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/menu_refresh"
+          android:title="@string/menu_refresh"
+          android:showAsAction="never" />
+
+</menu>
\ No newline at end of file
diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/dimens.xml
copy to samples/browseable/SwipeRefreshLayoutBasic/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/Basic/res/values-sw600dp/styles.xml
copy to samples/browseable/SwipeRefreshLayoutBasic/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/values/base-strings.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values/base-strings.xml
new file mode 100644
index 0000000..665d16f
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/values/base-strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">SwipeRefreshLayoutBasic</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            A basic sample which shows how to use SwipeRefreshLayout to add the \'swipe-to-refresh\'
+            gesture to a View, enabling the ability to trigger a refresh from swiping down on the view.
+            In this sample the View which can be refreshed is a ListView.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/values/colors.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values/colors.xml
new file mode 100644
index 0000000..ae1119e
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/values/colors.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <color name="swipe_color_1">#B6DB49</color>
+    <color name="swipe_color_2">#99CC00</color>
+    <color name="swipe_color_3">#8ABD00</color>
+    <color name="swipe_color_4">#7CAF00</color>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/values/strings.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/SwipeRefreshLayoutBasic/res/values/template-dimens.xml
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/res/values/template-styles.xml b/samples/browseable/SwipeRefreshLayoutBasic/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java
new file mode 100644
index 0000000..783735c
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/dummydata/Cheeses.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 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.common.dummydata;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Random;
+
+/**
+ * Dummy data.
+ */
+public class Cheeses {
+    static final String[] CHEESES = {
+            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+            "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+            "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+            "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+            "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+            "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+            "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+            "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+            "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+            "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+            "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+            "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+            "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+            "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+            "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+            "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+            "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+            "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+            "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+            "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+            "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+            "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+            "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+            "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+            "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+            "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+            "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+            "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+            "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+            "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+            "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+            "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+            "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+            "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+            "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+            "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+            "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+            "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+            "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+            "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+            "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+            "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+            "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+            "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+            "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+            "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+            "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+            "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+            "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+            "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+            "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+            "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+            "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+            "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+            "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+            "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+            "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+            "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+            "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+            "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+            "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+            "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+            "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+            "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+            "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+            "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+            "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+            "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+            "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+            "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+            "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+            "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+            "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+            "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+            "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+            "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+            "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+            "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+            "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+            "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+            "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+            "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+            "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+            "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+            "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+            "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+            "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+            "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+            "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+            "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+            "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+            "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+            "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+            "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+            "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+            "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+            "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+            "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+            "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+            "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+            "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+            "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+            "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+            "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+            "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+            "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+            "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+            "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+            "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+            "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+    };
+
+    public static ArrayList<String> asList() {
+        ArrayList<String> items = new ArrayList<String>();
+        for (int i = 0, z = CHEESES.length ; i < z ; i++) {
+            items.add(CHEESES[i]);
+        }
+        return items;
+    }
+
+    /**
+     * Return a list of random cheeses.
+     *
+     * @param count the amount of cheeses to return.
+     */
+    public static ArrayList<String> randomList(int count) {
+        Random random = new Random();
+        HashSet<String> items = new HashSet<String>();
+
+        // Make sure that don't infinity loop
+        count = Math.min(count, CHEESES.length);
+
+        while (items.size() < count) {
+            items.add(CHEESES[random.nextInt(CHEESES.length)]);
+        }
+
+        return new ArrayList<String>(items);
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java
new file mode 100644
index 0000000..f90aed1
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.swiperefreshlayoutbasic;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        SwipeRefreshLayoutBasicFragment fragment = new SwipeRefreshLayoutBasicFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java
new file mode 100644
index 0000000..13b22f5
--- /dev/null
+++ b/samples/browseable/SwipeRefreshLayoutBasic/src/com.example.android.swiperefreshlayoutbasic/SwipeRefreshLayoutBasicFragment.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2014 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.swiperefreshlayoutbasic;
+
+import com.example.android.common.dummydata.Cheeses;
+import com.example.android.common.logger.Log;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.List;
+
+/**
+ * A basic sample that shows how to use {@link android.support.v4.widget.SwipeRefreshLayout} to add
+ * the 'swipe-to-refresh' gesture to a layout. In this sample, SwipeRefreshLayout contains a
+ * scrollable {@link android.widget.ListView} as its only child.
+ *
+ * <p>To provide an accessible way to trigger the refresh, this app also provides a refresh
+ * action item.
+ *
+ * <p>In this sample app, the refresh updates the ListView with a random set of new items.
+ */
+public class SwipeRefreshLayoutBasicFragment extends Fragment {
+
+    private static final String LOG_TAG = SwipeRefreshLayoutBasicFragment.class.getSimpleName();
+
+    private static final int LIST_ITEM_COUNT = 20;
+
+    /**
+     * The {@link android.support.v4.widget.SwipeRefreshLayout} that detects swipe gestures and
+     * triggers callbacks in the app.
+     */
+    private SwipeRefreshLayout mSwipeRefreshLayout;
+
+    /**
+     * The {@link android.widget.ListView} that displays the content that should be refreshed.
+     */
+    private ListView mListView;
+
+    /**
+     * The {@link android.widget.ListAdapter} used to populate the {@link android.widget.ListView}
+     * defined in the previous statement.
+     */
+    private ArrayAdapter<String> mListAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Notify the system to allow an options menu for this fragment.
+        setHasOptionsMenu(true);
+    }
+
+    // BEGIN_INCLUDE (inflate_view)
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_sample, container, false);
+
+        // Retrieve the SwipeRefreshLayout and ListView instances
+        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh);
+
+        // BEGIN_INCLUDE (change_colors)
+        // Set the color scheme of the SwipeRefreshLayout by providing 4 color resource ids
+        mSwipeRefreshLayout.setColorScheme(
+                R.color.swipe_color_1, R.color.swipe_color_2,
+                R.color.swipe_color_3, R.color.swipe_color_4);
+        // END_INCLUDE (change_colors)
+
+        // Retrieve the ListView
+        mListView = (ListView) view.findViewById(android.R.id.list);
+
+        return view;
+    }
+    // END_INCLUDE (inflate_view)
+
+    // BEGIN_INCLUDE (setup_views)
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        /**
+         * Create an ArrayAdapter to contain the data for the ListView. Each item in the ListView
+         * uses the system-defined simple_list_item_1 layout that contains one TextView.
+         */
+        mListAdapter = new ArrayAdapter<String>(
+                getActivity(),
+                android.R.layout.simple_list_item_1,
+                android.R.id.text1,
+                Cheeses.randomList(LIST_ITEM_COUNT));
+
+        // Set the adapter between the ListView and its backing data.
+        mListView.setAdapter(mListAdapter);
+
+        // BEGIN_INCLUDE (setup_refreshlistener)
+        /**
+         * Implement {@link SwipeRefreshLayout.OnRefreshListener}. When users do the "swipe to
+         * refresh" gesture, SwipeRefreshLayout invokes
+         * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}. In
+         * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}, call a method that
+         * refreshes the content. Call the same method in response to the Refresh action from the
+         * action bar.
+         */
+        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout");
+
+                initiateRefresh();
+            }
+        });
+        // END_INCLUDE (setup_refreshlistener)
+    }
+    // END_INCLUDE (setup_views)
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.main_menu, menu);
+    }
+
+    // BEGIN_INCLUDE (setup_refresh_menu_listener)
+    /**
+     * Respond to the user's selection of the Refresh action item. Start the SwipeRefreshLayout
+     * progress bar, then initiate the background task that refreshes the content.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_refresh:
+                Log.i(LOG_TAG, "Refresh menu item selected");
+
+                // We make sure that the SwipeRefreshLayout is displaying it's refreshing indicator
+                if (!mSwipeRefreshLayout.isRefreshing()) {
+                    mSwipeRefreshLayout.setRefreshing(true);
+                }
+
+                // Start our refresh background task
+                initiateRefresh();
+
+                return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+    // END_INCLUDE (setup_refresh_menu_listener)
+
+    // BEGIN_INCLUDE (initiate_refresh)
+    /**
+     * By abstracting the refresh process to a single method, the app allows both the
+     * SwipeGestureLayout onRefresh() method and the Refresh action item to refresh the content.
+     */
+    private void initiateRefresh() {
+        Log.i(LOG_TAG, "initiateRefresh");
+
+        /**
+         * Execute the background task, which uses {@link android.os.AsyncTask} to load the data.
+         */
+        new DummyBackgroundTask().execute();
+    }
+    // END_INCLUDE (initiate_refresh)
+
+    // BEGIN_INCLUDE (refresh_complete)
+    /**
+     * When the AsyncTask finishes, it calls onRefreshComplete(), which updates the data in the
+     * ListAdapter and turns off the progress bar.
+     */
+    private void onRefreshComplete(List<String> result) {
+        Log.i(LOG_TAG, "onRefreshComplete");
+
+        // Remove all items from the ListAdapter, and then replace them with the new items
+        mListAdapter.clear();
+        for (String cheese : result) {
+            mListAdapter.add(cheese);
+        }
+
+        // Stop the refreshing indicator
+        mSwipeRefreshLayout.setRefreshing(false);
+    }
+    // END_INCLUDE (refresh_complete)
+
+    /**
+     * Dummy {@link AsyncTask} which simulates a long running task to fetch new cheeses.
+     */
+    private class DummyBackgroundTask extends AsyncTask<Void, Void, List<String>> {
+
+        static final int TASK_DURATION = 3 * 1000; // 3 seconds
+
+        @Override
+        protected List<String> doInBackground(Void... params) {
+            // Sleep for a small amount of time to simulate a background-task
+            try {
+                Thread.sleep(TASK_DURATION);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+            // Return a new random list of cheeses
+            return Cheeses.randomList(LIST_ITEM_COUNT);
+        }
+
+        @Override
+        protected void onPostExecute(List<String> result) {
+            super.onPostExecute(result);
+
+            // Tell the Fragment that the refresh has completed
+            onRefreshComplete(result);
+        }
+
+    }
+}
diff --git a/samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml b/samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml
new file mode 100644
index 0000000..be44930
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.swiperefreshlistfragment"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/SwipeRefreshListFragment/_index.jd b/samples/browseable/SwipeRefreshListFragment/_index.jd
new file mode 100644
index 0000000..3a34bb0
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/_index.jd
@@ -0,0 +1,12 @@
+
+
+
+page.tags="SwipeRefreshListFragment"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to use {@link android.support.v4.widget.SwipeRefreshLayout} within
+{@link android.app.ListFragment} to add the <em>swipe-to-refresh</em> gesture to a
+{@link android.widget.ListView}, which enables you to trigger a refresh by swiping down on that
+view. This functionality is provided by the <code>SwipeRefreshListFragment</code> class, which you
+can reuse.</p>
diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b10c5c1
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/SwipeRefreshListFragment/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..001eed9
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6fe78cc
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshListFragment/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshListFragment/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..5f89875
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshListFragment/res/layout-w720dp/activity_main.xml b/samples/browseable/SwipeRefreshListFragment/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml b/samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/SwipeRefreshListFragment/res/menu/main.xml b/samples/browseable/SwipeRefreshListFragment/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml b/samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml
new file mode 100644
index 0000000..c5e8954
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/menu/main_menu.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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">
+
+    <!--
+        A color scheme menu item used for demonstrating the use of SwipeRefreshLayout's color
+        scheme functionality. This kind of menu item should not be incorporated into your app,
+        it just to demonstrate the use of color. Instead you should choose a color scheme based
+        off of your application's branding.
+    -->
+    <item
+          android:id="@+id/menu_color_scheme"
+          android:title="@string/menu_color_scheme"
+          android:showAsAction="ifRoom">
+        <menu>
+            <group android:checkableBehavior="single">
+
+                <item
+                      android:id="@+id/menu_color_scheme_1"
+                      android:title="@string/menu_color_scheme_1" />
+
+                <item
+                      android:id="@+id/menu_color_scheme_2"
+                      android:title="@string/menu_color_scheme_2" />
+
+                <item
+                      android:id="@+id/menu_color_scheme_3"
+                      android:title="@string/menu_color_scheme_3" />
+
+            </group>
+        </menu>
+    </item>
+
+    <!--
+        Refresh action item which should be presented in the Action Bar's overflow area
+        by setting showAsAction='never'. This is so that users which are not using touch input
+        can still perform a refresh.
+    -->
+    <item
+          android:id="@+id/menu_refresh"
+          android:title="@string/menu_refresh"
+          android:showAsAction="never" />
+
+</menu>
\ No newline at end of file
diff --git a/samples/browseable/StorageClient/res/values-sw600dp/dimens.xml b/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/StorageClient/res/values-sw600dp/dimens.xml
copy to samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/StorageClient/res/values-sw600dp/styles.xml b/samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/StorageClient/res/values-sw600dp/styles.xml
rename to samples/browseable/SwipeRefreshListFragment/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml b/samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/SwipeRefreshListFragment/res/values/base-strings.xml b/samples/browseable/SwipeRefreshListFragment/res/values/base-strings.xml
new file mode 100644
index 0000000..f84c807
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/values/base-strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">SwipeRefreshListFragment</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            A sample which shows how to use SwipeRefreshLayout within a ListFragment to add the
+            \'swipe-to-refresh\' gesture to a ListView, enabling the ability to trigger a refresh
+            from swiping down on that view. This is provided through the re-usable
+            SwipeRefreshListFragment class.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/SwipeRefreshListFragment/res/values/colors.xml b/samples/browseable/SwipeRefreshListFragment/res/values/colors.xml
new file mode 100644
index 0000000..ae19990
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/values/colors.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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>
+
+    <!--
+        These are the different color schemes to be displayed by the SwipeRefreshLayout's loading
+        indicator. It expects exactly four colors.
+
+        Color scheme #1: a selection of holo colors.
+        Color scheme #2: a graduation of holo blue.
+        Color scheme #3: a graduation of holo green.
+    -->
+
+    <color name="color_scheme_1_1">#33B5E5</color>
+    <color name="color_scheme_1_2">#99CC00</color>
+    <color name="color_scheme_1_3">#FFBB33</color>
+    <color name="color_scheme_1_4">#FF4444</color>
+
+    <color name="color_scheme_2_1">#6DCAEC</color>
+    <color name="color_scheme_2_2">#33B5E5</color>
+    <color name="color_scheme_2_3">#24ADDE</color>
+    <color name="color_scheme_2_4">#16A5D7</color>
+
+    <color name="color_scheme_3_1">#B6DB49</color>
+    <color name="color_scheme_3_2">#99CC00</color>
+    <color name="color_scheme_3_3">#8ABD00</color>
+    <color name="color_scheme_3_4">#7CAF00</color>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshListFragment/res/values/strings.xml b/samples/browseable/SwipeRefreshListFragment/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/StorageClient/res/values/dimens.xml b/samples/browseable/SwipeRefreshListFragment/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/StorageClient/res/values/dimens.xml
copy to samples/browseable/SwipeRefreshListFragment/res/values/template-dimens.xml
diff --git a/samples/browseable/SwipeRefreshListFragment/res/values/template-styles.xml b/samples/browseable/SwipeRefreshListFragment/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java
new file mode 100644
index 0000000..783735c
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/dummydata/Cheeses.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 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.common.dummydata;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Random;
+
+/**
+ * Dummy data.
+ */
+public class Cheeses {
+    static final String[] CHEESES = {
+            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+            "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+            "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+            "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+            "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+            "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+            "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+            "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+            "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+            "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+            "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+            "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+            "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+            "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+            "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+            "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+            "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+            "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+            "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+            "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+            "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+            "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+            "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+            "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+            "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+            "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+            "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+            "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+            "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+            "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+            "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+            "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+            "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+            "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+            "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+            "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+            "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+            "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+            "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+            "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+            "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+            "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+            "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+            "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+            "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+            "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+            "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+            "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+            "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+            "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+            "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+            "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+            "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+            "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+            "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+            "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+            "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+            "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+            "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+            "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+            "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+            "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+            "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+            "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+            "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+            "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+            "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+            "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+            "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+            "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+            "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+            "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+            "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+            "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+            "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+            "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+            "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+            "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+            "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+            "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+            "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+            "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+            "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+            "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+            "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+            "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+            "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+            "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+            "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+            "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+            "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+            "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+            "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+            "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+            "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+            "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+            "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+            "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+            "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+            "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+            "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+            "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+            "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+            "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+            "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+            "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+            "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+            "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+            "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+            "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+    };
+
+    public static ArrayList<String> asList() {
+        ArrayList<String> items = new ArrayList<String>();
+        for (int i = 0, z = CHEESES.length ; i < z ; i++) {
+            items.add(CHEESES[i]);
+        }
+        return items;
+    }
+
+    /**
+     * Return a list of random cheeses.
+     *
+     * @param count the amount of cheeses to return.
+     */
+    public static ArrayList<String> randomList(int count) {
+        Random random = new Random();
+        HashSet<String> items = new HashSet<String>();
+
+        // Make sure that don't infinity loop
+        count = Math.min(count, CHEESES.length);
+
+        while (items.size() < count) {
+            items.add(CHEESES[random.nextInt(CHEESES.length)]);
+        }
+
+        return new ArrayList<String>(items);
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java
new file mode 100644
index 0000000..7f7a7c8
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.swiperefreshlistfragment;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        SwipeRefreshListFragmentFragment fragment = new SwipeRefreshListFragmentFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java
new file mode 100644
index 0000000..5796976
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragment.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2014 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.swiperefreshlistfragment;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+/**
+ * Subclass of {@link android.support.v4.app.ListFragment} which provides automatic support for
+ * providing the 'swipe-to-refresh' UX gesture by wrapping the the content view in a
+ * {@link android.support.v4.widget.SwipeRefreshLayout}.
+ */
+public class SwipeRefreshListFragment extends ListFragment {
+
+    private SwipeRefreshLayout mSwipeRefreshLayout;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+
+        // Create the list fragment's content view by calling the super method
+        final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState);
+
+        // Now create a SwipeRefreshLayout to wrap the fragment's content view
+        mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(container.getContext());
+
+        // Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills
+        // the SwipeRefreshLayout
+        mSwipeRefreshLayout.addView(listFragmentView,
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+
+        // Make sure that the SwipeRefreshLayout will fill the fragment
+        mSwipeRefreshLayout.setLayoutParams(
+                new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+
+        // Now return the SwipeRefreshLayout as this fragment's content view
+        return mSwipeRefreshLayout;
+    }
+
+    /**
+     * Set the {@link android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener} to listen for
+     * initiated refreshes.
+     *
+     * @see android.support.v4.widget.SwipeRefreshLayout#setOnRefreshListener(android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener)
+     */
+    public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) {
+        mSwipeRefreshLayout.setOnRefreshListener(listener);
+    }
+
+    /**
+     * Returns whether the {@link android.support.v4.widget.SwipeRefreshLayout} is currently
+     * refreshing or not.
+     *
+     * @see android.support.v4.widget.SwipeRefreshLayout#isRefreshing()
+     */
+    public boolean isRefreshing() {
+        return mSwipeRefreshLayout.isRefreshing();
+    }
+
+    /**
+     * Set whether the {@link android.support.v4.widget.SwipeRefreshLayout} should be displaying
+     * that it is refreshing or not.
+     *
+     * @see android.support.v4.widget.SwipeRefreshLayout#setRefreshing(boolean)
+     */
+    public void setRefreshing(boolean refreshing) {
+        mSwipeRefreshLayout.setRefreshing(refreshing);
+    }
+
+    /**
+     * Set the color scheme for the {@link android.support.v4.widget.SwipeRefreshLayout}.
+     *
+     * @see android.support.v4.widget.SwipeRefreshLayout#setColorScheme(int, int, int, int)
+     */
+    public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
+        mSwipeRefreshLayout.setColorScheme(colorRes1, colorRes2, colorRes3, colorRes4);
+    }
+
+    /**
+     * @return the fragment's {@link android.support.v4.widget.SwipeRefreshLayout} widget.
+     */
+    public SwipeRefreshLayout getSwipeRefreshLayout() {
+        return mSwipeRefreshLayout;
+    }
+
+    /**
+     * Sub-class of {@link android.support.v4.widget.SwipeRefreshLayout} for use in this
+     * {@link android.support.v4.app.ListFragment}. The reason that this is needed is because
+     * {@link android.support.v4.widget.SwipeRefreshLayout} only supports a single child, which it
+     * expects to be the one which triggers refreshes. In our case the layout's child is the content
+     * view returned from
+     * {@link android.support.v4.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}
+     * which is a {@link android.view.ViewGroup}.
+     *
+     * <p>To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to
+     * override the default behavior and properly signal when a gesture is possible. This is done by
+     * overriding {@link #canChildScrollUp()}.
+     */
+    private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout {
+
+        public ListFragmentSwipeRefreshLayout(Context context) {
+            super(context);
+        }
+
+        /**
+         * As mentioned above, we need to override this method to properly signal when a
+         * 'swipe-to-refresh' is possible.
+         *
+         * @return true if the {@link android.widget.ListView} is visible and can scroll up.
+         */
+        @Override
+        public boolean canChildScrollUp() {
+            final ListView listView = getListView();
+            if (listView.getVisibility() == View.VISIBLE) {
+                return canListViewScrollUp(listView);
+            } else {
+                return false;
+            }
+        }
+
+    }
+
+    // BEGIN_INCLUDE (check_list_can_scroll)
+    /**
+     * Utility method to check whether a {@link ListView} can scroll up from it's current position.
+     * Handles platform version differences, providing backwards compatible functionality where
+     * needed.
+     */
+    private static boolean canListViewScrollUp(ListView listView) {
+        if (android.os.Build.VERSION.SDK_INT >= 14) {
+            // For ICS and above we can call canScrollVertically() to determine this
+            return ViewCompat.canScrollVertically(listView, -1);
+        } else {
+            // Pre-ICS we need to manually check the first visible item and the child view's top
+            // value
+            return listView.getChildCount() > 0 &&
+                    (listView.getFirstVisiblePosition() > 0
+                            || listView.getChildAt(0).getTop() < listView.getPaddingTop());
+        }
+    }
+    // END_INCLUDE (check_list_can_scroll)
+
+}
diff --git a/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java
new file mode 100644
index 0000000..1147ea8
--- /dev/null
+++ b/samples/browseable/SwipeRefreshListFragment/src/com.example.android.swiperefreshlistfragment/SwipeRefreshListFragmentFragment.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2014 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.swiperefreshlistfragment;
+
+import com.example.android.common.dummydata.Cheeses;
+import com.example.android.common.logger.Log;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+
+import java.util.List;
+
+/**
+ * A sample which shows how to use {@link android.support.v4.widget.SwipeRefreshLayout} within a
+ * {@link android.support.v4.app.ListFragment} to add the 'swipe-to-refresh' gesture to a
+ * {@link android.widget.ListView}. This is provided through the provided re-usable
+ * {@link SwipeRefreshListFragment} class.
+ *
+ * <p>To provide an accessible way to trigger the refresh, this app also provides a refresh
+ * action item. This item should be displayed in the Action Bar's overflow item.
+ *
+ * <p>In this sample app, the refresh updates the ListView with a random set of new items.
+ *
+ * <p>This sample also provides the functionality to change the colors displayed in the
+ * {@link android.support.v4.widget.SwipeRefreshLayout} through the options menu. This is meant to
+ * showcase the use of color rather than being something that should be integrated into apps.
+ */
+public class SwipeRefreshListFragmentFragment extends SwipeRefreshListFragment {
+
+    private static final String LOG_TAG = SwipeRefreshListFragmentFragment.class.getSimpleName();
+
+    private static final int LIST_ITEM_COUNT = 20;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Notify the system to allow an options menu for this fragment.
+        setHasOptionsMenu(true);
+    }
+
+    // BEGIN_INCLUDE (setup_views)
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        /**
+         * Create an ArrayAdapter to contain the data for the ListView. Each item in the ListView
+         * uses the system-defined simple_list_item_1 layout that contains one TextView.
+         */
+        ListAdapter adapter = new ArrayAdapter<String>(
+                getActivity(),
+                android.R.layout.simple_list_item_1,
+                android.R.id.text1,
+                Cheeses.randomList(LIST_ITEM_COUNT));
+
+        // Set the adapter between the ListView and its backing data.
+        setListAdapter(adapter);
+
+        // BEGIN_INCLUDE (setup_refreshlistener)
+        /**
+         * Implement {@link SwipeRefreshLayout.OnRefreshListener}. When users do the "swipe to
+         * refresh" gesture, SwipeRefreshLayout invokes
+         * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}. In
+         * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}, call a method that
+         * refreshes the content. Call the same method in response to the Refresh action from the
+         * action bar.
+         */
+        setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout");
+
+                initiateRefresh();
+            }
+        });
+        // END_INCLUDE (setup_refreshlistener)
+    }
+    // END_INCLUDE (setup_views)
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.main_menu, menu);
+    }
+
+    // BEGIN_INCLUDE (setup_refresh_menu_listener)
+    /**
+     * Respond to the user's selection of the Refresh action item. Start the SwipeRefreshLayout
+     * progress bar, then initiate the background task that refreshes the content.
+     *
+     * <p>A color scheme menu item used for demonstrating the use of SwipeRefreshLayout's color
+     * scheme functionality. This kind of menu item should not be incorporated into your app,
+     * it just to demonstrate the use of color. Instead you should choose a color scheme based
+     * off of your application's branding.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_refresh:
+                Log.i(LOG_TAG, "Refresh menu item selected");
+
+                // We make sure that the SwipeRefreshLayout is displaying it's refreshing indicator
+                if (!isRefreshing()) {
+                    setRefreshing(true);
+                }
+
+                // Start our refresh background task
+                initiateRefresh();
+                return true;
+
+            case R.id.menu_color_scheme_1:
+                Log.i(LOG_TAG, "setColorScheme #1");
+                item.setChecked(true);
+
+                // Change the colors displayed by the SwipeRefreshLayout by providing it with 4
+                // color resource ids
+                setColorScheme(R.color.color_scheme_1_1, R.color.color_scheme_1_2,
+                        R.color.color_scheme_1_3, R.color.color_scheme_1_4);
+                return true;
+
+            case R.id.menu_color_scheme_2:
+                Log.i(LOG_TAG, "setColorScheme #2");
+                item.setChecked(true);
+
+                // Change the colors displayed by the SwipeRefreshLayout by providing it with 4
+                // color resource ids
+                setColorScheme(R.color.color_scheme_2_1, R.color.color_scheme_2_2,
+                        R.color.color_scheme_2_3, R.color.color_scheme_2_4);
+                return true;
+
+            case R.id.menu_color_scheme_3:
+                Log.i(LOG_TAG, "setColorScheme #3");
+                item.setChecked(true);
+
+                // Change the colors displayed by the SwipeRefreshLayout by providing it with 4
+                // color resource ids
+                setColorScheme(R.color.color_scheme_3_1, R.color.color_scheme_3_2,
+                        R.color.color_scheme_3_3, R.color.color_scheme_3_4);
+                return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+    // END_INCLUDE (setup_refresh_menu_listener)
+
+    // BEGIN_INCLUDE (initiate_refresh)
+    /**
+     * By abstracting the refresh process to a single method, the app allows both the
+     * SwipeGestureLayout onRefresh() method and the Refresh action item to refresh the content.
+     */
+    private void initiateRefresh() {
+        Log.i(LOG_TAG, "initiateRefresh");
+
+        /**
+         * Execute the background task, which uses {@link android.os.AsyncTask} to load the data.
+         */
+        new DummyBackgroundTask().execute();
+    }
+    // END_INCLUDE (initiate_refresh)
+
+    // BEGIN_INCLUDE (refresh_complete)
+    /**
+     * When the AsyncTask finishes, it calls onRefreshComplete(), which updates the data in the
+     * ListAdapter and turns off the progress bar.
+     */
+    private void onRefreshComplete(List<String> result) {
+        Log.i(LOG_TAG, "onRefreshComplete");
+
+        // Remove all items from the ListAdapter, and then replace them with the new items
+        ArrayAdapter<String> adapter = (ArrayAdapter<String>) getListAdapter();
+        adapter.clear();
+        for (String cheese : result) {
+            adapter.add(cheese);
+        }
+
+        // Stop the refreshing indicator
+        setRefreshing(false);
+    }
+    // END_INCLUDE (refresh_complete)
+
+    /**
+     * Dummy {@link AsyncTask} which simulates a long running task to fetch new cheeses.
+     */
+    private class DummyBackgroundTask extends AsyncTask<Void, Void, List<String>> {
+
+        static final int TASK_DURATION = 3 * 1000; // 3 seconds
+
+        @Override
+        protected List<String> doInBackground(Void... params) {
+            // Sleep for a small amount of time to simulate a background-task
+            try {
+                Thread.sleep(TASK_DURATION);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+            // Return a new random list of cheeses
+            return Cheeses.randomList(LIST_ITEM_COUNT);
+        }
+
+        @Override
+        protected void onPostExecute(List<String> result) {
+            super.onPostExecute(result);
+
+            // Tell the Fragment that the refresh has completed
+            onRefreshComplete(result);
+        }
+
+    }
+
+}
diff --git a/samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml b/samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml
new file mode 100644
index 0000000..aba15e0
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.swiperefreshmultipleviews"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  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/browseable/SwipeRefreshMultipleViews/_index.jd b/samples/browseable/SwipeRefreshMultipleViews/_index.jd
new file mode 100644
index 0000000..70ff8ea
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/_index.jd
@@ -0,0 +1,12 @@
+
+
+
+page.tags="SwipeRefreshMultipleViews"
+sample.group=UI
+@jd:body
+
+<p>This sample demonstrates how to use {@link android.support.v4.widget.SwipeRefreshLayout} to add
+the <em>swipe-to-refresh</em> gesture to a layout with multiple children, which enables you to
+trigger a refresh by swiping down on the view. In this sample
+{@link android.support.v4.widget.SwipeRefreshLayout} contains a scrollable
+{@link android.widget.GridView} with an empty {@link android.widget.TextView}.</p>
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..9fbf7ab
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/Basic/res/drawable-hdpi/tile.9.png
copy to samples/browseable/SwipeRefreshMultipleViews/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f834108
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..3197133
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d19455c
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/layout-w720dp/activity_main.xml b/samples/browseable/SwipeRefreshMultipleViews/res/layout-w720dp/activity_main.xml
new file mode 100755
index 0000000..c9a52f6
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/layout-w720dp/activity_main.xml
@@ -0,0 +1,73 @@
+<!--
+  Copyright 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:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <LinearLayout
+          android:id="@+id/sample_output"
+          android:layout_width="0px"
+          android:layout_height="match_parent"
+          android:layout_weight="1"
+          android:orientation="vertical">
+
+        <FrameLayout
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/margin_medium"
+                  android:paddingRight="@dimen/margin_medium"
+                  android:paddingTop="@dimen/margin_large"
+                  android:paddingBottom="@dimen/margin_large"
+                  android:text="@string/intro_message" />
+        </FrameLayout>
+
+        <View
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="@android:color/darker_gray" />
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1" />
+
+    </LinearLayout>
+
+    <View
+          android:layout_width="1dp"
+          android:layout_height="match_parent"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="0px"
+          android:layout_height="match_parent" />
+
+</LinearLayout>
+
+
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml b/samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml
new file mode 100755
index 0000000..1ae4f98
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+<!--
+  Copyright 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:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:id="@+id/sample_main_layout">
+
+    <ViewAnimator
+          android:id="@+id/sample_output"
+          android:layout_width="match_parent"
+          android:layout_height="0px"
+          android:layout_weight="1">
+
+        <ScrollView
+              style="@style/Widget.SampleMessageTile"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+            <TextView
+                  style="@style/Widget.SampleMessage"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:paddingLeft="@dimen/horizontal_page_margin"
+                  android:paddingRight="@dimen/horizontal_page_margin"
+                  android:paddingTop="@dimen/vertical_page_margin"
+                  android:paddingBottom="@dimen/vertical_page_margin"
+                  android:text="@string/intro_message" />
+        </ScrollView>
+
+        <fragment
+              android:name="com.example.android.common.logger.LogFragment"
+              android:id="@+id/log_fragment"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    </ViewAnimator>
+
+    <View
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="@android:color/darker_gray" />
+
+    <FrameLayout
+          android:id="@+id/sample_content_fragment"
+          android:layout_weight="2"
+          android:layout_width="match_parent"
+          android:layout_height="0px" />
+
+</LinearLayout>
+
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml b/samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml
new file mode 100644
index 0000000..077f3f1
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/layout/fragment_sample.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+
+<com.example.android.swiperefreshmultipleviews.MultiSwipeRefreshLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:id="@+id/swiperefresh"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent">
+
+    <FrameLayout
+          android:layout_width="match_parent"
+          android:layout_height="match_parent">
+
+        <GridView
+              android:id="@android:id/list"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:numColumns="2" />
+
+        <TextView
+              android:id="@android:id/empty"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="@string/empty_text"
+              android:layout_gravity="center"/>
+
+    </FrameLayout>
+
+</com.example.android.swiperefreshmultipleviews.MultiSwipeRefreshLayout>
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml
new file mode 100644
index 0000000..b49c2c5
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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/menu_toggle_log"
+          android:showAsAction="always"
+          android:title="@string/sample_show_log" />
+</menu>
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml
new file mode 100644
index 0000000..d8fa6e6
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/menu/main_menu.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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/menu_clear"
+          android:title="@string/menu_clear"
+          android:showAsAction="never" />
+
+    <item
+          android:id="@+id/menu_refresh"
+          android:title="@string/menu_refresh"
+          android:showAsAction="never" />
+
+</menu>
\ No newline at end of file
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml
copy to samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml
copy to samples/browseable/SwipeRefreshMultipleViews/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values/base-strings.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values/base-strings.xml
new file mode 100644
index 0000000..d7c1c9e
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/values/base-strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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">SwipeRefreshMultipleViews</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+            A sample which shows how to use SwipeRefreshLayout to add the \'swipe-to-refresh\'
+            gesture to a layout with multiple children, enabling the ability to trigger a
+            refresh from swiping down on the visible view. In this sample, SwipeRefreshLayout
+            contains a scrollable GridView, along with a TextView empty view.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values/color.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values/color.xml
new file mode 100644
index 0000000..48039b0
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/values/color.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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>
+
+    <color name="swipe_color_1">#B6DB49</color>
+    <color name="swipe_color_2">#99CC00</color>
+    <color name="swipe_color_3">#8ABD00</color>
+    <color name="swipe_color_4">#7CAF00</color>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values/strings.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values/strings.xml
new file mode 100755
index 0000000..7b9d9ec
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 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="sample_show_log">Show Log</string>
+    <string name="sample_hide_log">Hide Log</string>
+</resources>
diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AppRestrictions/res/values/dimens.xml
copy to samples/browseable/SwipeRefreshMultipleViews/res/values/template-dimens.xml
diff --git a/samples/browseable/SwipeRefreshMultipleViews/res/values/template-styles.xml b/samples/browseable/SwipeRefreshMultipleViews/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java
new file mode 100644
index 0000000..783735c
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/dummydata/Cheeses.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 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.common.dummydata;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Random;
+
+/**
+ * Dummy data.
+ */
+public class Cheeses {
+    static final String[] CHEESES = {
+            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+            "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+            "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+            "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+            "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+            "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+            "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+            "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+            "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+            "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+            "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+            "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+            "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+            "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+            "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+            "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+            "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+            "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+            "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+            "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+            "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+            "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+            "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+            "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+            "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+            "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+            "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+            "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+            "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+            "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+            "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+            "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+            "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+            "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+            "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+            "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+            "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+            "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+            "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+            "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+            "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+            "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+            "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+            "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+            "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+            "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+            "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+            "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+            "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+            "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+            "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+            "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+            "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+            "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+            "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+            "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+            "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+            "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+            "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+            "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+            "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+            "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+            "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+            "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+            "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+            "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+            "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+            "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+            "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+            "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+            "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+            "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+            "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+            "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+            "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+            "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+            "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+            "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+            "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+            "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+            "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+            "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+            "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+            "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+            "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+            "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+            "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+            "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+            "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+            "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+            "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+            "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+            "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+            "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+            "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+            "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+            "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+            "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+            "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+            "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+            "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+            "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+            "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+            "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+            "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+            "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+            "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+            "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+            "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+            "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+    };
+
+    public static ArrayList<String> asList() {
+        ArrayList<String> items = new ArrayList<String>();
+        for (int i = 0, z = CHEESES.length ; i < z ; i++) {
+            items.add(CHEESES[i]);
+        }
+        return items;
+    }
+
+    /**
+     * Return a list of random cheeses.
+     *
+     * @param count the amount of cheeses to return.
+     */
+    public static ArrayList<String> randomList(int count) {
+        Random random = new Random();
+        HashSet<String> items = new HashSet<String>();
+
+        // Make sure that don't infinity loop
+        count = Math.min(count, CHEESES.length);
+
+        while (items.size() < count) {
+            items.add(CHEESES[random.nextInt(CHEESES.length)]);
+        }
+
+        return new ArrayList<String>(items);
+    }
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 0000000..20049e3
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * 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.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+    /**
+     * Allows complete control over the colors drawn in the tab layout. Set with
+     * {@link #setCustomTabColorizer(TabColorizer)}.
+     */
+    public interface TabColorizer {
+
+        /**
+         * @return return the color of the indicator used when {@code position} is selected.
+         */
+        int getIndicatorColor(int position);
+
+        /**
+         * @return return the color of the divider drawn to the right of {@code position}.
+         */
+        int getDividerColor(int position);
+
+    }
+
+    private static final int TITLE_OFFSET_DIPS = 24;
+    private static final int TAB_VIEW_PADDING_DIPS = 16;
+    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+    private int mTitleOffset;
+
+    private int mTabViewLayoutId;
+    private int mTabViewTextViewId;
+
+    private ViewPager mViewPager;
+    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+    private final SlidingTabStrip mTabStrip;
+
+    public SlidingTabLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Make sure that the Tab Strips fills this View
+        setFillViewport(true);
+
+        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Set the custom {@link TabColorizer} to be used.
+     *
+     * If you only require simple custmisation then you can use
+     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+     * similar effects.
+     */
+    public void setCustomTabColorizer(TabColorizer tabColorizer) {
+        mTabStrip.setCustomTabColorizer(tabColorizer);
+    }
+
+    /**
+     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+     * circular array. Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setSelectedIndicatorColors(int... colors) {
+        mTabStrip.setSelectedIndicatorColors(colors);
+    }
+
+    /**
+     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+     * Providing one color will mean that all tabs are indicated with the same color.
+     */
+    public void setDividerColors(int... colors) {
+        mTabStrip.setDividerColors(colors);
+    }
+
+    /**
+     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+     * that the layout can update it's scroll position correctly.
+     *
+     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+     */
+    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+        mViewPagerPageChangeListener = listener;
+    }
+
+    /**
+     * Set the custom layout to be inflated for the tab views.
+     *
+     * @param layoutResId Layout id to be inflated
+     * @param textViewId id of the {@link TextView} in the inflated view
+     */
+    public void setCustomTabView(int layoutResId, int textViewId) {
+        mTabViewLayoutId = layoutResId;
+        mTabViewTextViewId = textViewId;
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(ViewPager viewPager) {
+        mTabStrip.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    /**
+     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+     * {@link #setCustomTabView(int, int)}.
+     */
+    protected TextView createDefaultTabView(Context context) {
+        TextView textView = new TextView(context);
+        textView.setGravity(Gravity.CENTER);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+        textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            // If we're running on Honeycomb or newer, then we can use the Theme's
+            // selectableItemBackground to ensure that the View has a pressed state
+            TypedValue outValue = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+                    outValue, true);
+            textView.setBackgroundResource(outValue.resourceId);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+            textView.setAllCaps(true);
+        }
+
+        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+        textView.setPadding(padding, padding, padding, padding);
+
+        return textView;
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+        final View.OnClickListener tabClickListener = new TabClickListener();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            View tabView = null;
+            TextView tabTitleView = null;
+
+            if (mTabViewLayoutId != 0) {
+                // If there is a custom tab view layout id set, try and inflate it
+                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+                        false);
+                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+            }
+
+            if (tabView == null) {
+                tabView = createDefaultTabView(getContext());
+            }
+
+            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+                tabTitleView = (TextView) tabView;
+            }
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabView.setOnClickListener(tabClickListener);
+
+            mTabStrip.addView(tabView);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mViewPager != null) {
+            scrollToTab(mViewPager.getCurrentItem(), 0);
+        }
+    }
+
+    private void scrollToTab(int tabIndex, int positionOffset) {
+        final int tabStripChildCount = mTabStrip.getChildCount();
+        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+            return;
+        }
+
+        View selectedChild = mTabStrip.getChildAt(tabIndex);
+        if (selectedChild != null) {
+            int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+            if (tabIndex > 0 || positionOffset > 0) {
+                // If we're not at the first child and are mid-scroll, make sure we obey the offset
+                targetScrollX -= mTitleOffset;
+            }
+
+            scrollTo(targetScrollX, 0);
+        }
+    }
+
+    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            int tabStripChildCount = mTabStrip.getChildCount();
+            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+                return;
+            }
+
+            mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+            View selectedTitle = mTabStrip.getChildAt(position);
+            int extraOffset = (selectedTitle != null)
+                    ? (int) (positionOffset * selectedTitle.getWidth())
+                    : 0;
+            scrollToTab(position, extraOffset);
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+                        positionOffsetPixels);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+                mTabStrip.onViewPagerPageChanged(position, 0f);
+                scrollToTab(position, 0);
+            }
+
+            if (mViewPagerPageChangeListener != null) {
+                mViewPagerPageChangeListener.onPageSelected(position);
+            }
+        }
+
+    }
+
+    private class TabClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+                if (v == mTabStrip.getChildAt(i)) {
+                    mViewPager.setCurrentItem(i);
+                    return;
+                }
+            }
+        }
+    }
+
+}
diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 0000000..d5bbbae
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+    private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+    private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+    private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+    private final int mBottomBorderThickness;
+    private final Paint mBottomBorderPaint;
+
+    private final int mSelectedIndicatorThickness;
+    private final Paint mSelectedIndicatorPaint;
+
+    private final int mDefaultBottomBorderColor;
+
+    private final Paint mDividerPaint;
+    private final float mDividerHeight;
+
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+    private final SimpleTabColorizer mDefaultTabColorizer;
+
+    SlidingTabStrip(Context context) {
+        this(context, null);
+    }
+
+    SlidingTabStrip(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+
+        final float density = getResources().getDisplayMetrics().density;
+
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+        final int themeForegroundColor =  outValue.data;
+
+        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+        mDefaultTabColorizer = new SimpleTabColorizer();
+        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+        mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+                DEFAULT_DIVIDER_COLOR_ALPHA));
+
+        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+        mBottomBorderPaint = new Paint();
+        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+        mSelectedIndicatorPaint = new Paint();
+
+        mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+        mDividerPaint = new Paint();
+        mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+    }
+
+    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+        mCustomTabColorizer = customTabColorizer;
+        invalidate();
+    }
+
+    void setSelectedIndicatorColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setIndicatorColors(colors);
+        invalidate();
+    }
+
+    void setDividerColors(int... colors) {
+        // Make sure that the custom colorizer is removed
+        mCustomTabColorizer = null;
+        mDefaultTabColorizer.setDividerColors(colors);
+        invalidate();
+    }
+
+    void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int height = getHeight();
+        final int childCount = getChildCount();
+        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+                ? mCustomTabColorizer
+                : mDefaultTabColorizer;
+
+        // Thick colored underline below the current selection
+        if (childCount > 0) {
+            View selectedTitle = getChildAt(mSelectedPosition);
+            int left = selectedTitle.getLeft();
+            int right = selectedTitle.getRight();
+            int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+                if (color != nextColor) {
+                    color = blendColors(nextColor, color, mSelectionOffset);
+                }
+
+                // Draw the selection partway between the tabs
+                View nextTitle = getChildAt(mSelectedPosition + 1);
+                left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                        (1.0f - mSelectionOffset) * left);
+                right = (int) (mSelectionOffset * nextTitle.getRight() +
+                        (1.0f - mSelectionOffset) * right);
+            }
+
+            mSelectedIndicatorPaint.setColor(color);
+
+            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+                    height, mSelectedIndicatorPaint);
+        }
+
+        // Thin underline along the entire bottom edge
+        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+        // Vertical separators between the titles
+        int separatorTop = (height - dividerHeightPx) / 2;
+        for (int i = 0; i < childCount - 1; i++) {
+            View child = getChildAt(i);
+            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+                    separatorTop + dividerHeightPx, mDividerPaint);
+        }
+    }
+
+    /**
+     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+     */
+    private static int setColorAlpha(int color, byte alpha) {
+        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+     *              0.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRation = 1f - ratio;
+        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+        return Color.rgb((int) r, (int) g, (int) b);
+    }
+
+    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+        private int[] mIndicatorColors;
+        private int[] mDividerColors;
+
+        @Override
+        public final int getIndicatorColor(int position) {
+            return mIndicatorColors[position % mIndicatorColors.length];
+        }
+
+        @Override
+        public final int getDividerColor(int position) {
+            return mDividerColors[position % mDividerColors.length];
+        }
+
+        void setIndicatorColors(int... colors) {
+            mIndicatorColors = colors;
+        }
+
+        void setDividerColors(int... colors) {
+            mDividerColors = colors;
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java
new file mode 100644
index 0000000..0191d87
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MainActivity.java
@@ -0,0 +1,110 @@
+/*
+* Copyright 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.swiperefreshmultipleviews;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ViewAnimator;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description, sample log and a custom
+ * {@link android.support.v4.app.Fragment} which can display a view.
+ * <p>
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+    public static final String TAG = "MainActivity";
+
+    // Whether the Log Fragment is currently shown
+    private boolean mLogShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        SwipeRefreshMultipleViewsFragment fragment = new SwipeRefreshMultipleViewsFragment();
+        transaction.replace(R.id.sample_content_fragment, fragment);
+        transaction.commit();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+        logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+        logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+        return super.onPrepareOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_toggle_log:
+                mLogShown = !mLogShown;
+                ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+                if (mLogShown) {
+                    output.setDisplayedChild(1);
+                } else {
+                    output.setDisplayedChild(0);
+                }
+                supportInvalidateOptionsMenu();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /** Create a chain of targets that will receive log data */
+    @Override
+    public void initializeLogging() {
+        // Wraps Android's native log framework.
+        LogWrapper logWrapper = new LogWrapper();
+        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+        Log.setLogNode(logWrapper);
+
+        // Filter strips out everything except the message text.
+        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+        logWrapper.setNext(msgFilter);
+
+        // On screen logging via a fragment with a TextView.
+        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.log_fragment);
+        msgFilter.setNext(logFragment.getLogView());
+
+        Log.i(TAG, "Ready");
+    }
+}
diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java
new file mode 100644
index 0000000..4e50145
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/MultiSwipeRefreshLayout.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014 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.swiperefreshmultipleviews;
+
+import android.content.Context;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.AbsListView;
+
+/**
+ * A descendant of {@link android.support.v4.widget.SwipeRefreshLayout} which supports multiple
+ * child views triggering a refresh gesture. You set the views which can trigger the gesture via
+ * {@link #setSwipeableChildren(int...)}, providing it the child ids.
+ */
+public class MultiSwipeRefreshLayout extends SwipeRefreshLayout {
+
+    private View[] mSwipeableChildren;
+
+    public MultiSwipeRefreshLayout(Context context) {
+        super(context);
+    }
+
+    public MultiSwipeRefreshLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Set the children which can trigger a refresh by swiping down when they are visible. These
+     * views need to be a descendant of this view.
+     */
+    public void setSwipeableChildren(final int... ids) {
+        assert ids != null;
+
+        // Iterate through the ids and find the Views
+        mSwipeableChildren = new View[ids.length];
+        for (int i = 0; i < ids.length; i++) {
+            mSwipeableChildren[i] = findViewById(ids[i]);
+        }
+    }
+
+    // BEGIN_INCLUDE(can_child_scroll_up)
+    /**
+     * This method controls when the swipe-to-refresh gesture is triggered. By returning false here
+     * we are signifying that the view is in a state where a refresh gesture can start.
+     *
+     * <p>As {@link android.support.v4.widget.SwipeRefreshLayout} only supports one direct child by
+     * default, we need to manually iterate through our swipeable children to see if any are in a
+     * state to trigger the gesture. If so we return false to start the gesture.
+     */
+    @Override
+    public boolean canChildScrollUp() {
+        if (mSwipeableChildren != null && mSwipeableChildren.length > 0) {
+            // Iterate through the scrollable children and check if any of them can not scroll up
+            for (View view : mSwipeableChildren) {
+                if (view != null && view.isShown() && !canViewScrollUp(view)) {
+                    // If the view is shown, and can not scroll upwards, return false and start the
+                    // gesture.
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    // END_INCLUDE(can_child_scroll_up)
+
+    // BEGIN_INCLUDE(can_view_scroll_up)
+    /**
+     * Utility method to check whether a {@link View} can scroll up from it's current position.
+     * Handles platform version differences, providing backwards compatible functionality where
+     * needed.
+     */
+    private static boolean canViewScrollUp(View view) {
+        if (android.os.Build.VERSION.SDK_INT >= 14) {
+            // For ICS and above we can call canScrollVertically() to determine this
+            return ViewCompat.canScrollVertically(view, -1);
+        } else {
+            if (view instanceof AbsListView) {
+                // Pre-ICS we need to manually check the first visible item and the child view's top
+                // value
+                final AbsListView listView = (AbsListView) view;
+                return listView.getChildCount() > 0 &&
+                        (listView.getFirstVisiblePosition() > 0
+                                || listView.getChildAt(0).getTop() < listView.getPaddingTop());
+            } else {
+                // For all other view types we just check the getScrollY() value
+                return view.getScrollY() > 0;
+            }
+        }
+    }
+    // END_INCLUDE(can_view_scroll_up)
+}
diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java
new file mode 100644
index 0000000..e2b83d3
--- /dev/null
+++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2014 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.swiperefreshmultipleviews;
+
+import com.example.android.common.dummydata.Cheeses;
+import com.example.android.common.logger.Log;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.GridView;
+
+import java.util.List;
+
+/**
+ * A sample which shows how to use {@link android.support.v4.widget.SwipeRefreshLayout} to add
+ * the 'swipe-to-refresh' gesture to a layout with multiple children. In this sample,
+ * SwipeRefreshLayout contains a scrollable {@link android.widget.GridView}, along with a
+ * {@link android.widget.TextView} empty view.
+ *
+ * <p>To provide an accessible way to trigger the refresh, this app also provides a refresh
+ * action item.
+ *
+ * <p>In this sample app, the refresh updates the GridView with a random set of new items.
+ */
+public class SwipeRefreshMultipleViewsFragment extends Fragment {
+
+    private static final String LOG_TAG = SwipeRefreshMultipleViewsFragment.class.getSimpleName();
+
+    private static final int LIST_ITEM_COUNT = 40;
+
+    /**
+     * The {@link MultiSwipeRefreshLayout} that detects swipe gestures and triggers callbacks in
+     * the app.
+     */
+    private MultiSwipeRefreshLayout mSwipeRefreshLayout;
+
+    /**
+     * The {@link android.widget.GridView} that displays the content that should be refreshed.
+     */
+    private GridView mGridView;
+
+    /**
+     * The {@link android.widget.ListAdapter} used to populate the {@link android.widget.GridView}
+     * defined in the previous statement.
+     */
+    private ArrayAdapter<String> mListAdapter;
+
+    /**
+     * The {@link View} which is displayed when the GridView is empty.
+     */
+    private View mEmptyView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Notify the system to allow an options menu for this fragment.
+        setHasOptionsMenu(true);
+    }
+
+    // BEGIN_INCLUDE (inflate_view)
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_sample, container, false);
+
+        // Retrieve the SwipeRefreshLayout and GridView instances
+        mSwipeRefreshLayout = (MultiSwipeRefreshLayout) view.findViewById(R.id.swiperefresh);
+
+        // BEGIN_INCLUDE (change_colors)
+        // Set the color scheme of the SwipeRefreshLayout by providing 4 color resource ids
+        mSwipeRefreshLayout.setColorScheme(
+                R.color.swipe_color_1, R.color.swipe_color_2,
+                R.color.swipe_color_3, R.color.swipe_color_4);
+        // END_INCLUDE (change_colors)
+
+        // Retrieve the GridView
+        mGridView = (GridView) view.findViewById(android.R.id.list);
+
+        // Retrieve the empty view
+        mEmptyView = view.findViewById(android.R.id.empty);
+
+        return view;
+    }
+    // END_INCLUDE (inflate_view)
+
+    // BEGIN_INCLUDE (setup_views)
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        /**
+         * Create an ArrayAdapter to contain the data for the GridView. Each item in the GridView
+         * uses the system-defined simple_list_item_1 layout that contains one TextView. Initially
+         */
+        mListAdapter = new ArrayAdapter<String>(
+                getActivity(),
+                android.R.layout.simple_list_item_1,
+                android.R.id.text1);
+
+        // Set the adapter between the GridView and its backing data.
+        mGridView.setAdapter(mListAdapter);
+
+        // Set the empty view so that it is displayed as needed
+        mGridView.setEmptyView(mEmptyView);
+
+        // BEGIN_INCLUDE (setup_swipeable_children)
+        // Tell the MultiSwipeRefreshLayout which views are swipeable. In this case, the GridView
+        // and empty view.
+        mSwipeRefreshLayout.setSwipeableChildren(android.R.id.list, android.R.id.empty);
+        // END_INCLUDE (setup_swipeable_children)
+
+        // BEGIN_INCLUDE (setup_refreshlistener)
+        /**
+         * Implement {@link SwipeRefreshLayout.OnRefreshListener}. When users do the "swipe to
+         * refresh" gesture, SwipeRefreshLayout invokes
+         * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}. In
+         * {@link SwipeRefreshLayout.OnRefreshListener#onRefresh onRefresh()}, call a method that
+         * refreshes the content. Call the same method in response to the Refresh action from the
+         * action bar.
+         */
+        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout");
+
+                initiateRefresh();
+            }
+        });
+        // END_INCLUDE (setup_refreshlistener)
+    }
+    // END_INCLUDE (setup_views)
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.main_menu, menu);
+    }
+
+    // BEGIN_INCLUDE (setup_refresh_menu_listener)
+    /**
+     * Respond to the user's selection of the Refresh action item. Start the SwipeRefreshLayout
+     * progress bar, then initiate the background task that refreshes the content.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_clear:
+                Log.i(LOG_TAG, "Clear menu item selected");
+                mListAdapter.clear();
+                return true;
+
+            case R.id.menu_refresh:
+                Log.i(LOG_TAG, "Refresh menu item selected");
+
+                // We make sure that the SwipeRefreshLayout is displaying it's refreshing indicator
+                if (!mSwipeRefreshLayout.isRefreshing()) {
+                    mSwipeRefreshLayout.setRefreshing(true);
+                }
+
+                // Start our refresh background task
+                initiateRefresh();
+
+                return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+    // END_INCLUDE (setup_refresh_menu_listener)
+
+    // BEGIN_INCLUDE (initiate_refresh)
+    /**
+     * By abstracting the refresh process to a single method, the app allows both the
+     * SwipeGestureLayout onRefresh() method and the Refresh action item to refresh the content.
+     */
+    private void initiateRefresh() {
+        Log.i(LOG_TAG, "initiateRefresh");
+
+        /**
+         * Execute the background task, which uses {@link android.os.AsyncTask} to load the data.
+         */
+        new DummyBackgroundTask().execute();
+    }
+    // END_INCLUDE (initiate_refresh)
+
+    // BEGIN_INCLUDE (refresh_complete)
+    /**
+     * When the AsyncTask finishes, it calls onRefreshComplete(), which updates the data in the
+     * ListAdapter and turns off the progress bar.
+     */
+    private void onRefreshComplete(List<String> result) {
+        Log.i(LOG_TAG, "onRefreshComplete");
+
+        // Remove all items from the ListAdapter, and then replace them with the new items
+        mListAdapter.clear();
+        for (String cheese : result) {
+            mListAdapter.add(cheese);
+        }
+
+        // Stop the refreshing indicator
+        mSwipeRefreshLayout.setRefreshing(false);
+    }
+    // END_INCLUDE (refresh_complete)
+
+    /**
+     * Dummy {@link AsyncTask} which simulates a long running task to fetch new cheeses.
+     */
+    private class DummyBackgroundTask extends AsyncTask<Void, Void, List<String>> {
+
+        static final int TASK_DURATION = 3 * 1000; // 3 seconds
+
+        @Override
+        protected List<String> doInBackground(Void... params) {
+            // Sleep for a small amount of time to simulate a background-task
+            try {
+                Thread.sleep(TASK_DURATION);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+            // Return a new random list of cheeses
+            return Cheeses.randomList(LIST_ITEM_COUNT);
+        }
+
+        @Override
+        protected void onPostExecute(List<String> result) {
+            super.onPostExecute(result);
+
+            // Tell the Fragment that the refresh has completed
+            onRefreshComplete(result);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml b/samples/browseable/TextLinkify/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml
rename to samples/browseable/TextLinkify/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/TextLinkify/res/values-sw600dp/styles.xml b/samples/browseable/TextLinkify/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/TextLinkify/res/values-sw600dp/styles.xml
rename to samples/browseable/TextLinkify/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/TextLinkify/res/values-v11/template-styles.xml b/samples/browseable/TextLinkify/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/TextLinkify/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/TextLinkify/res/values/dimens.xml b/samples/browseable/TextLinkify/res/values/dimens.xml
index 39e710b..3b1975a 100644
--- a/samples/browseable/TextLinkify/res/values/dimens.xml
+++ b/samples/browseable/TextLinkify/res/values/dimens.xml
@@ -5,28 +5,19 @@
   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
+        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>
 
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 
 </resources>
diff --git a/samples/browseable/TextLinkify/res/values/styles.xml b/samples/browseable/TextLinkify/res/values/styles.xml
index 404623e..29c4230 100644
--- a/samples/browseable/TextLinkify/res/values/styles.xml
+++ b/samples/browseable/TextLinkify/res/values/styles.xml
@@ -5,38 +5,18 @@
   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
+        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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="LinkText">
+        <item name="android:paddingTop">9dp</item>
+        <item name="android:paddingBottom">9dp</item>
     </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
 </resources>
diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/TextLinkify/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/Basic/res/values/dimens.xml
copy to samples/browseable/TextLinkify/res/values/template-dimens.xml
diff --git a/samples/browseable/TextLinkify/res/values/template-styles.xml b/samples/browseable/TextLinkify/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/TextLinkify/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml b/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml
index 22074a2..686fe89 100644
--- a/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml
+++ b/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml
@@ -5,20 +5,20 @@
   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
+        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>
 
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+    <!--
+         Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw600dp devices (e.g. 7" tablets) here.
+    -->
 
 </resources>
diff --git a/samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml b/samples/browseable/TextSwitcher/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml
copy to samples/browseable/TextSwitcher/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/TextSwitcher/res/values-sw600dp/styles.xml b/samples/browseable/TextSwitcher/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/TextSwitcher/res/values-sw600dp/styles.xml
rename to samples/browseable/TextSwitcher/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/TextSwitcher/res/values-v11/template-styles.xml b/samples/browseable/TextSwitcher/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/samples/browseable/TextSwitcher/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/samples/browseable/TextSwitcher/res/values/dimens.xml b/samples/browseable/TextSwitcher/res/values/dimens.xml
index 39e710b..3b1975a 100644
--- a/samples/browseable/TextSwitcher/res/values/dimens.xml
+++ b/samples/browseable/TextSwitcher/res/values/dimens.xml
@@ -5,28 +5,19 @@
   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
+        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>
 
-    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
-    <dimen name="margin_tiny">4dp</dimen>
-    <dimen name="margin_small">8dp</dimen>
-    <dimen name="margin_medium">16dp</dimen>
-    <dimen name="margin_large">32dp</dimen>
-    <dimen name="margin_huge">64dp</dimen>
-
-    <!-- Semantic definitions -->
-
-    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
-    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 
 </resources>
diff --git a/samples/browseable/TextSwitcher/res/values/styles.xml b/samples/browseable/TextSwitcher/res/values/styles.xml
deleted file mode 100644
index 404623e..0000000
--- a/samples/browseable/TextSwitcher/res/values/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
-  Copyright 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>
-
-    <!-- Activity themes -->
-
-    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-    <style name="Theme.Sample" parent="Theme.Base" />
-
-    <style name="AppTheme" parent="Theme.Sample" />
-    <!-- Widget styling -->
-
-    <style name="Widget" />
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceMedium</item>
-        <item name="android:lineSpacingMultiplier">1.1</item>
-    </style>
-
-    <style name="Widget.SampleMessageTile">
-        <item name="android:background">@drawable/tile</item>
-        <item name="android:shadowColor">#7F000000</item>
-        <item name="android:shadowDy">-3.5</item>
-        <item name="android:shadowRadius">2</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/DoneBar/res/values/dimens.xml b/samples/browseable/TextSwitcher/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/DoneBar/res/values/dimens.xml
copy to samples/browseable/TextSwitcher/res/values/template-dimens.xml
diff --git a/samples/browseable/TextSwitcher/res/values/template-styles.xml b/samples/browseable/TextSwitcher/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/samples/browseable/TextSwitcher/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/samples/browseable/repeatingAlarm/_index.jd b/samples/browseable/repeatingAlarm/_index.jd
deleted file mode 100644
index bd77d6c..0000000
--- a/samples/browseable/repeatingAlarm/_index.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-page.tags="RepeatingAlarm"
-sample.group=Background
-@jd:body
-
-<p>This sample demonstrates how to implement a repeating alarm using an
-{@link android.app.AlarmManager}.</p>
diff --git a/samples/browseable/repeatingAlarm/res/layout/activity_main.xml b/samples/browseable/repeatingAlarm/res/layout/activity_main.xml
deleted file mode 100755
index bc5a575..0000000
--- a/samples/browseable/repeatingAlarm/res/layout/activity_main.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
-  Copyright 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:orientation="vertical"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:id="@+id/sample_main_layout">
-    <TextView android:id="@+id/sample_output"
-              style="@style/Widget.SampleMessage"
-              android:layout_weight="1"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:text="@string/intro_message" />
-    <View
-            android:layout_width="fill_parent"
-            android:layout_height="1dp"
-            android:background="@android:color/darker_gray"/>
-    <fragment
-            android:name="com.example.android.common.logger.LogFragment"
-            android:id="@+id/log_fragment"
-            android:layout_weight="1"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" />
-</LinearLayout>
diff --git a/samples/browseable/repeatingAlarm/res/values-sw600dp/styles.xml b/samples/browseable/repeatingAlarm/res/values-sw600dp/styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/repeatingAlarm/res/values-sw600dp/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  Copyright 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>
-
-    <style name="Widget.SampleMessage">
-        <item name="android:textAppearance">?android:textAppearanceLarge</item>
-        <item name="android:lineSpacingMultiplier">1.2</item>
-        <item name="android:shadowDy">-6.5</item>
-    </style>
-
-</resources>
diff --git a/samples/browseable/repeatingAlarm/res/values/base-strings.xml b/samples/browseable/repeatingAlarm/res/values/base-strings.xml
deleted file mode 100644
index c11b89b..0000000
--- a/samples/browseable/repeatingAlarm/res/values/base-strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 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">repeatingAlarm</string>
-    <string name="intro_message">
-        <![CDATA[
-        
-            
-                Introductory text that explains what the sample is intended to demonstrate. Edit
-                in template-params.xml.
-            
-        
-        ]]>
-    </string>
-</resources>
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java b/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java
deleted file mode 100644
index 2d2a6aa..0000000
--- a/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-* Copyright 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.repeatingalarm;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentTransaction;
-import android.view.Menu;
-
-import com.example.android.common.activities.SampleActivityBase;
-import com.example.android.common.logger.Log;
-import com.example.android.common.logger.LogFragment;
-import com.example.android.common.logger.LogWrapper;
-import com.example.android.common.logger.MessageOnlyLogFilter;
-
-/**
- * A simple launcher activity containing a summary sample description
- * and a few action bar buttons.
- */
-public class MainActivity extends SampleActivityBase {
-
-    public static final String TAG = "MainActivity";
-
-    public static final String FRAGTAG = "RepeatingAlarmFragment";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-
-        if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
-            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-            RepeatingAlarmFragment fragment = new RepeatingAlarmFragment();
-            transaction.add(fragment, FRAGTAG);
-            transaction.commit();
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.main, menu);
-        return true;
-    }
-
-    /** Create a chain of targets that will receive log data */
-    @Override
-    public void initializeLogging() {
-        // Wraps Android's native log framework.
-        LogWrapper logWrapper = new LogWrapper();
-        // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
-        Log.setLogNode(logWrapper);
-
-        // Filter strips out everything except the message text.
-        MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
-        logWrapper.setNext(msgFilter);
-
-        // On screen logging via a fragment with a TextView.
-        LogFragment logFragment = (LogFragment) getSupportFragmentManager()
-                .findFragmentById(R.id.log_fragment);
-        msgFilter.setNext(logFragment.getLogView());
-
-        Log.i(TAG, "Ready");
-    }
-}
diff --git a/samples/samples_source.prop_template b/samples/samples_source.prop_template
index d3cdfd5..1524e33 100644
--- a/samples/samples_source.prop_template
+++ b/samples/samples_source.prop_template
@@ -1,4 +1,4 @@
 Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=5
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
diff --git a/samples/training/bitmapfun/BitmapFun/build.gradle b/samples/training/bitmapfun/BitmapFun/build.gradle
deleted file mode 100644
index c978231..0000000
--- a/samples/training/bitmapfun/BitmapFun/build.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-buildscript {
-    repositories {
-        mavenCentral()
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:0.6.+'
-    }
-}
-apply plugin: 'android'
-
-repositories {
-    mavenCentral()
-}
-
-android {
-    compileSdkVersion 19
-    buildToolsVersion "19.0.0"
-
-    defaultConfig {
-        minSdkVersion 7
-        targetSdkVersion 19
-    }
-}
-
-dependencies {
-    compile 'com.android.support:support-v4:19.0.+'
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml b/samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml
deleted file mode 100644
index 9ca5cf5..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.bitmapfun"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-sdk
-        android:minSdkVersion="7"
-        android:targetSdkVersion="19" />
-
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
-    <application
-        android:description="@string/app_description"
-        android:hardwareAccelerated="true"
-        android:icon="@drawable/ic_launcher"
-        android:label="@string/app_name"
-        android:allowBackup="false">
-        <activity
-            android:name=".ui.ImageDetailActivity"
-            android:label="@string/app_name"
-            android:parentActivityName=".ui.ImageGridActivity"
-            android:theme="@style/AppTheme.FullScreen" >
-            <meta-data android:name="android.support.PARENT_ACTIVITY"
-                       android:value=".ui.ImageGridActivity" />
-        </activity>
-        <activity
-            android:name=".ui.ImageGridActivity"
-            android:label="@string/app_name"
-            android:theme="@style/AppTheme" >
-            <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/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java
deleted file mode 100644
index fcf4496..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.provider;
-
-/**
- * Some simple test data to use for this sample app.
- */
-public class Images {
-
-    /**
-     * This are PicasaWeb URLs and could potentially change. Ideally the PicasaWeb API should be
-     * used to fetch the URLs.
-     *
-     * Credit to Romain Guy for the photos:
-     * http://www.curious-creature.org/
-     * https://plus.google.com/109538161516040592207/about
-     * http://www.flickr.com/photos/romainguy
-     */
-    public final static String[] imageUrls = new String[] {
-            "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg",
-            "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s1024/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
-            "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s1024/Another%252520Rockaway%252520Sunset.jpg",
-            "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s1024/Antelope%252520Butte.jpg",
-            "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s1024/Antelope%252520Hallway.jpg",
-            "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s1024/Antelope%252520Walls.jpg",
-            "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s1024/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
-            "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s1024/Backlit%252520Cloud.jpg",
-            "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s1024/Bee%252520and%252520Flower.jpg",
-            "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s1024/Bonzai%252520Rock%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s1024/Caterpillar.jpg",
-            "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s1024/Chess.jpg",
-            "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s1024/Chihuly.jpg",
-            "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s1024/Closed%252520Door.jpg",
-            "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s1024/Colorado%252520River%252520Sunset.jpg",
-            "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s1024/Colors%252520of%252520Autumn.jpg",
-            "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s1024/Countryside.jpg",
-            "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s1024/Death%252520Valley%252520-%252520Dunes.jpg",
-            "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s1024/Delicate%252520Arch.jpg",
-            "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s1024/Despair.jpg",
-            "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s1024/Eagle%252520Fall%252520Sunrise.jpg",
-            "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s1024/Electric%252520Storm.jpg",
-            "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s1024/False%252520Kiva.jpg",
-            "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s1024/Fitzgerald%252520Streaks.jpg",
-            "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s1024/Foggy%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s1024/Frantic.jpg",
-            "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s1024/Golden%252520Gate%252520Afternoon.jpg",
-            "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s1024/Golden%252520Gate%252520Fog.jpg",
-            "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s1024/Golden%252520Grass.jpg",
-            "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s1024/Grand%252520Teton.jpg",
-            "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s1024/Grass%252520Closeup.jpg",
-            "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s1024/Green%252520Grass.jpg",
-            "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s1024/Hanging%252520Leaf.jpg",
-            "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s1024/Highway%2525201.jpg",
-            "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s1024/Horseshoe%252520Bend%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s1024/Horseshoe%252520Bend.jpg",
-            "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s1024/Into%252520the%252520Blue.jpg",
-            "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s1024/Jelly%252520Fish%2525202.jpg",
-            "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s1024/Jelly%252520Fish%2525203.jpg",
-            "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s1024/Kauai.jpg",
-            "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s1024/Kyoto%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s1024/Lake%252520Tahoe%252520Colors.jpg",
-            "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s1024/Lava%252520from%252520the%252520Sky.jpg",
-            "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s1024/Leica%25252050mm%252520Summilux.jpg",
-            "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s1024/Leica%25252050mm%252520Summilux.jpg",
-            "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s1024/Leica%252520M8%252520%252528Front%252529.jpg",
-            "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s1024/Light%252520to%252520Sand.jpg",
-            "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s1024/Little%252520Bit%252520of%252520Paradise.jpg",
-            "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s1024/Lone%252520Pine%252520Sunset.jpg",
-            "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s1024/Lonely%252520Rock.jpg",
-            "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s1024/Longue%252520Vue.jpg",
-            "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s1024/Look%252520Me%252520in%252520the%252520Eye.jpg",
-            "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s1024/Lost%252520in%252520a%252520Field.jpg",
-            "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s1024/Marshall%252520Beach%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s1024/Mono%252520Lake%252520Blue.jpg",
-            "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s1024/Monument%252520Valley%252520Overlook.jpg",
-            "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s1024/Moving%252520Rock.jpg",
-            "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s1024/Napali%252520Coast.jpg",
-            "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s1024/One%252520Wheel.jpg",
-            "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s1024/Open%252520Sky.jpg",
-            "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s1024/Orange%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s1024/Orchid.jpg",
-            "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s1024/Over%252520there.jpg",
-            "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s1024/Plumes.jpg",
-            "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s1024/Rainbokeh.jpg",
-            "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s1024/Rainbow.jpg",
-            "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s1024/Rice%252520Fields.jpg",
-            "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s1024/Rockaway%252520Fire%252520Sky.jpg",
-            "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s1024/Rockaway%252520Flow.jpg",
-            "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s1024/Rockaway%252520Sunset%252520Sky.jpg",
-            "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s1024/Russian%252520Ridge%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s1024/Rust%252520Knot.jpg",
-            "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s1024/Sailing%252520Stones.jpg",
-            "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s1024/Seahorse.jpg",
-            "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s1024/Shinjuku%252520Street.jpg",
-            "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s1024/Sierra%252520Heavens.jpg",
-            "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s1024/Sierra%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s1024/Sin%252520Lights.jpg",
-            "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s1024/Starry%252520Lake.jpg",
-            "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s1024/Starry%252520Night.jpg",
-            "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s1024/Stream.jpg",
-            "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s1024/Strip%252520Sunset.jpg",
-            "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s1024/Sunset%252520Hills.jpg",
-            "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s1024/Tenaya%252520Lake%2525202.jpg",
-            "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s1024/Tenaya%252520Lake.jpg",
-            "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s1024/The%252520Cave%252520BW.jpg",
-            "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s1024/The%252520Fisherman.jpg",
-            "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s1024/The%252520Night%252520is%252520Coming.jpg",
-            "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s1024/The%252520Road.jpg",
-            "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s1024/Tokyo%252520Heights.jpg",
-            "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s1024/Tokyo%252520Highway.jpg",
-            "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s1024/Tokyo%252520Smog.jpg",
-            "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s1024/Tufa%252520at%252520Night.jpg",
-            "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s1024/Valley%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s1024/Windmill%252520Sunrise.jpg",
-            "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s1024/Windmill.jpg",
-            "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s1024/Windmills.jpg",
-            "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s1024/Yet%252520Another%252520Rockaway%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s1024/Yosemite%252520Tree.jpg",
-    };
-
-    /**
-     * This are PicasaWeb thumbnail URLs and could potentially change. Ideally the PicasaWeb API
-     * should be used to fetch the URLs.
-     *
-     * Credit to Romain Guy for the photos:
-     * http://www.curious-creature.org/
-     * https://plus.google.com/109538161516040592207/about
-     * http://www.flickr.com/photos/romainguy
-     */
-    public final static String[] imageThumbUrls = new String[] {
-            "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s240-c/A%252520Photographer.jpg",
-            "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s240-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
-            "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s240-c/Another%252520Rockaway%252520Sunset.jpg",
-            "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s240-c/Antelope%252520Butte.jpg",
-            "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s240-c/Antelope%252520Hallway.jpg",
-            "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s240-c/Antelope%252520Walls.jpg",
-            "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s240-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
-            "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s240-c/Backlit%252520Cloud.jpg",
-            "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s240-c/Bee%252520and%252520Flower.jpg",
-            "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s240-c/Bonzai%252520Rock%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s240-c/Caterpillar.jpg",
-            "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s240-c/Chess.jpg",
-            "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s240-c/Chihuly.jpg",
-            "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s240-c/Closed%252520Door.jpg",
-            "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s240-c/Colorado%252520River%252520Sunset.jpg",
-            "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s240-c/Colors%252520of%252520Autumn.jpg",
-            "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s240-c/Countryside.jpg",
-            "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s240-c/Death%252520Valley%252520-%252520Dunes.jpg",
-            "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s240-c/Delicate%252520Arch.jpg",
-            "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s240-c/Despair.jpg",
-            "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s240-c/Eagle%252520Fall%252520Sunrise.jpg",
-            "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s240-c/Electric%252520Storm.jpg",
-            "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s240-c/False%252520Kiva.jpg",
-            "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s240-c/Fitzgerald%252520Streaks.jpg",
-            "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s240-c/Foggy%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s240-c/Frantic.jpg",
-            "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s240-c/Golden%252520Gate%252520Afternoon.jpg",
-            "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s240-c/Golden%252520Gate%252520Fog.jpg",
-            "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s240-c/Golden%252520Grass.jpg",
-            "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s240-c/Grand%252520Teton.jpg",
-            "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s240-c/Grass%252520Closeup.jpg",
-            "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s240-c/Green%252520Grass.jpg",
-            "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s240-c/Hanging%252520Leaf.jpg",
-            "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s240-c/Highway%2525201.jpg",
-            "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s240-c/Horseshoe%252520Bend%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s240-c/Horseshoe%252520Bend.jpg",
-            "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s240-c/Into%252520the%252520Blue.jpg",
-            "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s240-c/Jelly%252520Fish%2525202.jpg",
-            "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s240-c/Jelly%252520Fish%2525203.jpg",
-            "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s240-c/Kauai.jpg",
-            "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s240-c/Kyoto%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s240-c/Lake%252520Tahoe%252520Colors.jpg",
-            "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s240-c/Lava%252520from%252520the%252520Sky.jpg",
-            "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s240-c/Leica%25252050mm%252520Summilux.jpg",
-            "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s240-c/Leica%25252050mm%252520Summilux.jpg",
-            "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s240-c/Leica%252520M8%252520%252528Front%252529.jpg",
-            "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s240-c/Light%252520to%252520Sand.jpg",
-            "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s240-c/Little%252520Bit%252520of%252520Paradise.jpg",
-            "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s240-c/Lone%252520Pine%252520Sunset.jpg",
-            "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s240-c/Lonely%252520Rock.jpg",
-            "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s240-c/Longue%252520Vue.jpg",
-            "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s240-c/Look%252520Me%252520in%252520the%252520Eye.jpg",
-            "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s240-c/Lost%252520in%252520a%252520Field.jpg",
-            "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s240-c/Marshall%252520Beach%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s240-c/Mono%252520Lake%252520Blue.jpg",
-            "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s240-c/Monument%252520Valley%252520Overlook.jpg",
-            "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s240-c/Moving%252520Rock.jpg",
-            "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s240-c/Napali%252520Coast.jpg",
-            "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s240-c/One%252520Wheel.jpg",
-            "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s240-c/Open%252520Sky.jpg",
-            "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s240-c/Orange%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s240-c/Orchid.jpg",
-            "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s240-c/Over%252520there.jpg",
-            "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s240-c/Plumes.jpg",
-            "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s240-c/Rainbokeh.jpg",
-            "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s240-c/Rainbow.jpg",
-            "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s240-c/Rice%252520Fields.jpg",
-            "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s240-c/Rockaway%252520Fire%252520Sky.jpg",
-            "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s240-c/Rockaway%252520Flow.jpg",
-            "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s240-c/Rockaway%252520Sunset%252520Sky.jpg",
-            "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s240-c/Russian%252520Ridge%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s240-c/Rust%252520Knot.jpg",
-            "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s240-c/Sailing%252520Stones.jpg",
-            "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s240-c/Seahorse.jpg",
-            "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s240-c/Shinjuku%252520Street.jpg",
-            "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s240-c/Sierra%252520Heavens.jpg",
-            "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s240-c/Sierra%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s240-c/Sin%252520Lights.jpg",
-            "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s240-c/Starry%252520Lake.jpg",
-            "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s240-c/Starry%252520Night.jpg",
-            "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s240-c/Stream.jpg",
-            "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s240-c/Strip%252520Sunset.jpg",
-            "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s240-c/Sunset%252520Hills.jpg",
-            "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s240-c/Tenaya%252520Lake%2525202.jpg",
-            "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s240-c/Tenaya%252520Lake.jpg",
-            "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s240-c/The%252520Cave%252520BW.jpg",
-            "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s240-c/The%252520Fisherman.jpg",
-            "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s240-c/The%252520Night%252520is%252520Coming.jpg",
-            "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s240-c/The%252520Road.jpg",
-            "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s240-c/Tokyo%252520Heights.jpg",
-            "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s240-c/Tokyo%252520Highway.jpg",
-            "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s240-c/Tokyo%252520Smog.jpg",
-            "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s240-c/Tufa%252520at%252520Night.jpg",
-            "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s240-c/Valley%252520Sunset.jpg",
-            "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s240-c/Windmill%252520Sunrise.jpg",
-            "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s240-c/Windmill.jpg",
-            "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s240-c/Windmills.jpg",
-            "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s240-c/Yet%252520Another%252520Rockaway%252520Sunset.jpg",
-            "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s240-c/Yosemite%252520Tree.jpg",
-    };
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java
deleted file mode 100644
index 05d7b03..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.ui;
-
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.support.v4.app.NavUtils;
-import android.support.v4.view.ViewPager;
-import android.util.DisplayMetrics;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.WindowManager.LayoutParams;
-import android.widget.Toast;
-
-import com.example.android.bitmapfun.BuildConfig;
-import com.example.android.bitmapfun.R;
-import com.example.android.bitmapfun.provider.Images;
-import com.example.android.bitmapfun.util.ImageCache;
-import com.example.android.bitmapfun.util.ImageFetcher;
-import com.example.android.bitmapfun.util.Utils;
-
-public class ImageDetailActivity extends FragmentActivity implements OnClickListener {
-    private static final String IMAGE_CACHE_DIR = "images";
-    public static final String EXTRA_IMAGE = "extra_image";
-
-    private ImagePagerAdapter mAdapter;
-    private ImageFetcher mImageFetcher;
-    private ViewPager mPager;
-
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        if (BuildConfig.DEBUG) {
-            Utils.enableStrictMode();
-        }
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.image_detail_pager);
-
-        // Fetch screen height and width, to use as our max size when loading images as this
-        // activity runs full screen
-        final DisplayMetrics displayMetrics = new DisplayMetrics();
-        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
-        final int height = displayMetrics.heightPixels;
-        final int width = displayMetrics.widthPixels;
-
-        // For this sample we'll use half of the longest width to resize our images. As the
-        // image scaling ensures the image is larger than this, we should be left with a
-        // resolution that is appropriate for both portrait and landscape. For best image quality
-        // we shouldn't divide by 2, but this will use more memory and require a larger memory
-        // cache.
-        final int longest = (height > width ? height : width) / 2;
-
-        ImageCache.ImageCacheParams cacheParams =
-                new ImageCache.ImageCacheParams(this, IMAGE_CACHE_DIR);
-        cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory
-
-        // The ImageFetcher takes care of loading images into our ImageView children asynchronously
-        mImageFetcher = new ImageFetcher(this, longest);
-        mImageFetcher.addImageCache(getSupportFragmentManager(), cacheParams);
-        mImageFetcher.setImageFadeIn(false);
-
-        // Set up ViewPager and backing adapter
-        mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), Images.imageUrls.length);
-        mPager = (ViewPager) findViewById(R.id.pager);
-        mPager.setAdapter(mAdapter);
-        mPager.setPageMargin((int) getResources().getDimension(R.dimen.image_detail_pager_margin));
-        mPager.setOffscreenPageLimit(2);
-
-        // Set up activity to go full screen
-        getWindow().addFlags(LayoutParams.FLAG_FULLSCREEN);
-
-        // Enable some additional newer visibility and ActionBar features to create a more
-        // immersive photo viewing experience
-        if (Utils.hasHoneycomb()) {
-            final ActionBar actionBar = getActionBar();
-
-            // Hide title text and set home as up
-            actionBar.setDisplayShowTitleEnabled(false);
-            actionBar.setDisplayHomeAsUpEnabled(true);
-
-            // Hide and show the ActionBar as the visibility changes
-            mPager.setOnSystemUiVisibilityChangeListener(
-                    new View.OnSystemUiVisibilityChangeListener() {
-                        @Override
-                        public void onSystemUiVisibilityChange(int vis) {
-                            if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
-                                actionBar.hide();
-                            } else {
-                                actionBar.show();
-                            }
-                        }
-                    });
-
-            // Start low profile mode and hide ActionBar
-            mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
-            actionBar.hide();
-        }
-
-        // Set the current item based on the extra passed in to this activity
-        final int extraCurrentItem = getIntent().getIntExtra(EXTRA_IMAGE, -1);
-        if (extraCurrentItem != -1) {
-            mPager.setCurrentItem(extraCurrentItem);
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mImageFetcher.setExitTasksEarly(false);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mImageFetcher.setExitTasksEarly(true);
-        mImageFetcher.flushCache();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mImageFetcher.closeCache();
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                NavUtils.navigateUpFromSameTask(this);
-                return true;
-            case R.id.clear_cache:
-                mImageFetcher.clearCache();
-                Toast.makeText(
-                        this, R.string.clear_cache_complete_toast,Toast.LENGTH_SHORT).show();
-                return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.main_menu, menu);
-        return true;
-    }
-
-    /**
-     * Called by the ViewPager child fragments to load images via the one ImageFetcher
-     */
-    public ImageFetcher getImageFetcher() {
-        return mImageFetcher;
-    }
-
-    /**
-     * The main adapter that backs the ViewPager. A subclass of FragmentStatePagerAdapter as there
-     * could be a large number of items in the ViewPager and we don't want to retain them all in
-     * memory at once but create/destroy them on the fly.
-     */
-    private class ImagePagerAdapter extends FragmentStatePagerAdapter {
-        private final int mSize;
-
-        public ImagePagerAdapter(FragmentManager fm, int size) {
-            super(fm);
-            mSize = size;
-        }
-
-        @Override
-        public int getCount() {
-            return mSize;
-        }
-
-        @Override
-        public Fragment getItem(int position) {
-            return ImageDetailFragment.newInstance(Images.imageUrls[position]);
-        }
-    }
-
-    /**
-     * Set on the ImageView in the ViewPager children fragments, to enable/disable low profile mode
-     * when the ImageView is touched.
-     */
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    @Override
-    public void onClick(View v) {
-        final int vis = mPager.getSystemUiVisibility();
-        if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
-            mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
-        } else {
-            mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
-        }
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java
deleted file mode 100644
index 9fff8a0..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.ui;
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.example.android.bitmapfun.R;
-import com.example.android.bitmapfun.util.ImageFetcher;
-import com.example.android.bitmapfun.util.ImageWorker;
-import com.example.android.bitmapfun.util.Utils;
-
-/**
- * This fragment will populate the children of the ViewPager from {@link ImageDetailActivity}.
- */
-public class ImageDetailFragment extends Fragment {
-    private static final String IMAGE_DATA_EXTRA = "extra_image_data";
-    private String mImageUrl;
-    private ImageView mImageView;
-    private ImageFetcher mImageFetcher;
-
-    /**
-     * Factory method to generate a new instance of the fragment given an image number.
-     *
-     * @param imageUrl The image url to load
-     * @return A new instance of ImageDetailFragment with imageNum extras
-     */
-    public static ImageDetailFragment newInstance(String imageUrl) {
-        final ImageDetailFragment f = new ImageDetailFragment();
-
-        final Bundle args = new Bundle();
-        args.putString(IMAGE_DATA_EXTRA, imageUrl);
-        f.setArguments(args);
-
-        return f;
-    }
-
-    /**
-     * Empty constructor as per the Fragment documentation
-     */
-    public ImageDetailFragment() {}
-
-    /**
-     * Populate image using a url from extras, use the convenience factory method
-     * {@link ImageDetailFragment#newInstance(String)} to create this fragment.
-     */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mImageUrl = getArguments() != null ? getArguments().getString(IMAGE_DATA_EXTRA) : null;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        // Inflate and locate the main ImageView
-        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
-        mImageView = (ImageView) v.findViewById(R.id.imageView);
-        return v;
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        // Use the parent activity to load the image asynchronously into the ImageView (so a single
-        // cache can be used over all pages in the ViewPager
-        if (ImageDetailActivity.class.isInstance(getActivity())) {
-            mImageFetcher = ((ImageDetailActivity) getActivity()).getImageFetcher();
-            mImageFetcher.loadImage(mImageUrl, mImageView);
-        }
-
-        // Pass clicks on the ImageView to the parent activity to handle
-        if (OnClickListener.class.isInstance(getActivity()) && Utils.hasHoneycomb()) {
-            mImageView.setOnClickListener((OnClickListener) getActivity());
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        if (mImageView != null) {
-            // Cancel any pending image work
-            ImageWorker.cancelWork(mImageView);
-            mImageView.setImageDrawable(null);
-        }
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java
deleted file mode 100644
index e7c7d1c..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.ui;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentTransaction;
-
-import com.example.android.bitmapfun.BuildConfig;
-import com.example.android.bitmapfun.util.Utils;
-
-/**
- * Simple FragmentActivity to hold the main {@link ImageGridFragment} and not much else.
- */
-public class ImageGridActivity extends FragmentActivity {
-    private static final String TAG = "ImageGridActivity";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        if (BuildConfig.DEBUG) {
-            Utils.enableStrictMode();
-        }
-        super.onCreate(savedInstanceState);
-
-        if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
-            final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-            ft.add(android.R.id.content, new ImageGridFragment(), TAG);
-            ft.commit();
-        }
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java
deleted file mode 100644
index ba4581a..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.ui;
-
-import android.annotation.TargetApi;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewTreeObserver;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import com.example.android.bitmapfun.BuildConfig;
-import com.example.android.bitmapfun.R;
-import com.example.android.bitmapfun.provider.Images;
-import com.example.android.bitmapfun.util.ImageCache.ImageCacheParams;
-import com.example.android.bitmapfun.util.ImageFetcher;
-import com.example.android.bitmapfun.util.Utils;
-
-/**
- * The main fragment that powers the ImageGridActivity screen. Fairly straight forward GridView
- * implementation with the key addition being the ImageWorker class w/ImageCache to load children
- * asynchronously, keeping the UI nice and smooth and caching thumbnails for quick retrieval. The
- * cache is retained over configuration changes like orientation change so the images are populated
- * quickly if, for example, the user rotates the device.
- */
-public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
-    private static final String TAG = "ImageGridFragment";
-    private static final String IMAGE_CACHE_DIR = "thumbs";
-
-    private int mImageThumbSize;
-    private int mImageThumbSpacing;
-    private ImageAdapter mAdapter;
-    private ImageFetcher mImageFetcher;
-
-    /**
-     * Empty constructor as per the Fragment documentation
-     */
-    public ImageGridFragment() {}
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setHasOptionsMenu(true);
-
-        mImageThumbSize = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
-        mImageThumbSpacing = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);
-
-        mAdapter = new ImageAdapter(getActivity());
-
-        ImageCacheParams cacheParams = new ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
-
-        cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory
-
-        // The ImageFetcher takes care of loading images into our ImageView children asynchronously
-        mImageFetcher = new ImageFetcher(getActivity(), mImageThumbSize);
-        mImageFetcher.setLoadingImage(R.drawable.empty_photo);
-        mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);
-    }
-
-    @Override
-    public View onCreateView(
-            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-
-        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
-        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
-        mGridView.setAdapter(mAdapter);
-        mGridView.setOnItemClickListener(this);
-        mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
-            @Override
-            public void onScrollStateChanged(AbsListView absListView, int scrollState) {
-                // Pause fetcher to ensure smoother scrolling when flinging
-                if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
-                    // Before Honeycomb pause image loading on scroll to help with performance
-                    if (!Utils.hasHoneycomb()) {
-                        mImageFetcher.setPauseWork(true);
-                    }
-                } else {
-                    mImageFetcher.setPauseWork(false);
-                }
-            }
-
-            @Override
-            public void onScroll(AbsListView absListView, int firstVisibleItem,
-                    int visibleItemCount, int totalItemCount) {
-            }
-        });
-
-        // This listener is used to get the final width of the GridView and then calculate the
-        // number of columns and the width of each column. The width of each column is variable
-        // as the GridView has stretchMode=columnWidth. The column width is used to set the height
-        // of each view so we get nice square thumbnails.
-        mGridView.getViewTreeObserver().addOnGlobalLayoutListener(
-                new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        if (mAdapter.getNumColumns() == 0) {
-                            final int numColumns = (int) Math.floor(
-                                    mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing));
-                            if (numColumns > 0) {
-                                final int columnWidth =
-                                        (mGridView.getWidth() / numColumns) - mImageThumbSpacing;
-                                mAdapter.setNumColumns(numColumns);
-                                mAdapter.setItemHeight(columnWidth);
-                                if (BuildConfig.DEBUG) {
-                                    Log.d(TAG, "onCreateView - numColumns set to " + numColumns);
-                                }
-                                if (Utils.hasJellyBean()) {
-                                    mGridView.getViewTreeObserver()
-                                            .removeOnGlobalLayoutListener(this);
-                                } else {
-                                    mGridView.getViewTreeObserver()
-                                            .removeGlobalOnLayoutListener(this);
-                                }
-                            }
-                        }
-                    }
-                });
-
-        return v;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mImageFetcher.setExitTasksEarly(false);
-        mAdapter.notifyDataSetChanged();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        mImageFetcher.setPauseWork(false);
-        mImageFetcher.setExitTasksEarly(true);
-        mImageFetcher.flushCache();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mImageFetcher.closeCache();
-    }
-
-    @TargetApi(VERSION_CODES.JELLY_BEAN)
-    @Override
-    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
-        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
-        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, (int) id);
-        if (Utils.hasJellyBean()) {
-            // makeThumbnailScaleUpAnimation() looks kind of ugly here as the loading spinner may
-            // show plus the thumbnail image in GridView is cropped. so using
-            // makeScaleUpAnimation() instead.
-            ActivityOptions options =
-                    ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getWidth(), v.getHeight());
-            getActivity().startActivity(i, options.toBundle());
-        } else {
-            startActivity(i);
-        }
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        inflater.inflate(R.menu.main_menu, menu);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.clear_cache:
-                mImageFetcher.clearCache();
-                Toast.makeText(getActivity(), R.string.clear_cache_complete_toast,
-                        Toast.LENGTH_SHORT).show();
-                return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    /**
-     * The main adapter that backs the GridView. This is fairly standard except the number of
-     * columns in the GridView is used to create a fake top row of empty views as we use a
-     * transparent ActionBar and don't want the real top row of images to start off covered by it.
-     */
-    private class ImageAdapter extends BaseAdapter {
-
-        private final Context mContext;
-        private int mItemHeight = 0;
-        private int mNumColumns = 0;
-        private int mActionBarHeight = 0;
-        private GridView.LayoutParams mImageViewLayoutParams;
-
-        public ImageAdapter(Context context) {
-            super();
-            mContext = context;
-            mImageViewLayoutParams = new GridView.LayoutParams(
-                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-            // Calculate ActionBar height
-            TypedValue tv = new TypedValue();
-            if (context.getTheme().resolveAttribute(
-                    android.R.attr.actionBarSize, tv, true)) {
-                mActionBarHeight = TypedValue.complexToDimensionPixelSize(
-                        tv.data, context.getResources().getDisplayMetrics());
-            }
-        }
-
-        @Override
-        public int getCount() {
-            // If columns have yet to be determined, return no items
-            if (getNumColumns() == 0) {
-                return 0;
-            }
-
-            // Size + number of columns for top empty row
-            return Images.imageThumbUrls.length + mNumColumns;
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return position < mNumColumns ?
-                    null : Images.imageThumbUrls[position - mNumColumns];
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position < mNumColumns ? 0 : position - mNumColumns;
-        }
-
-        @Override
-        public int getViewTypeCount() {
-            // Two types of views, the normal ImageView and the top row of empty views
-            return 2;
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            return (position < mNumColumns) ? 1 : 0;
-        }
-
-        @Override
-        public boolean hasStableIds() {
-            return true;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup container) {
-            // First check if this is the top row
-            if (position < mNumColumns) {
-                if (convertView == null) {
-                    convertView = new View(mContext);
-                }
-                // Set empty view with height of ActionBar
-                convertView.setLayoutParams(new AbsListView.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, mActionBarHeight));
-                return convertView;
-            }
-
-            // Now handle the main ImageView thumbnails
-            ImageView imageView;
-            if (convertView == null) { // if it's not recycled, instantiate and initialize
-                imageView = new RecyclingImageView(mContext);
-                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-                imageView.setLayoutParams(mImageViewLayoutParams);
-            } else { // Otherwise re-use the converted view
-                imageView = (ImageView) convertView;
-            }
-
-            // Check the height matches our calculated column width
-            if (imageView.getLayoutParams().height != mItemHeight) {
-                imageView.setLayoutParams(mImageViewLayoutParams);
-            }
-
-            // Finally load the image asynchronously into the ImageView, this also takes care of
-            // setting a placeholder image while the background thread runs
-            mImageFetcher.loadImage(Images.imageThumbUrls[position - mNumColumns], imageView);
-            return imageView;
-        }
-
-        /**
-         * Sets the item height. Useful for when we know the column width so the height can be set
-         * to match.
-         *
-         * @param height
-         */
-        public void setItemHeight(int height) {
-            if (height == mItemHeight) {
-                return;
-            }
-            mItemHeight = height;
-            mImageViewLayoutParams =
-                    new GridView.LayoutParams(LayoutParams.MATCH_PARENT, mItemHeight);
-            mImageFetcher.setImageSize(height);
-            notifyDataSetChanged();
-        }
-
-        public void setNumColumns(int numColumns) {
-            mNumColumns = numColumns;
-        }
-
-        public int getNumColumns() {
-            return mNumColumns;
-        }
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java
deleted file mode 100644
index 1bcc014..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.bitmapfun.ui;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-import com.example.android.bitmapfun.util.RecyclingBitmapDrawable;
-
-/**
- * Sub-class of ImageView which automatically notifies the drawable when it is
- * being displayed.
- */
-public class RecyclingImageView extends ImageView {
-
-    public RecyclingImageView(Context context) {
-        super(context);
-    }
-
-    public RecyclingImageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    /**
-     * @see android.widget.ImageView#onDetachedFromWindow()
-     */
-    @Override
-    protected void onDetachedFromWindow() {
-        // This has been detached from Window, so clear the drawable
-        setImageDrawable(null);
-
-        super.onDetachedFromWindow();
-    }
-
-    /**
-     * @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)
-     */
-    @Override
-    public void setImageDrawable(Drawable drawable) {
-        // Keep hold of previous Drawable
-        final Drawable previousDrawable = getDrawable();
-
-        // Call super to set new Drawable
-        super.setImageDrawable(drawable);
-
-        // Notify new Drawable that it is being displayed
-        notifyDrawable(drawable, true);
-
-        // Notify old Drawable so it is no longer being displayed
-        notifyDrawable(previousDrawable, false);
-    }
-
-    /**
-     * Notifies the drawable that it's displayed state has changed.
-     *
-     * @param drawable
-     * @param isDisplayed
-     */
-    private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
-        if (drawable instanceof RecyclingBitmapDrawable) {
-            // The drawable is a CountingBitmapDrawable, so notify it
-            ((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
-        } else if (drawable instanceof LayerDrawable) {
-            // The drawable is a LayerDrawable, so recurse on each layer
-            LayerDrawable layerDrawable = (LayerDrawable) drawable;
-            for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
-                notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
-            }
-        }
-    }
-
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java
deleted file mode 100644
index 018ce1a..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * Copyright (C) 2008 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.bitmapfun.util;
-
-import android.annotation.TargetApi;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Process;
-
-import java.util.ArrayDeque;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * *************************************
- * Copied from JB release framework:
- * https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/java/android/os/AsyncTask.java
- *
- * so that threading behavior on all OS versions is the same and we can tweak behavior by using
- * executeOnExecutor() if needed.
- *
- * There are 3 changes in this copy of AsyncTask:
- *    -pre-HC a single thread executor is used for serial operation
- *    (Executors.newSingleThreadExecutor) and is the default
- *    -the default THREAD_POOL_EXECUTOR was changed to use DiscardOldestPolicy
- *    -a new fixed thread pool called DUAL_THREAD_EXECUTOR was added
- * *************************************
- *
- * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
- * perform background operations and publish results on the UI thread without
- * having to manipulate threads and/or handlers.</p>
- *
- * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
- * and does not constitute a generic threading framework. AsyncTasks should ideally be
- * used for short operations (a few seconds at the most.) If you need to keep threads
- * running for long periods of time, it is highly recommended you use the various APIs
- * provided by the <code>java.util.concurrent</code> pacakge such as {@link Executor},
- * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
- *
- * <p>An asynchronous task is defined by a computation that runs on a background thread and
- * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
- * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
- * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
- * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using tasks and threads, read the
- * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
- * Threads</a> developer guide.</p>
- * </div>
- *
- * <h2>Usage</h2>
- * <p>AsyncTask must be subclassed to be used. The subclass will override at least
- * one method ({@link #doInBackground}), and most often will override a
- * second one ({@link #onPostExecute}.)</p>
- *
- * <p>Here is an example of subclassing:</p>
- * <pre class="prettyprint">
- * private class DownloadFilesTask extends AsyncTask&lt;URL, Integer, Long&gt; {
- *     protected Long doInBackground(URL... urls) {
- *         int count = urls.length;
- *         long totalSize = 0;
- *         for (int i = 0; i < count; i++) {
- *             totalSize += Downloader.downloadFile(urls[i]);
- *             publishProgress((int) ((i / (float) count) * 100));
- *             // Escape early if cancel() is called
- *             if (isCancelled()) break;
- *         }
- *         return totalSize;
- *     }
- *
- *     protected void onProgressUpdate(Integer... progress) {
- *         setProgressPercent(progress[0]);
- *     }
- *
- *     protected void onPostExecute(Long result) {
- *         showDialog("Downloaded " + result + " bytes");
- *     }
- * }
- * </pre>
- *
- * <p>Once created, a task is executed very simply:</p>
- * <pre class="prettyprint">
- * new DownloadFilesTask().execute(url1, url2, url3);
- * </pre>
- *
- * <h2>AsyncTask's generic types</h2>
- * <p>The three types used by an asynchronous task are the following:</p>
- * <ol>
- *     <li><code>Params</code>, the type of the parameters sent to the task upon
- *     execution.</li>
- *     <li><code>Progress</code>, the type of the progress units published during
- *     the background computation.</li>
- *     <li><code>Result</code>, the type of the result of the background
- *     computation.</li>
- * </ol>
- * <p>Not all types are always used by an asynchronous task. To mark a type as unused,
- * simply use the type {@link Void}:</p>
- * <pre>
- * private class MyTask extends AsyncTask&lt;Void, Void, Void&gt; { ... }
- * </pre>
- *
- * <h2>The 4 steps</h2>
- * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
- * <ol>
- *     <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
- *     is executed. This step is normally used to setup the task, for instance by
- *     showing a progress bar in the user interface.</li>
- *     <li>{@link #doInBackground}, invoked on the background thread
- *     immediately after {@link #onPreExecute()} finishes executing. This step is used
- *     to perform background computation that can take a long time. The parameters
- *     of the asynchronous task are passed to this step. The result of the computation must
- *     be returned by this step and will be passed back to the last step. This step
- *     can also use {@link #publishProgress} to publish one or more units
- *     of progress. These values are published on the UI thread, in the
- *     {@link #onProgressUpdate} step.</li>
- *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
- *     call to {@link #publishProgress}. The timing of the execution is
- *     undefined. This method is used to display any form of progress in the user
- *     interface while the background computation is still executing. For instance,
- *     it can be used to animate a progress bar or show logs in a text field.</li>
- *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
- *     computation finishes. The result of the background computation is passed to
- *     this step as a parameter.</li>
- * </ol>
- *
- * <h2>Cancelling a task</h2>
- * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
- * this method will cause subsequent calls to {@link #isCancelled()} to return true.
- * After invoking this method, {@link #onCancelled(Object)}, instead of
- * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
- * returns. To ensure that a task is cancelled as quickly as possible, you should always
- * check the return value of {@link #isCancelled()} periodically from
- * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
- *
- * <h2>Threading rules</h2>
- * <p>There are a few threading rules that must be followed for this class to
- * work properly:</p>
- * <ul>
- *     <li>The AsyncTask class must be loaded on the UI thread. This is done
- *     automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
- *     <li>The task instance must be created on the UI thread.</li>
- *     <li>{@link #execute} must be invoked on the UI thread.</li>
- *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
- *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
- *     <li>The task can be executed only once (an exception will be thrown if
- *     a second execution is attempted.)</li>
- * </ul>
- *
- * <h2>Memory observability</h2>
- * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
- * operations are safe without explicit synchronizations.</p>
- * <ul>
- *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
- *     in {@link #doInBackground}.
- *     <li>Set member fields in {@link #doInBackground}, and refer to them in
- *     {@link #onProgressUpdate} and {@link #onPostExecute}.
- * </ul>
- *
- * <h2>Order of execution</h2>
- * <p>When first introduced, AsyncTasks were executed serially on a single background
- * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
- * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
- * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
- * thread to avoid common application errors caused by parallel execution.</p>
- * <p>If you truly want parallel execution, you can invoke
- * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
- * {@link #THREAD_POOL_EXECUTOR}.</p>
- */
-public abstract class AsyncTask<Params, Progress, Result> {
-    private static final String LOG_TAG = "AsyncTask";
-
-    private static final int CORE_POOL_SIZE = 5;
-    private static final int MAXIMUM_POOL_SIZE = 128;
-    private static final int KEEP_ALIVE = 1;
-
-    private static final ThreadFactory  sThreadFactory = new ThreadFactory() {
-        private final AtomicInteger mCount = new AtomicInteger(1);
-
-        public Thread newThread(Runnable r) {
-            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
-        }
-    };
-
-    private static final BlockingQueue<Runnable> sPoolWorkQueue =
-            new LinkedBlockingQueue<Runnable>(10);
-
-    /**
-     * An {@link Executor} that can be used to execute tasks in parallel.
-     */
-    public static final Executor THREAD_POOL_EXECUTOR
-            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
-            TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory,
-            new ThreadPoolExecutor.DiscardOldestPolicy());
-
-    /**
-     * An {@link Executor} that executes tasks one at a time in serial
-     * order.  This serialization is global to a particular process.
-     */
-    public static final Executor SERIAL_EXECUTOR = Utils.hasHoneycomb() ? new SerialExecutor() :
-            Executors.newSingleThreadExecutor(sThreadFactory);
-
-    public static final Executor DUAL_THREAD_EXECUTOR =
-            Executors.newFixedThreadPool(2, sThreadFactory);
-
-    private static final int MESSAGE_POST_RESULT = 0x1;
-    private static final int MESSAGE_POST_PROGRESS = 0x2;
-
-    private static final InternalHandler sHandler = new InternalHandler();
-
-    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
-    private final WorkerRunnable<Params, Result> mWorker;
-    private final FutureTask<Result> mFuture;
-
-    private volatile Status mStatus = Status.PENDING;
-
-    private final AtomicBoolean mCancelled = new AtomicBoolean();
-    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
-
-    @TargetApi(11)
-    private static class SerialExecutor implements Executor {
-        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
-        Runnable mActive;
-
-        public synchronized void execute(final Runnable r) {
-            mTasks.offer(new Runnable() {
-                public void run() {
-                    try {
-                        r.run();
-                    } finally {
-                        scheduleNext();
-                    }
-                }
-            });
-            if (mActive == null) {
-                scheduleNext();
-            }
-        }
-
-        protected synchronized void scheduleNext() {
-            if ((mActive = mTasks.poll()) != null) {
-                THREAD_POOL_EXECUTOR.execute(mActive);
-            }
-        }
-    }
-
-    /**
-     * Indicates the current status of the task. Each status will be set only once
-     * during the lifetime of a task.
-     */
-    public enum Status {
-        /**
-         * Indicates that the task has not been executed yet.
-         */
-        PENDING,
-        /**
-         * Indicates that the task is running.
-         */
-        RUNNING,
-        /**
-         * Indicates that {@link AsyncTask#onPostExecute} has finished.
-         */
-        FINISHED,
-    }
-
-    /** @hide Used to force static handler to be created. */
-    public static void init() {
-        sHandler.getLooper();
-    }
-
-    /** @hide */
-    public static void setDefaultExecutor(Executor exec) {
-        sDefaultExecutor = exec;
-    }
-
-    /**
-     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
-     */
-    public AsyncTask() {
-        mWorker = new WorkerRunnable<Params, Result>() {
-            public Result call() throws Exception {
-                mTaskInvoked.set(true);
-
-                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                //noinspection unchecked
-                return postResult(doInBackground(mParams));
-            }
-        };
-
-        mFuture = new FutureTask<Result>(mWorker) {
-            @Override
-            protected void done() {
-                try {
-                    postResultIfNotInvoked(get());
-                } catch (InterruptedException e) {
-                    android.util.Log.w(LOG_TAG, e);
-                } catch (ExecutionException e) {
-                    throw new RuntimeException("An error occured while executing doInBackground()",
-                            e.getCause());
-                } catch (CancellationException e) {
-                    postResultIfNotInvoked(null);
-                }
-            }
-        };
-    }
-
-    private void postResultIfNotInvoked(Result result) {
-        final boolean wasTaskInvoked = mTaskInvoked.get();
-        if (!wasTaskInvoked) {
-            postResult(result);
-        }
-    }
-
-    private Result postResult(Result result) {
-        @SuppressWarnings("unchecked")
-        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
-                new AsyncTaskResult<Result>(this, result));
-        message.sendToTarget();
-        return result;
-    }
-
-    /**
-     * Returns the current status of this task.
-     *
-     * @return The current status.
-     */
-    public final Status getStatus() {
-        return mStatus;
-    }
-
-    /**
-     * Override this method to perform a computation on a background thread. The
-     * specified parameters are the parameters passed to {@link #execute}
-     * by the caller of this task.
-     *
-     * This method can call {@link #publishProgress} to publish updates
-     * on the UI thread.
-     *
-     * @param params The parameters of the task.
-     *
-     * @return A result, defined by the subclass of this task.
-     *
-     * @see #onPreExecute()
-     * @see #onPostExecute
-     * @see #publishProgress
-     */
-    protected abstract Result doInBackground(Params... params);
-
-    /**
-     * Runs on the UI thread before {@link #doInBackground}.
-     *
-     * @see #onPostExecute
-     * @see #doInBackground
-     */
-    protected void onPreExecute() {
-    }
-
-    /**
-     * <p>Runs on the UI thread after {@link #doInBackground}. The
-     * specified result is the value returned by {@link #doInBackground}.</p>
-     *
-     * <p>This method won't be invoked if the task was cancelled.</p>
-     *
-     * @param result The result of the operation computed by {@link #doInBackground}.
-     *
-     * @see #onPreExecute
-     * @see #doInBackground
-     * @see #onCancelled(Object)
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    protected void onPostExecute(Result result) {
-    }
-
-    /**
-     * Runs on the UI thread after {@link #publishProgress} is invoked.
-     * The specified values are the values passed to {@link #publishProgress}.
-     *
-     * @param values The values indicating progress.
-     *
-     * @see #publishProgress
-     * @see #doInBackground
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    protected void onProgressUpdate(Progress... values) {
-    }
-
-    /**
-     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
-     * {@link #doInBackground(Object[])} has finished.</p>
-     *
-     * <p>The default implementation simply invokes {@link #onCancelled()} and
-     * ignores the result. If you write your own implementation, do not call
-     * <code>super.onCancelled(result)</code>.</p>
-     *
-     * @param result The result, if any, computed in
-     *               {@link #doInBackground(Object[])}, can be null
-     *
-     * @see #cancel(boolean)
-     * @see #isCancelled()
-     */
-    @SuppressWarnings({"UnusedParameters"})
-    protected void onCancelled(Result result) {
-        onCancelled();
-    }
-
-    /**
-     * <p>Applications should preferably override {@link #onCancelled(Object)}.
-     * This method is invoked by the default implementation of
-     * {@link #onCancelled(Object)}.</p>
-     *
-     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
-     * {@link #doInBackground(Object[])} has finished.</p>
-     *
-     * @see #onCancelled(Object)
-     * @see #cancel(boolean)
-     * @see #isCancelled()
-     */
-    protected void onCancelled() {
-    }
-
-    /**
-     * Returns <tt>true</tt> if this task was cancelled before it completed
-     * normally. If you are calling {@link #cancel(boolean)} on the task,
-     * the value returned by this method should be checked periodically from
-     * {@link #doInBackground(Object[])} to end the task as soon as possible.
-     *
-     * @return <tt>true</tt> if task was cancelled before it completed
-     *
-     * @see #cancel(boolean)
-     */
-    public final boolean isCancelled() {
-        return mCancelled.get();
-    }
-
-    /**
-     * <p>Attempts to cancel execution of this task.  This attempt will
-     * fail if the task has already completed, already been cancelled,
-     * or could not be cancelled for some other reason. If successful,
-     * and this task has not started when <tt>cancel</tt> is called,
-     * this task should never run. If the task has already started,
-     * then the <tt>mayInterruptIfRunning</tt> parameter determines
-     * whether the thread executing this task should be interrupted in
-     * an attempt to stop the task.</p>
-     *
-     * <p>Calling this method will result in {@link #onCancelled(Object)} being
-     * invoked on the UI thread after {@link #doInBackground(Object[])}
-     * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
-     * is never invoked. After invoking this method, you should check the
-     * value returned by {@link #isCancelled()} periodically from
-     * {@link #doInBackground(Object[])} to finish the task as early as
-     * possible.</p>
-     *
-     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
-     *        task should be interrupted; otherwise, in-progress tasks are allowed
-     *        to complete.
-     *
-     * @return <tt>false</tt> if the task could not be cancelled,
-     *         typically because it has already completed normally;
-     *         <tt>true</tt> otherwise
-     *
-     * @see #isCancelled()
-     * @see #onCancelled(Object)
-     */
-    public final boolean cancel(boolean mayInterruptIfRunning) {
-        mCancelled.set(true);
-        return mFuture.cancel(mayInterruptIfRunning);
-    }
-
-    /**
-     * Waits if necessary for the computation to complete, and then
-     * retrieves its result.
-     *
-     * @return The computed result.
-     *
-     * @throws CancellationException If the computation was cancelled.
-     * @throws ExecutionException If the computation threw an exception.
-     * @throws InterruptedException If the current thread was interrupted
-     *         while waiting.
-     */
-    public final Result get() throws InterruptedException, ExecutionException {
-        return mFuture.get();
-    }
-
-    /**
-     * Waits if necessary for at most the given time for the computation
-     * to complete, and then retrieves its result.
-     *
-     * @param timeout Time to wait before cancelling the operation.
-     * @param unit The time unit for the timeout.
-     *
-     * @return The computed result.
-     *
-     * @throws CancellationException If the computation was cancelled.
-     * @throws ExecutionException If the computation threw an exception.
-     * @throws InterruptedException If the current thread was interrupted
-     *         while waiting.
-     * @throws TimeoutException If the wait timed out.
-     */
-    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
-            ExecutionException, TimeoutException {
-        return mFuture.get(timeout, unit);
-    }
-
-    /**
-     * Executes the task with the specified parameters. The task returns
-     * itself (this) so that the caller can keep a reference to it.
-     *
-     * <p>Note: this function schedules the task on a queue for a single background
-     * thread or pool of threads depending on the platform version.  When first
-     * introduced, AsyncTasks were executed serially on a single background thread.
-     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
-     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
-     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
-     * executed on a single thread to avoid common application errors caused
-     * by parallel execution.  If you truly want parallel execution, you can use
-     * the {@link #executeOnExecutor} version of this method
-     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
-     * on its use.
-     *
-     * <p>This method must be invoked on the UI thread.
-     *
-     * @param params The parameters of the task.
-     *
-     * @return This instance of AsyncTask.
-     *
-     * @throws IllegalStateException If {@link #getStatus()} returns either
-     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
-     *
-     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
-     * @see #execute(Runnable)
-     */
-    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
-        return executeOnExecutor(sDefaultExecutor, params);
-    }
-
-    /**
-     * Executes the task with the specified parameters. The task returns
-     * itself (this) so that the caller can keep a reference to it.
-     *
-     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
-     * allow multiple tasks to run in parallel on a pool of threads managed by
-     * AsyncTask, however you can also use your own {@link Executor} for custom
-     * behavior.
-     *
-     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
-     * a thread pool is generally <em>not</em> what one wants, because the order
-     * of their operation is not defined.  For example, if these tasks are used
-     * to modify any state in common (such as writing a file due to a button click),
-     * there are no guarantees on the order of the modifications.
-     * Without careful work it is possible in rare cases for the newer version
-     * of the data to be over-written by an older one, leading to obscure data
-     * loss and stability issues.  Such changes are best
-     * executed in serial; to guarantee such work is serialized regardless of
-     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
-     *
-     * <p>This method must be invoked on the UI thread.
-     *
-     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
-     *              convenient process-wide thread pool for tasks that are loosely coupled.
-     * @param params The parameters of the task.
-     *
-     * @return This instance of AsyncTask.
-     *
-     * @throws IllegalStateException If {@link #getStatus()} returns either
-     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
-     *
-     * @see #execute(Object[])
-     */
-    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
-            Params... params) {
-        if (mStatus != Status.PENDING) {
-            switch (mStatus) {
-                case RUNNING:
-                    throw new IllegalStateException("Cannot execute task:"
-                            + " the task is already running.");
-                case FINISHED:
-                    throw new IllegalStateException("Cannot execute task:"
-                            + " the task has already been executed "
-                            + "(a task can be executed only once)");
-            }
-        }
-
-        mStatus = Status.RUNNING;
-
-        onPreExecute();
-
-        mWorker.mParams = params;
-        exec.execute(mFuture);
-
-        return this;
-    }
-
-    /**
-     * Convenience version of {@link #execute(Object...)} for use with
-     * a simple Runnable object. See {@link #execute(Object[])} for more
-     * information on the order of execution.
-     *
-     * @see #execute(Object[])
-     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
-     */
-    public static void execute(Runnable runnable) {
-        sDefaultExecutor.execute(runnable);
-    }
-
-    /**
-     * This method can be invoked from {@link #doInBackground} to
-     * publish updates on the UI thread while the background computation is
-     * still running. Each call to this method will trigger the execution of
-     * {@link #onProgressUpdate} on the UI thread.
-     *
-     * {@link #onProgressUpdate} will note be called if the task has been
-     * canceled.
-     *
-     * @param values The progress values to update the UI with.
-     *
-     * @see #onProgressUpdate
-     * @see #doInBackground
-     */
-    protected final void publishProgress(Progress... values) {
-        if (!isCancelled()) {
-            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
-                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
-        }
-    }
-
-    private void finish(Result result) {
-        if (isCancelled()) {
-            onCancelled(result);
-        } else {
-            onPostExecute(result);
-        }
-        mStatus = Status.FINISHED;
-    }
-
-    private static class InternalHandler extends Handler {
-        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
-            switch (msg.what) {
-                case MESSAGE_POST_RESULT:
-                    // There is only one result
-                    result.mTask.finish(result.mData[0]);
-                    break;
-                case MESSAGE_POST_PROGRESS:
-                    result.mTask.onProgressUpdate(result.mData);
-                    break;
-            }
-        }
-    }
-
-    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
-        Params[] mParams;
-    }
-
-    @SuppressWarnings({"RawUseOfParameterizedType"})
-    private static class AsyncTaskResult<Data> {
-        final AsyncTask mTask;
-        final Data[] mData;
-
-        AsyncTaskResult(AsyncTask task, Data... data) {
-            mTask = task;
-            mData = data;
-        }
-    }
-}
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java
deleted file mode 100644
index 26cdbd7..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * Copyright (C) 2011 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.bitmapfun.util;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedWriter;
-import java.io.Closeable;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.lang.reflect.Array;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- ******************************************************************************
- * Taken from the JB source code, can be found in:
- * libcore/luni/src/main/java/libcore/io/DiskLruCache.java
- * or direct link:
- * https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
- ******************************************************************************
- *
- * A cache that uses a bounded amount of space on a filesystem. Each cache
- * entry has a string key and a fixed number of values. Values are byte
- * sequences, accessible as streams or files. Each value must be between {@code
- * 0} and {@code Integer.MAX_VALUE} bytes in length.
- *
- * <p>The cache stores its data in a directory on the filesystem. This
- * directory must be exclusive to the cache; the cache may delete or overwrite
- * files from its directory. It is an error for multiple processes to use the
- * same cache directory at the same time.
- *
- * <p>This cache limits the number of bytes that it will store on the
- * filesystem. When the number of stored bytes exceeds the limit, the cache will
- * remove entries in the background until the limit is satisfied. The limit is
- * not strict: the cache may temporarily exceed it while waiting for files to be
- * deleted. The limit does not include filesystem overhead or the cache
- * journal so space-sensitive applications should set a conservative limit.
- *
- * <p>Clients call {@link #edit} to create or update the values of an entry. An
- * entry may have only one editor at one time; if a value is not available to be
- * edited then {@link #edit} will return null.
- * <ul>
- *     <li>When an entry is being <strong>created</strong> it is necessary to
- *         supply a full set of values; the empty value should be used as a
- *         placeholder if necessary.
- *     <li>When an entry is being <strong>edited</strong>, it is not necessary
- *         to supply data for every value; values default to their previous
- *         value.
- * </ul>
- * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
- * or {@link Editor#abort}. Committing is atomic: a read observes the full set
- * of values as they were before or after the commit, but never a mix of values.
- *
- * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
- * observe the value at the time that {@link #get} was called. Updates and
- * removals after the call do not impact ongoing reads.
- *
- * <p>This class is tolerant of some I/O errors. If files are missing from the
- * filesystem, the corresponding entries will be dropped from the cache. If
- * an error occurs while writing a cache value, the edit will fail silently.
- * Callers should handle other problems by catching {@code IOException} and
- * responding appropriately.
- */
-public final class DiskLruCache implements Closeable {
-    static final String JOURNAL_FILE = "journal";
-    static final String JOURNAL_FILE_TMP = "journal.tmp";
-    static final String MAGIC = "libcore.io.DiskLruCache";
-    static final String VERSION_1 = "1";
-    static final long ANY_SEQUENCE_NUMBER = -1;
-    private static final String CLEAN = "CLEAN";
-    private static final String DIRTY = "DIRTY";
-    private static final String REMOVE = "REMOVE";
-    private static final String READ = "READ";
-
-    private static final Charset UTF_8 = Charset.forName("UTF-8");
-    private static final int IO_BUFFER_SIZE = 8 * 1024;
-
-    /*
-     * This cache uses a journal file named "journal". A typical journal file
-     * looks like this:
-     *     libcore.io.DiskLruCache
-     *     1
-     *     100
-     *     2
-     *
-     *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
-     *     DIRTY 335c4c6028171cfddfbaae1a9c313c52
-     *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
-     *     REMOVE 335c4c6028171cfddfbaae1a9c313c52
-     *     DIRTY 1ab96a171faeeee38496d8b330771a7a
-     *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
-     *     READ 335c4c6028171cfddfbaae1a9c313c52
-     *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
-     *
-     * The first five lines of the journal form its header. They are the
-     * constant string "libcore.io.DiskLruCache", the disk cache's version,
-     * the application's version, the value count, and a blank line.
-     *
-     * Each of the subsequent lines in the file is a record of the state of a
-     * cache entry. Each line contains space-separated values: a state, a key,
-     * and optional state-specific values.
-     *   o DIRTY lines track that an entry is actively being created or updated.
-     *     Every successful DIRTY action should be followed by a CLEAN or REMOVE
-     *     action. DIRTY lines without a matching CLEAN or REMOVE indicate that
-     *     temporary files may need to be deleted.
-     *   o CLEAN lines track a cache entry that has been successfully published
-     *     and may be read. A publish line is followed by the lengths of each of
-     *     its values.
-     *   o READ lines track accesses for LRU.
-     *   o REMOVE lines track entries that have been deleted.
-     *
-     * The journal file is appended to as cache operations occur. The journal may
-     * occasionally be compacted by dropping redundant lines. A temporary file named
-     * "journal.tmp" will be used during compaction; that file should be deleted if
-     * it exists when the cache is opened.
-     */
-
-    private final File directory;
-    private final File journalFile;
-    private final File journalFileTmp;
-    private final int appVersion;
-    private final long maxSize;
-    private final int valueCount;
-    private long size = 0;
-    private Writer journalWriter;
-    private final LinkedHashMap<String, Entry> lruEntries
-            = new LinkedHashMap<String, Entry>(0, 0.75f, true);
-    private int redundantOpCount;
-
-    /**
-     * To differentiate between old and current snapshots, each entry is given
-     * a sequence number each time an edit is committed. A snapshot is stale if
-     * its sequence number is not equal to its entry's sequence number.
-     */
-    private long nextSequenceNumber = 0;
-
-    /* From java.util.Arrays */
-    @SuppressWarnings("unchecked")
-    private static <T> T[] copyOfRange(T[] original, int start, int end) {
-        final int originalLength = original.length; // For exception priority compatibility.
-        if (start > end) {
-            throw new IllegalArgumentException();
-        }
-        if (start < 0 || start > originalLength) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        final int resultLength = end - start;
-        final int copyLength = Math.min(resultLength, originalLength - start);
-        final T[] result = (T[]) Array
-                .newInstance(original.getClass().getComponentType(), resultLength);
-        System.arraycopy(original, start, result, 0, copyLength);
-        return result;
-    }
-
-    /**
-     * Returns the remainder of 'reader' as a string, closing it when done.
-     */
-    public static String readFully(Reader reader) throws IOException {
-        try {
-            StringWriter writer = new StringWriter();
-            char[] buffer = new char[1024];
-            int count;
-            while ((count = reader.read(buffer)) != -1) {
-                writer.write(buffer, 0, count);
-            }
-            return writer.toString();
-        } finally {
-            reader.close();
-        }
-    }
-
-    /**
-     * Returns the ASCII characters up to but not including the next "\r\n", or
-     * "\n".
-     *
-     * @throws java.io.EOFException if the stream is exhausted before the next newline
-     *     character.
-     */
-    public static String readAsciiLine(InputStream in) throws IOException {
-        // TODO: support UTF-8 here instead
-
-        StringBuilder result = new StringBuilder(80);
-        while (true) {
-            int c = in.read();
-            if (c == -1) {
-                throw new EOFException();
-            } else if (c == '\n') {
-                break;
-            }
-
-            result.append((char) c);
-        }
-        int length = result.length();
-        if (length > 0 && result.charAt(length - 1) == '\r') {
-            result.setLength(length - 1);
-        }
-        return result.toString();
-    }
-
-    /**
-     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
-     */
-    public static void closeQuietly(Closeable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException rethrown) {
-                throw rethrown;
-            } catch (Exception ignored) {
-            }
-        }
-    }
-
-    /**
-     * Recursively delete everything in {@code dir}.
-     */
-    // TODO: this should specify paths as Strings rather than as Files
-    public static void deleteContents(File dir) throws IOException {
-        File[] files = dir.listFiles();
-        if (files == null) {
-            throw new IllegalArgumentException("not a directory: " + dir);
-        }
-        for (File file : files) {
-            if (file.isDirectory()) {
-                deleteContents(file);
-            }
-            if (!file.delete()) {
-                throw new IOException("failed to delete file: " + file);
-            }
-        }
-    }
-
-    /** This cache uses a single background thread to evict entries. */
-    private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
-            60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
-    private final Callable<Void> cleanupCallable = new Callable<Void>() {
-        @Override public Void call() throws Exception {
-            synchronized (DiskLruCache.this) {
-                if (journalWriter == null) {
-                    return null; // closed
-                }
-                trimToSize();
-                if (journalRebuildRequired()) {
-                    rebuildJournal();
-                    redundantOpCount = 0;
-                }
-            }
-            return null;
-        }
-    };
-
-    private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
-        this.directory = directory;
-        this.appVersion = appVersion;
-        this.journalFile = new File(directory, JOURNAL_FILE);
-        this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
-        this.valueCount = valueCount;
-        this.maxSize = maxSize;
-    }
-
-    /**
-     * Opens the cache in {@code directory}, creating a cache if none exists
-     * there.
-     *
-     * @param directory a writable directory
-     * @param appVersion
-     * @param valueCount the number of values per cache entry. Must be positive.
-     * @param maxSize the maximum number of bytes this cache should use to store
-     * @throws IOException if reading or writing the cache directory fails
-     */
-    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
-            throws IOException {
-        if (maxSize <= 0) {
-            throw new IllegalArgumentException("maxSize <= 0");
-        }
-        if (valueCount <= 0) {
-            throw new IllegalArgumentException("valueCount <= 0");
-        }
-
-        // prefer to pick up where we left off
-        DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
-        if (cache.journalFile.exists()) {
-            try {
-                cache.readJournal();
-                cache.processJournal();
-                cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
-                        IO_BUFFER_SIZE);
-                return cache;
-            } catch (IOException journalIsCorrupt) {
-//                System.logW("DiskLruCache " + directory + " is corrupt: "
-//                        + journalIsCorrupt.getMessage() + ", removing");
-                cache.delete();
-            }
-        }
-
-        // create a new empty cache
-        directory.mkdirs();
-        cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
-        cache.rebuildJournal();
-        return cache;
-    }
-
-    private void readJournal() throws IOException {
-        InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE);
-        try {
-            String magic = readAsciiLine(in);
-            String version = readAsciiLine(in);
-            String appVersionString = readAsciiLine(in);
-            String valueCountString = readAsciiLine(in);
-            String blank = readAsciiLine(in);
-            if (!MAGIC.equals(magic)
-                    || !VERSION_1.equals(version)
-                    || !Integer.toString(appVersion).equals(appVersionString)
-                    || !Integer.toString(valueCount).equals(valueCountString)
-                    || !"".equals(blank)) {
-                throw new IOException("unexpected journal header: ["
-                        + magic + ", " + version + ", " + valueCountString + ", " + blank + "]");
-            }
-
-            while (true) {
-                try {
-                    readJournalLine(readAsciiLine(in));
-                } catch (EOFException endOfJournal) {
-                    break;
-                }
-            }
-        } finally {
-            closeQuietly(in);
-        }
-    }
-
-    private void readJournalLine(String line) throws IOException {
-        String[] parts = line.split(" ");
-        if (parts.length < 2) {
-            throw new IOException("unexpected journal line: " + line);
-        }
-
-        String key = parts[1];
-        if (parts[0].equals(REMOVE) && parts.length == 2) {
-            lruEntries.remove(key);
-            return;
-        }
-
-        Entry entry = lruEntries.get(key);
-        if (entry == null) {
-            entry = new Entry(key);
-            lruEntries.put(key, entry);
-        }
-
-        if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {
-            entry.readable = true;
-            entry.currentEditor = null;
-            entry.setLengths(copyOfRange(parts, 2, parts.length));
-        } else if (parts[0].equals(DIRTY) && parts.length == 2) {
-            entry.currentEditor = new Editor(entry);
-        } else if (parts[0].equals(READ) && parts.length == 2) {
-            // this work was already done by calling lruEntries.get()
-        } else {
-            throw new IOException("unexpected journal line: " + line);
-        }
-    }
-
-    /**
-     * Computes the initial size and collects garbage as a part of opening the
-     * cache. Dirty entries are assumed to be inconsistent and will be deleted.
-     */
-    private void processJournal() throws IOException {
-        deleteIfExists(journalFileTmp);
-        for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
-            Entry entry = i.next();
-            if (entry.currentEditor == null) {
-                for (int t = 0; t < valueCount; t++) {
-                    size += entry.lengths[t];
-                }
-            } else {
-                entry.currentEditor = null;
-                for (int t = 0; t < valueCount; t++) {
-                    deleteIfExists(entry.getCleanFile(t));
-                    deleteIfExists(entry.getDirtyFile(t));
-                }
-                i.remove();
-            }
-        }
-    }
-
-    /**
-     * Creates a new journal that omits redundant information. This replaces the
-     * current journal if it exists.
-     */
-    private synchronized void rebuildJournal() throws IOException {
-        if (journalWriter != null) {
-            journalWriter.close();
-        }
-
-        Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE);
-        writer.write(MAGIC);
-        writer.write("\n");
-        writer.write(VERSION_1);
-        writer.write("\n");
-        writer.write(Integer.toString(appVersion));
-        writer.write("\n");
-        writer.write(Integer.toString(valueCount));
-        writer.write("\n");
-        writer.write("\n");
-
-        for (Entry entry : lruEntries.values()) {
-            if (entry.currentEditor != null) {
-                writer.write(DIRTY + ' ' + entry.key + '\n');
-            } else {
-                writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
-            }
-        }
-
-        writer.close();
-        journalFileTmp.renameTo(journalFile);
-        journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE);
-    }
-
-    private static void deleteIfExists(File file) throws IOException {
-//        try {
-//            Libcore.os.remove(file.getPath());
-//        } catch (ErrnoException errnoException) {
-//            if (errnoException.errno != OsConstants.ENOENT) {
-//                throw errnoException.rethrowAsIOException();
-//            }
-//        }
-        if (file.exists() && !file.delete()) {
-            throw new IOException();
-        }
-    }
-
-    /**
-     * Returns a snapshot of the entry named {@code key}, or null if it doesn't
-     * exist is not currently readable. If a value is returned, it is moved to
-     * the head of the LRU queue.
-     */
-    public synchronized Snapshot get(String key) throws IOException {
-        checkNotClosed();
-        validateKey(key);
-        Entry entry = lruEntries.get(key);
-        if (entry == null) {
-            return null;
-        }
-
-        if (!entry.readable) {
-            return null;
-        }
-
-        /*
-         * Open all streams eagerly to guarantee that we see a single published
-         * snapshot. If we opened streams lazily then the streams could come
-         * from different edits.
-         */
-        InputStream[] ins = new InputStream[valueCount];
-        try {
-            for (int i = 0; i < valueCount; i++) {
-                ins[i] = new FileInputStream(entry.getCleanFile(i));
-            }
-        } catch (FileNotFoundException e) {
-            // a file must have been deleted manually!
-            return null;
-        }
-
-        redundantOpCount++;
-        journalWriter.append(READ + ' ' + key + '\n');
-        if (journalRebuildRequired()) {
-            executorService.submit(cleanupCallable);
-        }
-
-        return new Snapshot(key, entry.sequenceNumber, ins);
-    }
-
-    /**
-     * Returns an editor for the entry named {@code key}, or null if another
-     * edit is in progress.
-     */
-    public Editor edit(String key) throws IOException {
-        return edit(key, ANY_SEQUENCE_NUMBER);
-    }
-
-    private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
-        checkNotClosed();
-        validateKey(key);
-        Entry entry = lruEntries.get(key);
-        if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
-                && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
-            return null; // snapshot is stale
-        }
-        if (entry == null) {
-            entry = new Entry(key);
-            lruEntries.put(key, entry);
-        } else if (entry.currentEditor != null) {
-            return null; // another edit is in progress
-        }
-
-        Editor editor = new Editor(entry);
-        entry.currentEditor = editor;
-
-        // flush the journal before creating files to prevent file leaks
-        journalWriter.write(DIRTY + ' ' + key + '\n');
-        journalWriter.flush();
-        return editor;
-    }
-
-    /**
-     * Returns the directory where this cache stores its data.
-     */
-    public File getDirectory() {
-        return directory;
-    }
-
-    /**
-     * Returns the maximum number of bytes that this cache should use to store
-     * its data.
-     */
-    public long maxSize() {
-        return maxSize;
-    }
-
-    /**
-     * Returns the number of bytes currently being used to store the values in
-     * this cache. This may be greater than the max size if a background
-     * deletion is pending.
-     */
-    public synchronized long size() {
-        return size;
-    }
-
-    private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
-        Entry entry = editor.entry;
-        if (entry.currentEditor != editor) {
-            throw new IllegalStateException();
-        }
-
-        // if this edit is creating the entry for the first time, every index must have a value
-        if (success && !entry.readable) {
-            for (int i = 0; i < valueCount; i++) {
-                if (!entry.getDirtyFile(i).exists()) {
-                    editor.abort();
-                    throw new IllegalStateException("edit didn't create file " + i);
-                }
-            }
-        }
-
-        for (int i = 0; i < valueCount; i++) {
-            File dirty = entry.getDirtyFile(i);
-            if (success) {
-                if (dirty.exists()) {
-                    File clean = entry.getCleanFile(i);
-                    dirty.renameTo(clean);
-                    long oldLength = entry.lengths[i];
-                    long newLength = clean.length();
-                    entry.lengths[i] = newLength;
-                    size = size - oldLength + newLength;
-                }
-            } else {
-                deleteIfExists(dirty);
-            }
-        }
-
-        redundantOpCount++;
-        entry.currentEditor = null;
-        if (entry.readable | success) {
-            entry.readable = true;
-            journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
-            if (success) {
-                entry.sequenceNumber = nextSequenceNumber++;
-            }
-        } else {
-            lruEntries.remove(entry.key);
-            journalWriter.write(REMOVE + ' ' + entry.key + '\n');
-        }
-
-        if (size > maxSize || journalRebuildRequired()) {
-            executorService.submit(cleanupCallable);
-        }
-    }
-
-    /**
-     * We only rebuild the journal when it will halve the size of the journal
-     * and eliminate at least 2000 ops.
-     */
-    private boolean journalRebuildRequired() {
-        final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000;
-        return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD
-                && redundantOpCount >= lruEntries.size();
-    }
-
-    /**
-     * Drops the entry for {@code key} if it exists and can be removed. Entries
-     * actively being edited cannot be removed.
-     *
-     * @return true if an entry was removed.
-     */
-    public synchronized boolean remove(String key) throws IOException {
-        checkNotClosed();
-        validateKey(key);
-        Entry entry = lruEntries.get(key);
-        if (entry == null || entry.currentEditor != null) {
-            return false;
-        }
-
-        for (int i = 0; i < valueCount; i++) {
-            File file = entry.getCleanFile(i);
-            if (!file.delete()) {
-                throw new IOException("failed to delete " + file);
-            }
-            size -= entry.lengths[i];
-            entry.lengths[i] = 0;
-        }
-
-        redundantOpCount++;
-        journalWriter.append(REMOVE + ' ' + key + '\n');
-        lruEntries.remove(key);
-
-        if (journalRebuildRequired()) {
-            executorService.submit(cleanupCallable);
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns true if this cache has been closed.
-     */
-    public boolean isClosed() {
-        return journalWriter == null;
-    }
-
-    private void checkNotClosed() {
-        if (journalWriter == null) {
-            throw new IllegalStateException("cache is closed");
-        }
-    }
-
-    /**
-     * Force buffered operations to the filesystem.
-     */
-    public synchronized void flush() throws IOException {
-        checkNotClosed();
-        trimToSize();
-        journalWriter.flush();
-    }
-
-    /**
-     * Closes this cache. Stored values will remain on the filesystem.
-     */
-    public synchronized void close() throws IOException {
-        if (journalWriter == null) {
-            return; // already closed
-        }
-        for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
-            if (entry.currentEditor != null) {
-                entry.currentEditor.abort();
-            }
-        }
-        trimToSize();
-        journalWriter.close();
-        journalWriter = null;
-    }
-
-    private void trimToSize() throws IOException {
-        while (size > maxSize) {
-//            Map.Entry<String, Entry> toEvict = lruEntries.eldest();
-            final Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
-            remove(toEvict.getKey());
-        }
-    }
-
-    /**
-     * Closes the cache and deletes all of its stored values. This will delete
-     * all files in the cache directory including files that weren't created by
-     * the cache.
-     */
-    public void delete() throws IOException {
-        close();
-        deleteContents(directory);
-    }
-
-    private void validateKey(String key) {
-        if (key.contains(" ") || key.contains("\n") || key.contains("\r")) {
-            throw new IllegalArgumentException(
-                    "keys must not contain spaces or newlines: \"" + key + "\"");
-        }
-    }
-
-    private static String inputStreamToString(InputStream in) throws IOException {
-        return readFully(new InputStreamReader(in, UTF_8));
-    }
-
-    /**
-     * A snapshot of the values for an entry.
-     */
-    public final class Snapshot implements Closeable {
-        private final String key;
-        private final long sequenceNumber;
-        private final InputStream[] ins;
-
-        private Snapshot(String key, long sequenceNumber, InputStream[] ins) {
-            this.key = key;
-            this.sequenceNumber = sequenceNumber;
-            this.ins = ins;
-        }
-
-        /**
-         * Returns an editor for this snapshot's entry, or null if either the
-         * entry has changed since this snapshot was created or if another edit
-         * is in progress.
-         */
-        public Editor edit() throws IOException {
-            return DiskLruCache.this.edit(key, sequenceNumber);
-        }
-
-        /**
-         * Returns the unbuffered stream with the value for {@code index}.
-         */
-        public InputStream getInputStream(int index) {
-            return ins[index];
-        }
-
-        /**
-         * Returns the string value for {@code index}.
-         */
-        public String getString(int index) throws IOException {
-            return inputStreamToString(getInputStream(index));
-        }
-
-        @Override public void close() {
-            for (InputStream in : ins) {
-                closeQuietly(in);
-            }
-        }
-    }
-
-    /**
-     * Edits the values for an entry.
-     */
-    public final class Editor {
-        private final Entry entry;
-        private boolean hasErrors;
-
-        private Editor(Entry entry) {
-            this.entry = entry;
-        }
-
-        /**
-         * Returns an unbuffered input stream to read the last committed value,
-         * or null if no value has been committed.
-         */
-        public InputStream newInputStream(int index) throws IOException {
-            synchronized (DiskLruCache.this) {
-                if (entry.currentEditor != this) {
-                    throw new IllegalStateException();
-                }
-                if (!entry.readable) {
-                    return null;
-                }
-                return new FileInputStream(entry.getCleanFile(index));
-            }
-        }
-
-        /**
-         * Returns the last committed value as a string, or null if no value
-         * has been committed.
-         */
-        public String getString(int index) throws IOException {
-            InputStream in = newInputStream(index);
-            return in != null ? inputStreamToString(in) : null;
-        }
-
-        /**
-         * Returns a new unbuffered output stream to write the value at
-         * {@code index}. If the underlying output stream encounters errors
-         * when writing to the filesystem, this edit will be aborted when
-         * {@link #commit} is called. The returned output stream does not throw
-         * IOExceptions.
-         */
-        public OutputStream newOutputStream(int index) throws IOException {
-            synchronized (DiskLruCache.this) {
-                if (entry.currentEditor != this) {
-                    throw new IllegalStateException();
-                }
-                return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
-            }
-        }
-
-        /**
-         * Sets the value at {@code index} to {@code value}.
-         */
-        public void set(int index, String value) throws IOException {
-            Writer writer = null;
-            try {
-                writer = new OutputStreamWriter(newOutputStream(index), UTF_8);
-                writer.write(value);
-            } finally {
-                closeQuietly(writer);
-            }
-        }
-
-        /**
-         * Commits this edit so it is visible to readers.  This releases the
-         * edit lock so another edit may be started on the same key.
-         */
-        public void commit() throws IOException {
-            if (hasErrors) {
-                completeEdit(this, false);
-                remove(entry.key); // the previous entry is stale
-            } else {
-                completeEdit(this, true);
-            }
-        }
-
-        /**
-         * Aborts this edit. This releases the edit lock so another edit may be
-         * started on the same key.
-         */
-        public void abort() throws IOException {
-            completeEdit(this, false);
-        }
-
-        private class FaultHidingOutputStream extends FilterOutputStream {
-            private FaultHidingOutputStream(OutputStream out) {
-                super(out);
-            }
-
-            @Override public void write(int oneByte) {
-                try {
-                    out.write(oneByte);
-                } catch (IOException e) {
-                    hasErrors = true;
-                }
-            }
-
-            @Override public void write(byte[] buffer, int offset, int length) {
-                try {
-                    out.write(buffer, offset, length);
-                } catch (IOException e) {
-                    hasErrors = true;
-                }
-            }
-
-            @Override public void close() {
-                try {
-                    out.close();
-                } catch (IOException e) {
-                    hasErrors = true;
-                }
-            }
-
-            @Override public void flush() {
-                try {
-                    out.flush();
-                } catch (IOException e) {
-                    hasErrors = true;
-                }
-            }
-        }
-    }
-
-    private final class Entry {
-        private final String key;
-
-        /** Lengths of this entry's files. */
-        private final long[] lengths;
-
-        /** True if this entry has ever been published */
-        private boolean readable;
-
-        /** The ongoing edit or null if this entry is not being edited. */
-        private Editor currentEditor;
-
-        /** The sequence number of the most recently committed edit to this entry. */
-        private long sequenceNumber;
-
-        private Entry(String key) {
-            this.key = key;
-            this.lengths = new long[valueCount];
-        }
-
-        public String getLengths() throws IOException {
-            StringBuilder result = new StringBuilder();
-            for (long size : lengths) {
-                result.append(' ').append(size);
-            }
-            return result.toString();
-        }
-
-        /**
-         * Set lengths using decimal numbers like "10123".
-         */
-        private void setLengths(String[] strings) throws IOException {
-            if (strings.length != valueCount) {
-                throw invalidLengths(strings);
-            }
-
-            try {
-                for (int i = 0; i < strings.length; i++) {
-                    lengths[i] = Long.parseLong(strings[i]);
-                }
-            } catch (NumberFormatException e) {
-                throw invalidLengths(strings);
-            }
-        }
-
-        private IOException invalidLengths(String[] strings) throws IOException {
-            throw new IOException("unexpected journal line: " + Arrays.toString(strings));
-        }
-
-        public File getCleanFile(int i) {
-            return new File(directory, key + "." + i);
-        }
-
-        public File getDirtyFile(int i) {
-            return new File(directory, key + "." + i + ".tmp");
-        }
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java
deleted file mode 100644
index 7021d2c..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.util;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.StatFs;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.util.LruCache;
-import android.util.Log;
-
-import com.example.android.bitmapfun.BuildConfig;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ref.SoftReference;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * This class handles disk and memory caching of bitmaps in conjunction with the
- * {@link ImageWorker} class and its subclasses. Use
- * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} to get an instance of this
- * class, although usually a cache should be added directly to an {@link ImageWorker} by calling
- * {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}.
- */
-public class ImageCache {
-    private static final String TAG = "ImageCache";
-
-    // Default memory cache size in kilobytes
-    private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 5MB
-
-    // Default disk cache size in bytes
-    private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
-
-    // Compression settings when writing images to disk cache
-    private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
-    private static final int DEFAULT_COMPRESS_QUALITY = 70;
-    private static final int DISK_CACHE_INDEX = 0;
-
-    // Constants to easily toggle various caches
-    private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
-    private static final boolean DEFAULT_DISK_CACHE_ENABLED = true;
-    private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
-
-    private DiskLruCache mDiskLruCache;
-    private LruCache<String, BitmapDrawable> mMemoryCache;
-    private ImageCacheParams mCacheParams;
-    private final Object mDiskCacheLock = new Object();
-    private boolean mDiskCacheStarting = true;
-
-    private Set<SoftReference<Bitmap>> mReusableBitmaps;
-
-    /**
-     * Create a new ImageCache object using the specified parameters. This should not be
-     * called directly by other classes, instead use
-     * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} to fetch an ImageCache
-     * instance.
-     *
-     * @param cacheParams The cache parameters to use to initialize the cache
-     */
-    private ImageCache(ImageCacheParams cacheParams) {
-        init(cacheParams);
-    }
-
-    /**
-     * Return an {@link ImageCache} instance. A {@link RetainFragment} is used to retain the
-     * ImageCache object across configuration changes such as a change in device orientation.
-     *
-     * @param fragmentManager The fragment manager to use when dealing with the retained fragment.
-     * @param cacheParams The cache parameters to use if the ImageCache needs instantiation.
-     * @return An existing retained ImageCache object or a new one if one did not exist
-     */
-    public static ImageCache getInstance(
-            FragmentManager fragmentManager, ImageCacheParams cacheParams) {
-
-        // Search for, or create an instance of the non-UI RetainFragment
-        final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager);
-
-        // See if we already have an ImageCache stored in RetainFragment
-        ImageCache imageCache = (ImageCache) mRetainFragment.getObject();
-
-        // No existing ImageCache, create one and store it in RetainFragment
-        if (imageCache == null) {
-            imageCache = new ImageCache(cacheParams);
-            mRetainFragment.setObject(imageCache);
-        }
-
-        return imageCache;
-    }
-
-    /**
-     * Initialize the cache, providing all parameters.
-     *
-     * @param cacheParams The cache parameters to initialize the cache
-     */
-    private void init(ImageCacheParams cacheParams) {
-        mCacheParams = cacheParams;
-
-        // Set up memory cache
-        if (mCacheParams.memoryCacheEnabled) {
-            if (BuildConfig.DEBUG) {
-                Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")");
-            }
-
-            // If we're running on Honeycomb or newer, create a set of reusable bitmaps that can be
-            // populated into the inBitmap field of BitmapFactory.Options. Note that the set is
-            // of SoftReferences which will actually not be very effective due to the garbage
-            // collector being aggressive clearing Soft/WeakReferences. A better approach
-            // would be to use a strongly references bitmaps, however this would require some
-            // balancing of memory usage between this set and the bitmap LruCache. It would also
-            // require knowledge of the expected size of the bitmaps. From Honeycomb to JellyBean
-            // the size would need to be precise, from KitKat onward the size would just need to
-            // be the upper bound (due to changes in how inBitmap can re-use bitmaps).
-            if (Utils.hasHoneycomb()) {
-                mReusableBitmaps =
-                        Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
-            }
-
-            mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
-
-                /**
-                 * Notify the removed entry that is no longer being cached
-                 */
-                @Override
-                protected void entryRemoved(boolean evicted, String key,
-                        BitmapDrawable oldValue, BitmapDrawable newValue) {
-                    if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
-                        // The removed entry is a recycling drawable, so notify it 
-                        // that it has been removed from the memory cache
-                        ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
-                    } else {
-                        // The removed entry is a standard BitmapDrawable
-
-                        if (Utils.hasHoneycomb()) {
-                            // We're running on Honeycomb or later, so add the bitmap
-                            // to a SoftReference set for possible use with inBitmap later
-                            mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
-                        }
-                    }
-                }
-
-                /**
-                 * Measure item size in kilobytes rather than units which is more practical
-                 * for a bitmap cache
-                 */
-                @Override
-                protected int sizeOf(String key, BitmapDrawable value) {
-                    final int bitmapSize = getBitmapSize(value) / 1024;
-                    return bitmapSize == 0 ? 1 : bitmapSize;
-                }
-            };
-        }
-
-        // By default the disk cache is not initialized here as it should be initialized
-        // on a separate thread due to disk access.
-        if (cacheParams.initDiskCacheOnCreate) {
-            // Set up disk cache
-            initDiskCache();
-        }
-    }
-
-    /**
-     * Initializes the disk cache.  Note that this includes disk access so this should not be
-     * executed on the main/UI thread. By default an ImageCache does not initialize the disk
-     * cache when it is created, instead you should call initDiskCache() to initialize it on a
-     * background thread.
-     */
-    public void initDiskCache() {
-        // Set up disk cache
-        synchronized (mDiskCacheLock) {
-            if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
-                File diskCacheDir = mCacheParams.diskCacheDir;
-                if (mCacheParams.diskCacheEnabled && diskCacheDir != null) {
-                    if (!diskCacheDir.exists()) {
-                        diskCacheDir.mkdirs();
-                    }
-                    if (getUsableSpace(diskCacheDir) > mCacheParams.diskCacheSize) {
-                        try {
-                            mDiskLruCache = DiskLruCache.open(
-                                    diskCacheDir, 1, 1, mCacheParams.diskCacheSize);
-                            if (BuildConfig.DEBUG) {
-                                Log.d(TAG, "Disk cache initialized");
-                            }
-                        } catch (final IOException e) {
-                            mCacheParams.diskCacheDir = null;
-                            Log.e(TAG, "initDiskCache - " + e);
-                        }
-                    }
-                }
-            }
-            mDiskCacheStarting = false;
-            mDiskCacheLock.notifyAll();
-        }
-    }
-
-    /**
-     * Adds a bitmap to both memory and disk cache.
-     * @param data Unique identifier for the bitmap to store
-     * @param value The bitmap drawable to store
-     */
-    public void addBitmapToCache(String data, BitmapDrawable value) {
-        if (data == null || value == null) {
-            return;
-        }
-
-        // Add to memory cache
-        if (mMemoryCache != null) {
-            if (RecyclingBitmapDrawable.class.isInstance(value)) {
-                // The removed entry is a recycling drawable, so notify it 
-                // that it has been added into the memory cache
-                ((RecyclingBitmapDrawable) value).setIsCached(true);
-            }
-            mMemoryCache.put(data, value);
-        }
-
-        synchronized (mDiskCacheLock) {
-            // Add to disk cache
-            if (mDiskLruCache != null) {
-                final String key = hashKeyForDisk(data);
-                OutputStream out = null;
-                try {
-                    DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
-                    if (snapshot == null) {
-                        final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
-                        if (editor != null) {
-                            out = editor.newOutputStream(DISK_CACHE_INDEX);
-                            value.getBitmap().compress(
-                                    mCacheParams.compressFormat, mCacheParams.compressQuality, out);
-                            editor.commit();
-                            out.close();
-                        }
-                    } else {
-                        snapshot.getInputStream(DISK_CACHE_INDEX).close();
-                    }
-                } catch (final IOException e) {
-                    Log.e(TAG, "addBitmapToCache - " + e);
-                } catch (Exception e) {
-                    Log.e(TAG, "addBitmapToCache - " + e);
-                } finally {
-                    try {
-                        if (out != null) {
-                            out.close();
-                        }
-                    } catch (IOException e) {}
-                }
-            }
-        }
-    }
-
-    /**
-     * Get from memory cache.
-     *
-     * @param data Unique identifier for which item to get
-     * @return The bitmap drawable if found in cache, null otherwise
-     */
-    public BitmapDrawable getBitmapFromMemCache(String data) {
-        BitmapDrawable memValue = null;
-
-        if (mMemoryCache != null) {
-            memValue = mMemoryCache.get(data);
-        }
-
-        if (BuildConfig.DEBUG && memValue != null) {
-            Log.d(TAG, "Memory cache hit");
-        }
-
-        return memValue;
-    }
-
-    /**
-     * Get from disk cache.
-     *
-     * @param data Unique identifier for which item to get
-     * @return The bitmap if found in cache, null otherwise
-     */
-    public Bitmap getBitmapFromDiskCache(String data) {
-        final String key = hashKeyForDisk(data);
-        Bitmap bitmap = null;
-
-        synchronized (mDiskCacheLock) {
-            while (mDiskCacheStarting) {
-                try {
-                    mDiskCacheLock.wait();
-                } catch (InterruptedException e) {}
-            }
-            if (mDiskLruCache != null) {
-                InputStream inputStream = null;
-                try {
-                    final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
-                    if (snapshot != null) {
-                        if (BuildConfig.DEBUG) {
-                            Log.d(TAG, "Disk cache hit");
-                        }
-                        inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
-                        if (inputStream != null) {
-                            FileDescriptor fd = ((FileInputStream) inputStream).getFD();
-
-                            // Decode bitmap, but we don't want to sample so give
-                            // MAX_VALUE as the target dimensions
-                            bitmap = ImageResizer.decodeSampledBitmapFromDescriptor(
-                                    fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this);
-                        }
-                    }
-                } catch (final IOException e) {
-                    Log.e(TAG, "getBitmapFromDiskCache - " + e);
-                } finally {
-                    try {
-                        if (inputStream != null) {
-                            inputStream.close();
-                        }
-                    } catch (IOException e) {}
-                }
-            }
-            return bitmap;
-        }
-    }
-
-    /**
-     * @param options - BitmapFactory.Options with out* options populated
-     * @return Bitmap that case be used for inBitmap
-     */
-    protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
-        Bitmap bitmap = null;
-
-        if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
-            synchronized (mReusableBitmaps) {
-                final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps.iterator();
-                Bitmap item;
-
-                while (iterator.hasNext()) {
-                    item = iterator.next().get();
-
-                    if (null != item && item.isMutable()) {
-                        // Check to see it the item can be used for inBitmap
-                        if (canUseForInBitmap(item, options)) {
-                            bitmap = item;
-
-                            // Remove from reusable set so it can't be used again
-                            iterator.remove();
-                            break;
-                        }
-                    } else {
-                        // Remove from the set if the reference has been cleared.
-                        iterator.remove();
-                    }
-                }
-            }
-        }
-
-        return bitmap;
-    }
-
-    /**
-     * Clears both the memory and disk cache associated with this ImageCache object. Note that
-     * this includes disk access so this should not be executed on the main/UI thread.
-     */
-    public void clearCache() {
-        if (mMemoryCache != null) {
-            mMemoryCache.evictAll();
-            if (BuildConfig.DEBUG) {
-                Log.d(TAG, "Memory cache cleared");
-            }
-        }
-
-        synchronized (mDiskCacheLock) {
-            mDiskCacheStarting = true;
-            if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
-                try {
-                    mDiskLruCache.delete();
-                    if (BuildConfig.DEBUG) {
-                        Log.d(TAG, "Disk cache cleared");
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "clearCache - " + e);
-                }
-                mDiskLruCache = null;
-                initDiskCache();
-            }
-        }
-    }
-
-    /**
-     * Flushes the disk cache associated with this ImageCache object. Note that this includes
-     * disk access so this should not be executed on the main/UI thread.
-     */
-    public void flush() {
-        synchronized (mDiskCacheLock) {
-            if (mDiskLruCache != null) {
-                try {
-                    mDiskLruCache.flush();
-                    if (BuildConfig.DEBUG) {
-                        Log.d(TAG, "Disk cache flushed");
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "flush - " + e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Closes the disk cache associated with this ImageCache object. Note that this includes
-     * disk access so this should not be executed on the main/UI thread.
-     */
-    public void close() {
-        synchronized (mDiskCacheLock) {
-            if (mDiskLruCache != null) {
-                try {
-                    if (!mDiskLruCache.isClosed()) {
-                        mDiskLruCache.close();
-                        mDiskLruCache = null;
-                        if (BuildConfig.DEBUG) {
-                            Log.d(TAG, "Disk cache closed");
-                        }
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "close - " + e);
-                }
-            }
-        }
-    }
-
-    /**
-     * A holder class that contains cache parameters.
-     */
-    public static class ImageCacheParams {
-        public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
-        public int diskCacheSize = DEFAULT_DISK_CACHE_SIZE;
-        public File diskCacheDir;
-        public CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
-        public int compressQuality = DEFAULT_COMPRESS_QUALITY;
-        public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
-        public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED;
-        public boolean initDiskCacheOnCreate = DEFAULT_INIT_DISK_CACHE_ON_CREATE;
-
-        /**
-         * Create a set of image cache parameters that can be provided to
-         * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} or
-         * {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}.
-         * @param context A context to use.
-         * @param diskCacheDirectoryName A unique subdirectory name that will be appended to the
-         *                               application cache directory. Usually "cache" or "images"
-         *                               is sufficient.
-         */
-        public ImageCacheParams(Context context, String diskCacheDirectoryName) {
-            diskCacheDir = getDiskCacheDir(context, diskCacheDirectoryName);
-        }
-
-        /**
-         * Sets the memory cache size based on a percentage of the max available VM memory.
-         * Eg. setting percent to 0.2 would set the memory cache to one fifth of the available
-         * memory. Throws {@link IllegalArgumentException} if percent is < 0.01 or > .8.
-         * memCacheSize is stored in kilobytes instead of bytes as this will eventually be passed
-         * to construct a LruCache which takes an int in its constructor.
-         *
-         * This value should be chosen carefully based on a number of factors
-         * Refer to the corresponding Android Training class for more discussion:
-         * http://developer.android.com/training/displaying-bitmaps/
-         *
-         * @param percent Percent of available app memory to use to size memory cache
-         */
-        public void setMemCacheSizePercent(float percent) {
-            if (percent < 0.01f || percent > 0.8f) {
-                throw new IllegalArgumentException("setMemCacheSizePercent - percent must be "
-                        + "between 0.01 and 0.8 (inclusive)");
-            }
-            memCacheSize = Math.round(percent * Runtime.getRuntime().maxMemory() / 1024);
-        }
-    }
-
-    /**
-     * @param candidate - Bitmap to check
-     * @param targetOptions - Options that have the out* value populated
-     * @return true if <code>candidate</code> can be used for inBitmap re-use with
-     *      <code>targetOptions</code>
-     */
-    @TargetApi(VERSION_CODES.KITKAT)
-    private static boolean canUseForInBitmap(
-            Bitmap candidate, BitmapFactory.Options targetOptions) {
-
-        if (!Utils.hasKitKat()) {
-            // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
-            return candidate.getWidth() == targetOptions.outWidth
-                    && candidate.getHeight() == targetOptions.outHeight
-                    && targetOptions.inSampleSize == 1;
-        }
-
-        // From Android 4.4 (KitKat) onward we can re-use if the byte size of the new bitmap
-        // is smaller than the reusable bitmap candidate allocation byte count.
-        int width = targetOptions.outWidth / targetOptions.inSampleSize;
-        int height = targetOptions.outHeight / targetOptions.inSampleSize;
-        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
-        return byteCount <= candidate.getAllocationByteCount();
-    }
-
-    /**
-     * Return the byte usage per pixel of a bitmap based on its configuration.
-     * @param config The bitmap configuration.
-     * @return The byte usage per pixel.
-     */
-    private static int getBytesPerPixel(Config config) {
-        if (config == Config.ARGB_8888) {
-            return 4;
-        } else if (config == Config.RGB_565) {
-            return 2;
-        } else if (config == Config.ARGB_4444) {
-            return 2;
-        } else if (config == Config.ALPHA_8) {
-            return 1;
-        }
-        return 1;
-    }
-
-    /**
-     * Get a usable cache directory (external if available, internal otherwise).
-     *
-     * @param context The context to use
-     * @param uniqueName A unique directory name to append to the cache dir
-     * @return The cache dir
-     */
-    public static File getDiskCacheDir(Context context, String uniqueName) {
-        // Check if media is mounted or storage is built-in, if so, try and use external cache dir
-        // otherwise use internal cache dir
-        final String cachePath =
-                Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
-                        !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
-                                context.getCacheDir().getPath();
-
-        return new File(cachePath + File.separator + uniqueName);
-    }
-
-    /**
-     * A hashing method that changes a string (like a URL) into a hash suitable for using as a
-     * disk filename.
-     */
-    public static String hashKeyForDisk(String key) {
-        String cacheKey;
-        try {
-            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
-            mDigest.update(key.getBytes());
-            cacheKey = bytesToHexString(mDigest.digest());
-        } catch (NoSuchAlgorithmException e) {
-            cacheKey = String.valueOf(key.hashCode());
-        }
-        return cacheKey;
-    }
-
-    private static String bytesToHexString(byte[] bytes) {
-        // http://stackoverflow.com/questions/332079
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < bytes.length; i++) {
-            String hex = Integer.toHexString(0xFF & bytes[i]);
-            if (hex.length() == 1) {
-                sb.append('0');
-            }
-            sb.append(hex);
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Get the size in bytes of a bitmap in a BitmapDrawable. Note that from Android 4.4 (KitKat)
-     * onward this returns the allocated memory size of the bitmap which can be larger than the
-     * actual bitmap data byte count (in the case it was re-used).
-     *
-     * @param value
-     * @return size in bytes
-     */
-    @TargetApi(VERSION_CODES.KITKAT)
-    public static int getBitmapSize(BitmapDrawable value) {
-        Bitmap bitmap = value.getBitmap();
-
-        // From KitKat onward use getAllocationByteCount() as allocated bytes can potentially be
-        // larger than bitmap byte count.
-        if (Utils.hasKitKat()) {
-            return bitmap.getAllocationByteCount();
-        }
-
-        if (Utils.hasHoneycombMR1()) {
-            return bitmap.getByteCount();
-        }
-
-        // Pre HC-MR1
-        return bitmap.getRowBytes() * bitmap.getHeight();
-    }
-
-    /**
-     * Check if external storage is built-in or removable.
-     *
-     * @return True if external storage is removable (like an SD card), false
-     *         otherwise.
-     */
-    @TargetApi(VERSION_CODES.GINGERBREAD)
-    public static boolean isExternalStorageRemovable() {
-        if (Utils.hasGingerbread()) {
-            return Environment.isExternalStorageRemovable();
-        }
-        return true;
-    }
-
-    /**
-     * Get the external app cache directory.
-     *
-     * @param context The context to use
-     * @return The external cache dir
-     */
-    @TargetApi(VERSION_CODES.FROYO)
-    public static File getExternalCacheDir(Context context) {
-        if (Utils.hasFroyo()) {
-            return context.getExternalCacheDir();
-        }
-
-        // Before Froyo we need to construct the external cache dir ourselves
-        final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
-        return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
-    }
-
-    /**
-     * Check how much usable space is available at a given path.
-     *
-     * @param path The path to check
-     * @return The space available in bytes
-     */
-    @TargetApi(VERSION_CODES.GINGERBREAD)
-    public static long getUsableSpace(File path) {
-        if (Utils.hasGingerbread()) {
-            return path.getUsableSpace();
-        }
-        final StatFs stats = new StatFs(path.getPath());
-        return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
-    }
-
-    /**
-     * Locate an existing instance of this Fragment or if not found, create and
-     * add it using FragmentManager.
-     *
-     * @param fm The FragmentManager manager to use.
-     * @return The existing instance of the Fragment or the new instance if just
-     *         created.
-     */
-    private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
-        // Check to see if we have retained the worker fragment.
-        RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(TAG);
-
-        // If not retained (or first time running), we need to create and add it.
-        if (mRetainFragment == null) {
-            mRetainFragment = new RetainFragment();
-            fm.beginTransaction().add(mRetainFragment, TAG).commitAllowingStateLoss();
-        }
-
-        return mRetainFragment;
-    }
-
-    /**
-     * A simple non-UI Fragment that stores a single Object and is retained over configuration
-     * changes. It will be used to retain the ImageCache object.
-     */
-    public static class RetainFragment extends Fragment {
-        private Object mObject;
-
-        /**
-         * Empty constructor as per the Fragment documentation
-         */
-        public RetainFragment() {}
-
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-
-            // Make sure this Fragment is retained over a configuration change
-            setRetainInstance(true);
-        }
-
-        /**
-         * Store a single object in this Fragment.
-         *
-         * @param object The object to store
-         */
-        public void setObject(Object object) {
-            mObject = object;
-        }
-
-        /**
-         * Get the stored object.
-         *
-         * @return The stored object
-         */
-        public Object getObject() {
-            return mObject;
-        }
-    }
-
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java
deleted file mode 100644
index 4c92d74..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.util;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Build;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.example.android.bitmapfun.BuildConfig;
-import com.example.android.bitmapfun.R;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-/**
- * A simple subclass of {@link ImageResizer} that fetches and resizes images fetched from a URL.
- */
-public class ImageFetcher extends ImageResizer {
-    private static final String TAG = "ImageFetcher";
-    private static final int HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10MB
-    private static final String HTTP_CACHE_DIR = "http";
-    private static final int IO_BUFFER_SIZE = 8 * 1024;
-
-    private DiskLruCache mHttpDiskCache;
-    private File mHttpCacheDir;
-    private boolean mHttpDiskCacheStarting = true;
-    private final Object mHttpDiskCacheLock = new Object();
-    private static final int DISK_CACHE_INDEX = 0;
-
-    /**
-     * Initialize providing a target image width and height for the processing images.
-     *
-     * @param context
-     * @param imageWidth
-     * @param imageHeight
-     */
-    public ImageFetcher(Context context, int imageWidth, int imageHeight) {
-        super(context, imageWidth, imageHeight);
-        init(context);
-    }
-
-    /**
-     * Initialize providing a single target image size (used for both width and height);
-     *
-     * @param context
-     * @param imageSize
-     */
-    public ImageFetcher(Context context, int imageSize) {
-        super(context, imageSize);
-        init(context);
-    }
-
-    private void init(Context context) {
-        checkConnection(context);
-        mHttpCacheDir = ImageCache.getDiskCacheDir(context, HTTP_CACHE_DIR);
-    }
-
-    @Override
-    protected void initDiskCacheInternal() {
-        super.initDiskCacheInternal();
-        initHttpDiskCache();
-    }
-
-    private void initHttpDiskCache() {
-        if (!mHttpCacheDir.exists()) {
-            mHttpCacheDir.mkdirs();
-        }
-        synchronized (mHttpDiskCacheLock) {
-            if (ImageCache.getUsableSpace(mHttpCacheDir) > HTTP_CACHE_SIZE) {
-                try {
-                    mHttpDiskCache = DiskLruCache.open(mHttpCacheDir, 1, 1, HTTP_CACHE_SIZE);
-                    if (BuildConfig.DEBUG) {
-                        Log.d(TAG, "HTTP cache initialized");
-                    }
-                } catch (IOException e) {
-                    mHttpDiskCache = null;
-                }
-            }
-            mHttpDiskCacheStarting = false;
-            mHttpDiskCacheLock.notifyAll();
-        }
-    }
-
-    @Override
-    protected void clearCacheInternal() {
-        super.clearCacheInternal();
-        synchronized (mHttpDiskCacheLock) {
-            if (mHttpDiskCache != null && !mHttpDiskCache.isClosed()) {
-                try {
-                    mHttpDiskCache.delete();
-                    if (BuildConfig.DEBUG) {
-                        Log.d(TAG, "HTTP cache cleared");
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "clearCacheInternal - " + e);
-                }
-                mHttpDiskCache = null;
-                mHttpDiskCacheStarting = true;
-                initHttpDiskCache();
-            }
-        }
-    }
-
-    @Override
-    protected void flushCacheInternal() {
-        super.flushCacheInternal();
-        synchronized (mHttpDiskCacheLock) {
-            if (mHttpDiskCache != null) {
-                try {
-                    mHttpDiskCache.flush();
-                    if (BuildConfig.DEBUG) {
-                        Log.d(TAG, "HTTP cache flushed");
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "flush - " + e);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected void closeCacheInternal() {
-        super.closeCacheInternal();
-        synchronized (mHttpDiskCacheLock) {
-            if (mHttpDiskCache != null) {
-                try {
-                    if (!mHttpDiskCache.isClosed()) {
-                        mHttpDiskCache.close();
-                        mHttpDiskCache = null;
-                        if (BuildConfig.DEBUG) {
-                            Log.d(TAG, "HTTP cache closed");
-                        }
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "closeCacheInternal - " + e);
-                }
-            }
-        }
-    }
-
-    /**
-    * Simple network connection check.
-    *
-    * @param context
-    */
-    private void checkConnection(Context context) {
-        final ConnectivityManager cm =
-                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
-        if (networkInfo == null || !networkInfo.isConnectedOrConnecting()) {
-            Toast.makeText(context, R.string.no_network_connection_toast, Toast.LENGTH_LONG).show();
-            Log.e(TAG, "checkConnection - no connection found");
-        }
-    }
-
-    /**
-     * The main process method, which will be called by the ImageWorker in the AsyncTask background
-     * thread.
-     *
-     * @param data The data to load the bitmap, in this case, a regular http URL
-     * @return The downloaded and resized bitmap
-     */
-    private Bitmap processBitmap(String data) {
-        if (BuildConfig.DEBUG) {
-            Log.d(TAG, "processBitmap - " + data);
-        }
-
-        final String key = ImageCache.hashKeyForDisk(data);
-        FileDescriptor fileDescriptor = null;
-        FileInputStream fileInputStream = null;
-        DiskLruCache.Snapshot snapshot;
-        synchronized (mHttpDiskCacheLock) {
-            // Wait for disk cache to initialize
-            while (mHttpDiskCacheStarting) {
-                try {
-                    mHttpDiskCacheLock.wait();
-                } catch (InterruptedException e) {}
-            }
-
-            if (mHttpDiskCache != null) {
-                try {
-                    snapshot = mHttpDiskCache.get(key);
-                    if (snapshot == null) {
-                        if (BuildConfig.DEBUG) {
-                            Log.d(TAG, "processBitmap, not found in http cache, downloading...");
-                        }
-                        DiskLruCache.Editor editor = mHttpDiskCache.edit(key);
-                        if (editor != null) {
-                            if (downloadUrlToStream(data,
-                                    editor.newOutputStream(DISK_CACHE_INDEX))) {
-                                editor.commit();
-                            } else {
-                                editor.abort();
-                            }
-                        }
-                        snapshot = mHttpDiskCache.get(key);
-                    }
-                    if (snapshot != null) {
-                        fileInputStream =
-                                (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
-                        fileDescriptor = fileInputStream.getFD();
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "processBitmap - " + e);
-                } catch (IllegalStateException e) {
-                    Log.e(TAG, "processBitmap - " + e);
-                } finally {
-                    if (fileDescriptor == null && fileInputStream != null) {
-                        try {
-                            fileInputStream.close();
-                        } catch (IOException e) {}
-                    }
-                }
-            }
-        }
-
-        Bitmap bitmap = null;
-        if (fileDescriptor != null) {
-            bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth,
-                    mImageHeight, getImageCache());
-        }
-        if (fileInputStream != null) {
-            try {
-                fileInputStream.close();
-            } catch (IOException e) {}
-        }
-        return bitmap;
-    }
-
-    @Override
-    protected Bitmap processBitmap(Object data) {
-        return processBitmap(String.valueOf(data));
-    }
-
-    /**
-     * Download a bitmap from a URL and write the content to an output stream.
-     *
-     * @param urlString The URL to fetch
-     * @return true if successful, false otherwise
-     */
-    public boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
-        disableConnectionReuseIfNecessary();
-        HttpURLConnection urlConnection = null;
-        BufferedOutputStream out = null;
-        BufferedInputStream in = null;
-
-        try {
-            final URL url = new URL(urlString);
-            urlConnection = (HttpURLConnection) url.openConnection();
-            in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
-            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
-
-            int b;
-            while ((b = in.read()) != -1) {
-                out.write(b);
-            }
-            return true;
-        } catch (final IOException e) {
-            Log.e(TAG, "Error in downloadBitmap - " + e);
-        } finally {
-            if (urlConnection != null) {
-                urlConnection.disconnect();
-            }
-            try {
-                if (out != null) {
-                    out.close();
-                }
-                if (in != null) {
-                    in.close();
-                }
-            } catch (final IOException e) {}
-        }
-        return false;
-    }
-
-    /**
-     * Workaround for bug pre-Froyo, see here for more info:
-     * http://android-developers.blogspot.com/2011/09/androids-http-clients.html
-     */
-    public static void disableConnectionReuseIfNecessary() {
-        // HTTP connection reuse which was buggy pre-froyo
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
-            System.setProperty("http.keepAlive", "false");
-        }
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java
deleted file mode 100644
index e8ce4e1..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.util;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Build;
-import android.util.Log;
-
-import com.example.android.bitmapfun.BuildConfig;
-
-import java.io.FileDescriptor;
-
-/**
- * A simple subclass of {@link ImageWorker} that resizes images from resources given a target width
- * and height. Useful for when the input images might be too large to simply load directly into
- * memory.
- */
-public class ImageResizer extends ImageWorker {
-    private static final String TAG = "ImageResizer";
-    protected int mImageWidth;
-    protected int mImageHeight;
-
-    /**
-     * Initialize providing a single target image size (used for both width and height);
-     *
-     * @param context
-     * @param imageWidth
-     * @param imageHeight
-     */
-    public ImageResizer(Context context, int imageWidth, int imageHeight) {
-        super(context);
-        setImageSize(imageWidth, imageHeight);
-    }
-
-    /**
-     * Initialize providing a single target image size (used for both width and height);
-     *
-     * @param context
-     * @param imageSize
-     */
-    public ImageResizer(Context context, int imageSize) {
-        super(context);
-        setImageSize(imageSize);
-    }
-
-    /**
-     * Set the target image width and height.
-     *
-     * @param width
-     * @param height
-     */
-    public void setImageSize(int width, int height) {
-        mImageWidth = width;
-        mImageHeight = height;
-    }
-
-    /**
-     * Set the target image size (width and height will be the same).
-     *
-     * @param size
-     */
-    public void setImageSize(int size) {
-        setImageSize(size, size);
-    }
-
-    /**
-     * The main processing method. This happens in a background task. In this case we are just
-     * sampling down the bitmap and returning it from a resource.
-     *
-     * @param resId
-     * @return
-     */
-    private Bitmap processBitmap(int resId) {
-        if (BuildConfig.DEBUG) {
-            Log.d(TAG, "processBitmap - " + resId);
-        }
-        return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,
-                mImageHeight, getImageCache());
-    }
-
-    @Override
-    protected Bitmap processBitmap(Object data) {
-        return processBitmap(Integer.parseInt(String.valueOf(data)));
-    }
-
-    /**
-     * Decode and sample down a bitmap from resources to the requested width and height.
-     *
-     * @param res The resources object containing the image data
-     * @param resId The resource id of the image data
-     * @param reqWidth The requested width of the resulting bitmap
-     * @param reqHeight The requested height of the resulting bitmap
-     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
-     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
-     *         that are equal to or greater than the requested width and height
-     */
-    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
-            int reqWidth, int reqHeight, ImageCache cache) {
-
-        // First decode with inJustDecodeBounds=true to check dimensions
-        final BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inJustDecodeBounds = true;
-        BitmapFactory.decodeResource(res, resId, options);
-
-        // Calculate inSampleSize
-        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
-
-        // If we're running on Honeycomb or newer, try to use inBitmap
-        if (Utils.hasHoneycomb()) {
-            addInBitmapOptions(options, cache);
-        }
-
-        // Decode bitmap with inSampleSize set
-        options.inJustDecodeBounds = false;
-        return BitmapFactory.decodeResource(res, resId, options);
-    }
-
-    /**
-     * Decode and sample down a bitmap from a file to the requested width and height.
-     *
-     * @param filename The full path of the file to decode
-     * @param reqWidth The requested width of the resulting bitmap
-     * @param reqHeight The requested height of the resulting bitmap
-     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
-     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
-     *         that are equal to or greater than the requested width and height
-     */
-    public static Bitmap decodeSampledBitmapFromFile(String filename,
-            int reqWidth, int reqHeight, ImageCache cache) {
-
-        // First decode with inJustDecodeBounds=true to check dimensions
-        final BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inJustDecodeBounds = true;
-        BitmapFactory.decodeFile(filename, options);
-
-        // Calculate inSampleSize
-        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
-
-        // If we're running on Honeycomb or newer, try to use inBitmap
-        if (Utils.hasHoneycomb()) {
-            addInBitmapOptions(options, cache);
-        }
-
-        // Decode bitmap with inSampleSize set
-        options.inJustDecodeBounds = false;
-        return BitmapFactory.decodeFile(filename, options);
-    }
-
-    /**
-     * Decode and sample down a bitmap from a file input stream to the requested width and height.
-     *
-     * @param fileDescriptor The file descriptor to read from
-     * @param reqWidth The requested width of the resulting bitmap
-     * @param reqHeight The requested height of the resulting bitmap
-     * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap
-     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
-     *         that are equal to or greater than the requested width and height
-     */
-    public static Bitmap decodeSampledBitmapFromDescriptor(
-            FileDescriptor fileDescriptor, int reqWidth, int reqHeight, ImageCache cache) {
-
-        // First decode with inJustDecodeBounds=true to check dimensions
-        final BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inJustDecodeBounds = true;
-        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
-
-        // Calculate inSampleSize
-        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
-
-        // Decode bitmap with inSampleSize set
-        options.inJustDecodeBounds = false;
-
-        // If we're running on Honeycomb or newer, try to use inBitmap
-        if (Utils.hasHoneycomb()) {
-            addInBitmapOptions(options, cache);
-        }
-
-        return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
-    }
-
-    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-    private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
-        // inBitmap only works with mutable bitmaps so force the decoder to
-        // return mutable bitmaps.
-        options.inMutable = true;
-
-        if (cache != null) {
-            // Try and find a bitmap to use for inBitmap
-            Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
-
-            if (inBitmap != null) {
-                options.inBitmap = inBitmap;
-            }
-        }
-    }
-
-    /**
-     * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
-     * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
-     * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
-     * having a width and height equal to or larger than the requested width and height.
-     *
-     * @param options An options object with out* params already populated (run through a decode*
-     *            method with inJustDecodeBounds==true
-     * @param reqWidth The requested width of the resulting bitmap
-     * @param reqHeight The requested height of the resulting bitmap
-     * @return The value to be used for inSampleSize
-     */
-    public static int calculateInSampleSize(BitmapFactory.Options options,
-            int reqWidth, int reqHeight) {
-        // Raw height and width of image
-        final int height = options.outHeight;
-        final int width = options.outWidth;
-        int inSampleSize = 1;
-
-        if (height > reqHeight || width > reqWidth) {
-
-            final int halfHeight = height / 2;
-            final int halfWidth = width / 2;
-
-            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
-            // height and width larger than the requested height and width.
-            while ((halfHeight / inSampleSize) > reqHeight
-                    && (halfWidth / inSampleSize) > reqWidth) {
-                inSampleSize *= 2;
-            }
-
-            // This offers some additional logic in case the image has a strange
-            // aspect ratio. For example, a panorama may have a much larger
-            // width than height. In these cases the total pixels might still
-            // end up being too large to fit comfortably in memory, so we should
-            // be more aggressive with sample down the image (=larger inSampleSize).
-
-            long totalPixels = width * height / inSampleSize;
-
-            // Anything more than 2x the requested pixels we'll sample down further
-            final long totalReqPixelsCap = reqWidth * reqHeight * 2;
-
-            while (totalPixels > totalReqPixelsCap) {
-                inSampleSize *= 2;
-                totalPixels /= 2;
-            }
-        }
-        return inSampleSize;
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java
deleted file mode 100644
index d260660..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.util;
-
-import com.example.android.bitmapfun.BuildConfig;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.util.Log;
-import android.widget.ImageView;
-
-import java.lang.ref.WeakReference;
-
-/**
- * This class wraps up completing some arbitrary long running work when loading a bitmap to an
- * ImageView. It handles things like using a memory and disk cache, running the work in a background
- * thread and setting a placeholder image.
- */
-public abstract class ImageWorker {
-    private static final String TAG = "ImageWorker";
-    private static final int FADE_IN_TIME = 200;
-
-    private ImageCache mImageCache;
-    private ImageCache.ImageCacheParams mImageCacheParams;
-    private Bitmap mLoadingBitmap;
-    private boolean mFadeInBitmap = true;
-    private boolean mExitTasksEarly = false;
-    protected boolean mPauseWork = false;
-    private final Object mPauseWorkLock = new Object();
-
-    protected Resources mResources;
-
-    private static final int MESSAGE_CLEAR = 0;
-    private static final int MESSAGE_INIT_DISK_CACHE = 1;
-    private static final int MESSAGE_FLUSH = 2;
-    private static final int MESSAGE_CLOSE = 3;
-
-    protected ImageWorker(Context context) {
-        mResources = context.getResources();
-    }
-
-    /**
-     * Load an image specified by the data parameter into an ImageView (override
-     * {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
-     * disk cache will be used if an {@link ImageCache} has been added using
-     * {@link ImageWorker#addImageCache(FragmentManager, ImageCache.ImageCacheParams)}. If the
-     * image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
-     * will be created to asynchronously load the bitmap.
-     *
-     * @param data The URL of the image to download.
-     * @param imageView The ImageView to bind the downloaded image to.
-     */
-    public void loadImage(Object data, ImageView imageView) {
-        if (data == null) {
-            return;
-        }
-
-        BitmapDrawable value = null;
-
-        if (mImageCache != null) {
-            value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
-        }
-
-        if (value != null) {
-            // Bitmap found in memory cache
-            imageView.setImageDrawable(value);
-        } else if (cancelPotentialWork(data, imageView)) {
-            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
-            final AsyncDrawable asyncDrawable =
-                    new AsyncDrawable(mResources, mLoadingBitmap, task);
-            imageView.setImageDrawable(asyncDrawable);
-
-            // NOTE: This uses a custom version of AsyncTask that has been pulled from the
-            // framework and slightly modified. Refer to the docs at the top of the class
-            // for more info on what was changed.
-            task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, data);
-        }
-    }
-
-    /**
-     * Set placeholder bitmap that shows when the the background thread is running.
-     *
-     * @param bitmap
-     */
-    public void setLoadingImage(Bitmap bitmap) {
-        mLoadingBitmap = bitmap;
-    }
-
-    /**
-     * Set placeholder bitmap that shows when the the background thread is running.
-     *
-     * @param resId
-     */
-    public void setLoadingImage(int resId) {
-        mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId);
-    }
-
-    /**
-     * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap
-     * caching.
-     * @param fragmentManager
-     * @param cacheParams The cache parameters to use for the image cache.
-     */
-    public void addImageCache(FragmentManager fragmentManager,
-            ImageCache.ImageCacheParams cacheParams) {
-        mImageCacheParams = cacheParams;
-        mImageCache = ImageCache.getInstance(fragmentManager, mImageCacheParams);
-        new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
-    }
-
-    /**
-     * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap
-     * caching.
-     * @param activity
-     * @param diskCacheDirectoryName See
-     * {@link ImageCache.ImageCacheParams#ImageCacheParams(Context, String)}.
-     */
-    public void addImageCache(FragmentActivity activity, String diskCacheDirectoryName) {
-        mImageCacheParams = new ImageCache.ImageCacheParams(activity, diskCacheDirectoryName);
-        mImageCache = ImageCache.getInstance(activity.getSupportFragmentManager(), mImageCacheParams);
-        new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
-    }
-
-    /**
-     * If set to true, the image will fade-in once it has been loaded by the background thread.
-     */
-    public void setImageFadeIn(boolean fadeIn) {
-        mFadeInBitmap = fadeIn;
-    }
-
-    public void setExitTasksEarly(boolean exitTasksEarly) {
-        mExitTasksEarly = exitTasksEarly;
-        setPauseWork(false);
-    }
-
-    /**
-     * Subclasses should override this to define any processing or work that must happen to produce
-     * the final bitmap. This will be executed in a background thread and be long running. For
-     * example, you could resize a large bitmap here, or pull down an image from the network.
-     *
-     * @param data The data to identify which image to process, as provided by
-     *            {@link ImageWorker#loadImage(Object, ImageView)}
-     * @return The processed bitmap
-     */
-    protected abstract Bitmap processBitmap(Object data);
-
-    /**
-     * @return The {@link ImageCache} object currently being used by this ImageWorker.
-     */
-    protected ImageCache getImageCache() {
-        return mImageCache;
-    }
-
-    /**
-     * Cancels any pending work attached to the provided ImageView.
-     * @param imageView
-     */
-    public static void cancelWork(ImageView imageView) {
-        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-        if (bitmapWorkerTask != null) {
-            bitmapWorkerTask.cancel(true);
-            if (BuildConfig.DEBUG) {
-                final Object bitmapData = bitmapWorkerTask.data;
-                Log.d(TAG, "cancelWork - cancelled work for " + bitmapData);
-            }
-        }
-    }
-
-    /**
-     * Returns true if the current work has been canceled or if there was no work in
-     * progress on this image view.
-     * Returns false if the work in progress deals with the same data. The work is not
-     * stopped in that case.
-     */
-    public static boolean cancelPotentialWork(Object data, ImageView imageView) {
-        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
-        if (bitmapWorkerTask != null) {
-            final Object bitmapData = bitmapWorkerTask.data;
-            if (bitmapData == null || !bitmapData.equals(data)) {
-                bitmapWorkerTask.cancel(true);
-                if (BuildConfig.DEBUG) {
-                    Log.d(TAG, "cancelPotentialWork - cancelled work for " + data);
-                }
-            } else {
-                // The same work is already in progress.
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * @param imageView Any imageView
-     * @return Retrieve the currently active work task (if any) associated with this imageView.
-     * null if there is no such task.
-     */
-    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
-        if (imageView != null) {
-            final Drawable drawable = imageView.getDrawable();
-            if (drawable instanceof AsyncDrawable) {
-                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
-                return asyncDrawable.getBitmapWorkerTask();
-            }
-        }
-        return null;
-    }
-
-    /**
-     * The actual AsyncTask that will asynchronously process the image.
-     */
-    private class BitmapWorkerTask extends AsyncTask<Object, Void, BitmapDrawable> {
-        private Object data;
-        private final WeakReference<ImageView> imageViewReference;
-
-        public BitmapWorkerTask(ImageView imageView) {
-            imageViewReference = new WeakReference<ImageView>(imageView);
-        }
-
-        /**
-         * Background processing.
-         */
-        @Override
-        protected BitmapDrawable doInBackground(Object... params) {
-            if (BuildConfig.DEBUG) {
-                Log.d(TAG, "doInBackground - starting work");
-            }
-
-            data = params[0];
-            final String dataString = String.valueOf(data);
-            Bitmap bitmap = null;
-            BitmapDrawable drawable = null;
-
-            // Wait here if work is paused and the task is not cancelled
-            synchronized (mPauseWorkLock) {
-                while (mPauseWork && !isCancelled()) {
-                    try {
-                        mPauseWorkLock.wait();
-                    } catch (InterruptedException e) {}
-                }
-            }
-
-            // If the image cache is available and this task has not been cancelled by another
-            // thread and the ImageView that was originally bound to this task is still bound back
-            // to this task and our "exit early" flag is not set then try and fetch the bitmap from
-            // the cache
-            if (mImageCache != null && !isCancelled() && getAttachedImageView() != null
-                    && !mExitTasksEarly) {
-                bitmap = mImageCache.getBitmapFromDiskCache(dataString);
-            }
-
-            // If the bitmap was not found in the cache and this task has not been cancelled by
-            // another thread and the ImageView that was originally bound to this task is still
-            // bound back to this task and our "exit early" flag is not set, then call the main
-            // process method (as implemented by a subclass)
-            if (bitmap == null && !isCancelled() && getAttachedImageView() != null
-                    && !mExitTasksEarly) {
-                bitmap = processBitmap(params[0]);
-            }
-
-            // If the bitmap was processed and the image cache is available, then add the processed
-            // bitmap to the cache for future use. Note we don't check if the task was cancelled
-            // here, if it was, and the thread is still running, we may as well add the processed
-            // bitmap to our cache as it might be used again in the future
-            if (bitmap != null) {
-                if (Utils.hasHoneycomb()) {
-                    // Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
-                    drawable = new BitmapDrawable(mResources, bitmap);
-                } else {
-                    // Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
-                    // which will recycle automagically
-                    drawable = new RecyclingBitmapDrawable(mResources, bitmap);
-                }
-
-                if (mImageCache != null) {
-                    mImageCache.addBitmapToCache(dataString, drawable);
-                }
-            }
-
-            if (BuildConfig.DEBUG) {
-                Log.d(TAG, "doInBackground - finished work");
-            }
-
-            return drawable;
-        }
-
-        /**
-         * Once the image is processed, associates it to the imageView
-         */
-        @Override
-        protected void onPostExecute(BitmapDrawable value) {
-            // if cancel was called on this task or the "exit early" flag is set then we're done
-            if (isCancelled() || mExitTasksEarly) {
-                value = null;
-            }
-
-            final ImageView imageView = getAttachedImageView();
-            if (value != null && imageView != null) {
-                if (BuildConfig.DEBUG) {
-                    Log.d(TAG, "onPostExecute - setting bitmap");
-                }
-                setImageDrawable(imageView, value);
-            }
-        }
-
-        @Override
-        protected void onCancelled(BitmapDrawable value) {
-            super.onCancelled(value);
-            synchronized (mPauseWorkLock) {
-                mPauseWorkLock.notifyAll();
-            }
-        }
-
-        /**
-         * Returns the ImageView associated with this task as long as the ImageView's task still
-         * points to this task as well. Returns null otherwise.
-         */
-        private ImageView getAttachedImageView() {
-            final ImageView imageView = imageViewReference.get();
-            final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
-
-            if (this == bitmapWorkerTask) {
-                return imageView;
-            }
-
-            return null;
-        }
-    }
-
-    /**
-     * A custom Drawable that will be attached to the imageView while the work is in progress.
-     * Contains a reference to the actual worker task, so that it can be stopped if a new binding is
-     * required, and makes sure that only the last started worker process can bind its result,
-     * independently of the finish order.
-     */
-    private static class AsyncDrawable extends BitmapDrawable {
-        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
-
-        public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
-            super(res, bitmap);
-            bitmapWorkerTaskReference =
-                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
-        }
-
-        public BitmapWorkerTask getBitmapWorkerTask() {
-            return bitmapWorkerTaskReference.get();
-        }
-    }
-
-    /**
-     * Called when the processing is complete and the final drawable should be 
-     * set on the ImageView.
-     *
-     * @param imageView
-     * @param drawable
-     */
-    private void setImageDrawable(ImageView imageView, Drawable drawable) {
-        if (mFadeInBitmap) {
-            // Transition drawable with a transparent drawable and the final drawable
-            final TransitionDrawable td =
-                    new TransitionDrawable(new Drawable[] {
-                            new ColorDrawable(android.R.color.transparent),
-                            drawable
-                    });
-            // Set background to loading bitmap
-            imageView.setBackgroundDrawable(
-                    new BitmapDrawable(mResources, mLoadingBitmap));
-
-            imageView.setImageDrawable(td);
-            td.startTransition(FADE_IN_TIME);
-        } else {
-            imageView.setImageDrawable(drawable);
-        }
-    }
-
-    /**
-     * Pause any ongoing background work. This can be used as a temporary
-     * measure to improve performance. For example background work could
-     * be paused when a ListView or GridView is being scrolled using a
-     * {@link android.widget.AbsListView.OnScrollListener} to keep
-     * scrolling smooth.
-     * <p>
-     * If work is paused, be sure setPauseWork(false) is called again
-     * before your fragment or activity is destroyed (for example during
-     * {@link android.app.Activity#onPause()}), or there is a risk the
-     * background thread will never finish.
-     */
-    public void setPauseWork(boolean pauseWork) {
-        synchronized (mPauseWorkLock) {
-            mPauseWork = pauseWork;
-            if (!mPauseWork) {
-                mPauseWorkLock.notifyAll();
-            }
-        }
-    }
-
-    protected class CacheAsyncTask extends AsyncTask<Object, Void, Void> {
-
-        @Override
-        protected Void doInBackground(Object... params) {
-            switch ((Integer)params[0]) {
-                case MESSAGE_CLEAR:
-                    clearCacheInternal();
-                    break;
-                case MESSAGE_INIT_DISK_CACHE:
-                    initDiskCacheInternal();
-                    break;
-                case MESSAGE_FLUSH:
-                    flushCacheInternal();
-                    break;
-                case MESSAGE_CLOSE:
-                    closeCacheInternal();
-                    break;
-            }
-            return null;
-        }
-    }
-
-    protected void initDiskCacheInternal() {
-        if (mImageCache != null) {
-            mImageCache.initDiskCache();
-        }
-    }
-
-    protected void clearCacheInternal() {
-        if (mImageCache != null) {
-            mImageCache.clearCache();
-        }
-    }
-
-    protected void flushCacheInternal() {
-        if (mImageCache != null) {
-            mImageCache.flush();
-        }
-    }
-
-    protected void closeCacheInternal() {
-        if (mImageCache != null) {
-            mImageCache.close();
-            mImageCache = null;
-        }
-    }
-
-    public void clearCache() {
-        new CacheAsyncTask().execute(MESSAGE_CLEAR);
-    }
-
-    public void flushCache() {
-        new CacheAsyncTask().execute(MESSAGE_FLUSH);
-    }
-
-    public void closeCache() {
-        new CacheAsyncTask().execute(MESSAGE_CLOSE);
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java
deleted file mode 100644
index 2aae97f..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.bitmapfun.util;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.util.Log;
-
-import com.example.android.bitmapfun.BuildConfig;
-
-/**
- * A BitmapDrawable that keeps track of whether it is being displayed or cached.
- * When the drawable is no longer being displayed or cached,
- * {@link Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
- */
-public class RecyclingBitmapDrawable extends BitmapDrawable {
-
-    static final String LOG_TAG = "CountingBitmapDrawable";
-
-    private int mCacheRefCount = 0;
-    private int mDisplayRefCount = 0;
-
-    private boolean mHasBeenDisplayed;
-
-    public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
-        super(res, bitmap);
-    }
-
-    /**
-     * Notify the drawable that the displayed state has changed. Internally a
-     * count is kept so that the drawable knows when it is no longer being
-     * displayed.
-     *
-     * @param isDisplayed - Whether the drawable is being displayed or not
-     */
-    public void setIsDisplayed(boolean isDisplayed) {
-        synchronized (this) {
-            if (isDisplayed) {
-                mDisplayRefCount++;
-                mHasBeenDisplayed = true;
-            } else {
-                mDisplayRefCount--;
-            }
-        }
-
-        // Check to see if recycle() can be called
-        checkState();
-    }
-
-    /**
-     * Notify the drawable that the cache state has changed. Internally a count
-     * is kept so that the drawable knows when it is no longer being cached.
-     *
-     * @param isCached - Whether the drawable is being cached or not
-     */
-    public void setIsCached(boolean isCached) {
-        synchronized (this) {
-            if (isCached) {
-                mCacheRefCount++;
-            } else {
-                mCacheRefCount--;
-            }
-        }
-
-        // Check to see if recycle() can be called
-        checkState();
-    }
-
-    private synchronized void checkState() {
-        // If the drawable cache and display ref counts = 0, and this drawable
-        // has been displayed, then recycle
-        if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
-                && hasValidBitmap()) {
-            if (BuildConfig.DEBUG) {
-                Log.d(LOG_TAG, "No longer being used or cached so recycling. "
-                        + toString());
-            }
-
-            getBitmap().recycle();
-        }
-    }
-
-    private synchronized boolean hasValidBitmap() {
-        Bitmap bitmap = getBitmap();
-        return bitmap != null && !bitmap.isRecycled();
-    }
-
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java
deleted file mode 100644
index 31dc342..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.bitmapfun.util;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.os.StrictMode;
-
-import com.example.android.bitmapfun.ui.ImageDetailActivity;
-import com.example.android.bitmapfun.ui.ImageGridActivity;
-
-/**
- * Class containing some static utility methods.
- */
-public class Utils {
-    private Utils() {};
-
-
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public static void enableStrictMode() {
-        if (Utils.hasGingerbread()) {
-            StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
-                    new StrictMode.ThreadPolicy.Builder()
-                            .detectAll()
-                            .penaltyLog();
-            StrictMode.VmPolicy.Builder vmPolicyBuilder =
-                    new StrictMode.VmPolicy.Builder()
-                            .detectAll()
-                            .penaltyLog();
-
-            if (Utils.hasHoneycomb()) {
-                threadPolicyBuilder.penaltyFlashScreen();
-                vmPolicyBuilder
-                        .setClassInstanceLimit(ImageGridActivity.class, 1)
-                        .setClassInstanceLimit(ImageDetailActivity.class, 1);
-            }
-            StrictMode.setThreadPolicy(threadPolicyBuilder.build());
-            StrictMode.setVmPolicy(vmPolicyBuilder.build());
-        }
-    }
-
-    public static boolean hasFroyo() {
-        // Can use static final constants like FROYO, declared in later versions
-        // of the OS since they are inlined at compile time. This is guaranteed behavior.
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
-    }
-
-    public static boolean hasGingerbread() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
-    }
-
-    public static boolean hasHoneycomb() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
-    }
-
-    public static boolean hasHoneycombMR1() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1;
-    }
-
-    public static boolean hasJellyBean() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
-    }
-
-    public static boolean hasKitKat() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
-    }
-}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable/photogrid_list_selector.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/drawable/photogrid_list_selector.xml
deleted file mode 100644
index 19d8670..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable/photogrid_list_selector.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item android:state_pressed="true">
-        <shape>
-            <solid android:color="@color/grid_state_pressed" />
-        </shape>
-    </item>
-    <item android:state_focused="true">
-        <shape>
-            <solid android:color="@color/grid_state_focused" />
-        </shape>
-    </item>
-    <item android:drawable="@android:color/transparent" />
-
-</selector>
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml
deleted file mode 100644
index 6940357..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent" >
-
-    <ProgressBar
-        style="?android:attr/progressBarStyleLarge"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center" />
-
-    <com.example.android.bitmapfun.ui.RecyclingImageView
-        android:id="@+id/imageView"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:contentDescription="@string/imageview_description" />
-
-</FrameLayout>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml
deleted file mode 100644
index 0c64526..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<resources>
-
-    <style name="AppTheme" parent="@android:style/Theme.Holo">
-        <item name="android:windowActionBarOverlay">true</item>
-        <item name="android:windowBackground">@android:color/black</item>
-        <item name="android:actionBarStyle">@style/TranslucentDarkActionBar</item>
-    </style>
-
-    <style name="AppTheme.FullScreen" />
-
-    <style name="TranslucentDarkActionBar" parent="@android:style/Widget.Holo.ActionBar">
-        <item name="android:background">#99000000</item>
-    </style>
-
-    <style name="PhotoGridLayout">
-        <item name="android:drawSelectorOnTop">true</item>
-    </style>
-
-</resources>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml
deleted file mode 100644
index 7e4a4fe..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<resources>
-
-    <color name="grid_state_pressed">#BB7dbcd3</color>
-    <color name="grid_state_focused">#777dbcd3</color>
-
-</resources>
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml
deleted file mode 100644
index 60d540f..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<resources>
-
-    <dimen name="image_thumbnail_size">100dp</dimen>
-    <dimen name="image_thumbnail_spacing">1dp</dimen>
-    <dimen name="image_detail_pager_margin">80dp</dimen>
-
-</resources>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml
deleted file mode 100644
index 8108c23..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<resources>
-
-    <string name="app_name">BitmapFun</string>
-    <string name="app_description">This is a sample application for the Android Training class
-        &quot;Displaying Bitmaps Efficiently&quot;
-        (http://developer.android.com/training/displaying-bitmaps/display-bitmap.html). It is not
-        designed to be a full reference application but to demonstrate the concepts discussed in
-        training course.</string>
-    <string name="clear_cache_menu">Clear Caches</string>
-    <string name="clear_cache_complete_toast">Caches have been cleared</string>
-    <string name="imageview_description">Image Thumbnail</string>
-    <string name="no_network_connection_toast">No network connection found</string>
-
-</resources>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml
deleted file mode 100644
index 0f1a018..0000000
--- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2012 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<resources>
-
-    <style name="AppTheme" parent="android:Theme" />
-
-    <style name="AppTheme.FullScreen" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen" />
-
-    <style name="PhotoGridLayout">
-        <item name="android:drawSelectorOnTop">true</item>
-        <item name="android:listSelector">@drawable/photogrid_list_selector</item>
-    </style>
-
-</resources>
\ No newline at end of file
diff --git a/samples/training/bitmapfun/README b/samples/training/bitmapfun/README
deleted file mode 100644
index a0a192f..0000000
--- a/samples/training/bitmapfun/README
+++ /dev/null
@@ -1,8 +0,0 @@
-This is an Android Studio project:
-http://developer.android.com/sdk/installing/studio.html
-
-First copy local.properties.sample to local.properties and set your SDK path.
-
-Then import the project into Android Studio:
-File -> Import Project -> Choose Directory -> Import from external model ->
-    Gradle -> Use default gradle wrapper -> Finish
diff --git a/samples/training/bitmapfun/build.gradle b/samples/training/bitmapfun/build.gradle
deleted file mode 100644
index 495c503..0000000
--- a/samples/training/bitmapfun/build.gradle
+++ /dev/null
@@ -1 +0,0 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
diff --git a/samples/training/bitmapfun/local.properties.sample b/samples/training/bitmapfun/local.properties.sample
deleted file mode 100644
index 37317f4..0000000
--- a/samples/training/bitmapfun/local.properties.sample
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file should be copied to local.properties and path set to point
-# to your Android SDK.
-
-# Location of the SDK. This is only used by Gradle.
-# For customization when using a Version Control System, please read the
-# header note.
-sdk.dir=/usr/local/lib/android-sdk
\ No newline at end of file
diff --git a/samples/training/bitmapfun/settings.gradle b/samples/training/bitmapfun/settings.gradle
deleted file mode 100644
index 9f12781..0000000
--- a/samples/training/bitmapfun/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include ':BitmapFun'
diff --git a/scripts/app_engine_server/app.yaml.production b/scripts/app_engine_server/app.yaml.production
new file mode 100644
index 0000000..61bd51a
--- /dev/null
+++ b/scripts/app_engine_server/app.yaml.production
@@ -0,0 +1,16 @@
+application: androiddevdocs
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /gae_shell/static
+  static_dir: gae_shell/static
+  expiration: 1d
+
+- url: /gae_shell/.*
+  script: /gae_shell/shell.py
+  login: admin
+
+- url: .*
+  script: main.py
diff --git a/scripts/app_engine_server/redirects.yaml b/scripts/app_engine_server/redirects.yaml
index 106b64e..b7a90a5 100644
--- a/scripts/app_engine_server/redirects.yaml
+++ b/scripts/app_engine_server/redirects.yaml
@@ -303,6 +303,10 @@
 - src: /guide/appendix/install-location.html
   dst: /guide/topics/data/install-location.html
   type: permanent
+
+- src: /guide/appendix/g-app-intents.html
+  dst: /guide/components/intents-common.html
+  type: permanent
   comment: Redirect to new location
 
 # new one
@@ -547,7 +551,11 @@
   type: permanent
 
 - src: /resources/samples/.*
-  dst: /tools/samples/index.html
+  dst: /samples/index.html
+  type: permanent
+
+- src: /tools/samples/index.html
+  dst: /samples/index.html
   type: permanent
   comment: Redirect to new location
 
@@ -561,12 +569,6 @@
   type: permanent
   comment: Redirect to new location
 
-- src: /guide/index.html
-  dst: /guide/components/index.html
-  type: permanent
-  comment: Redirect to new location
-
-
 
 # ------------------- TRAINING -------------------
 
diff --git a/sdk/api-versions.xml b/sdk/api-versions.xml
index de035e8..3855e9d 100644
--- a/sdk/api-versions.xml
+++ b/sdk/api-versions.xml
@@ -12869,6 +12869,7 @@
 		<field name="EFFECT_TYPE_BASS_BOOST" since="18" />
 		<field name="EFFECT_TYPE_ENV_REVERB" since="18" />
 		<field name="EFFECT_TYPE_EQUALIZER" since="18" />
+		<field name="EFFECT_TYPE_LOUDNESS_ENHANCER" since="19" />
 		<field name="EFFECT_TYPE_NS" since="18" />
 		<field name="EFFECT_TYPE_PRESET_REVERB" since="18" />
 		<field name="EFFECT_TYPE_VIRTUALIZER" since="18" />
@@ -13027,7 +13028,7 @@
 	</class>
 	<class name="android/media/audiofx/LoudnessEnhancer" since="19">
 		<extends name="android/media/audiofx/AudioEffect" />
-		<method name="&lt;init>()V" />
+		<method name="&lt;init>(I)V" />
 		<method name="getTargetGain()F" />
 		<method name="setTargetGain(I)V" />
 		<field name="PARAM_TARGET_GAIN_MB" />
@@ -25504,10 +25505,8 @@
 		<method name="&lt;init>()V" />
 		<method name="beginDelayedTransition(Landroid/view/ViewGroup;)V" />
 		<method name="beginDelayedTransition(Landroid/view/ViewGroup;Landroid/transition/Transition;)V" />
-		<method name="getDefaultTransition()Landroid/transition/Transition;" />
 		<method name="go(Landroid/transition/Scene;)V" />
 		<method name="go(Landroid/transition/Scene;Landroid/transition/Transition;)V" />
-		<method name="setDefaultTransition(Landroid/transition/Transition;)V" />
 		<method name="setTransition(Landroid/transition/Scene;Landroid/transition/Scene;Landroid/transition/Transition;)V" />
 		<method name="setTransition(Landroid/transition/Scene;Landroid/transition/Transition;)V" />
 		<method name="transitionTo(Landroid/transition/Scene;)V" />
diff --git a/sdk/build_tools_source.prop_template b/sdk/build_tools_source.prop_template
index 5d62307..c9bfc2f 100644
--- a/sdk/build_tools_source.prop_template
+++ b/sdk/build_tools_source.prop_template
@@ -1,3 +1,3 @@
 Pkg.UserSrc=false
-Pkg.Revision=${PLATFORM_SDK_VERSION}.0.0
+Pkg.Revision=${PLATFORM_SDK_VERSION}.0.2
 
diff --git a/sdk/doc_source.prop_template b/sdk/doc_source.prop_template
index d3cdfd5..523d6bd 100644
--- a/sdk/doc_source.prop_template
+++ b/sdk/doc_source.prop_template
@@ -1,4 +1,4 @@
 Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=2
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
diff --git a/sdk/images_armeabi-v7a_source.prop_template b/sdk/images_armeabi-v7a_source.prop_template
index 86fc2a0..4f2daac 100644
--- a/sdk/images_armeabi-v7a_source.prop_template
+++ b/sdk/images_armeabi-v7a_source.prop_template
@@ -1,6 +1,6 @@
 Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
 Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=2
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
 SystemImage.Abi=armeabi-v7a
diff --git a/sdk/images_armeabi_source.prop_template b/sdk/images_armeabi_source.prop_template
index 8644d10..906e378 100644
--- a/sdk/images_armeabi_source.prop_template
+++ b/sdk/images_armeabi_source.prop_template
@@ -1,6 +1,6 @@
 Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
 Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=2
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
 SystemImage.Abi=armeabi
diff --git a/sdk/images_x86_source.prop_template b/sdk/images_x86_source.prop_template
index a587cd1..90024ea 100644
--- a/sdk/images_x86_source.prop_template
+++ b/sdk/images_x86_source.prop_template
@@ -1,6 +1,6 @@
 Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
 Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=2
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
 SystemImage.Abi=x86
diff --git a/sdk/plat_tools_source.prop_template b/sdk/plat_tools_source.prop_template
index 5d62307..b83af7f 100644
--- a/sdk/plat_tools_source.prop_template
+++ b/sdk/plat_tools_source.prop_template
@@ -1,3 +1,3 @@
 Pkg.UserSrc=false
-Pkg.Revision=${PLATFORM_SDK_VERSION}.0.0
+Pkg.Revision=${PLATFORM_SDK_VERSION}.0.1
 
diff --git a/sdk/platform_source.prop_template b/sdk/platform_source.prop_template
index 7ecb1e2..09a2d81 100644
--- a/sdk/platform_source.prop_template
+++ b/sdk/platform_source.prop_template
@@ -2,7 +2,7 @@
 Pkg.UserSrc=false
 Platform.Version=${PLATFORM_VERSION}
 Platform.CodeName=KitKat
-Pkg.Revision=1
+Pkg.Revision=2
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
 Layoutlib.Api=10
diff --git a/sdk/source_source.prop_template b/sdk/source_source.prop_template
index d3cdfd5..523d6bd 100644
--- a/sdk/source_source.prop_template
+++ b/sdk/source_source.prop_template
@@ -1,4 +1,4 @@
 Pkg.UserSrc=false
-Pkg.Revision=1
+Pkg.Revision=2
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
diff --git a/sdk/support_source.prop_template b/sdk/support_source.prop_template
index ba7e6fb..20022d2 100644
--- a/sdk/support_source.prop_template
+++ b/sdk/support_source.prop_template
@@ -1,5 +1,5 @@
 Pkg.UserSrc=false
-Pkg.Revision=${PLATFORM_SDK_VERSION}
+Pkg.Revision=${PLATFORM_SDK_VERSION}.2.0
 Extra.Vendor=android
 Extra.VendorId=android
 Extra.VendorDisplay=Android