Retroactively add EffectiveNavigation and TabCompat sample code.

This sample code was launched with the Implementing Effective Navigation and
Creating Backward-Compatible UIs training classes, but the source was never
added to AOSP. This retroactively adds the source, unmodified.

Change-Id: If6face5a0548107f7fd273e466b1ced2790f4f3a
diff --git a/samples/training/EffectiveNavigation/AndroidManifest.xml b/samples/training/EffectiveNavigation/AndroidManifest.xml
new file mode 100755
index 0000000..b1e4e11
--- /dev/null
+++ b/samples/training/EffectiveNavigation/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<!--
+  Copyright 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.effectivenavigation"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="14" />
+
+    <application android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@android:style/Theme.Holo.Light.DarkActionBar">
+
+        <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>
+
+        <activity android:name=".CollectionDemoActivity" android:label="@string/demo_collection" />
+
+    </application>
+</manifest>
diff --git a/samples/training/EffectiveNavigation/res/drawable-hdpi/ic_launcher.png b/samples/training/EffectiveNavigation/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..d2dae4c
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/EffectiveNavigation/res/drawable-ldpi/ic_launcher.png b/samples/training/EffectiveNavigation/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..d30b965
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/EffectiveNavigation/res/drawable-mdpi/ic_launcher.png b/samples/training/EffectiveNavigation/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6625345
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/EffectiveNavigation/res/drawable-xhdpi/ic_launcher.png b/samples/training/EffectiveNavigation/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..67331ce
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/EffectiveNavigation/res/layout/activity_collection_demo.xml b/samples/training/EffectiveNavigation/res/layout/activity_collection_demo.xml
new file mode 100755
index 0000000..e34f471
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/layout/activity_collection_demo.xml
@@ -0,0 +1,35 @@
+<!--
+  Copyright 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.
+  -->
+
+<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pager"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <!--
+    This title strip will display the currently visible page title, as well as the page
+    titles for adjacent pages.
+    -->
+    <android.support.v4.view.PagerTitleStrip android:id="@+id/pager_title_strip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:background="#33b5e5"
+        android:textColor="#fff"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp" />
+
+</android.support.v4.view.ViewPager>
diff --git a/samples/training/EffectiveNavigation/res/layout/activity_main.xml b/samples/training/EffectiveNavigation/res/layout/activity_main.xml
new file mode 100755
index 0000000..339b592
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/layout/activity_main.xml
@@ -0,0 +1,20 @@
+<!--
+  Copyright 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.
+  -->
+
+<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pager"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/samples/training/EffectiveNavigation/res/layout/fragment_collection_object.xml b/samples/training/EffectiveNavigation/res/layout/fragment_collection_object.xml
new file mode 100644
index 0000000..945bf06
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/layout/fragment_collection_object.xml
@@ -0,0 +1,23 @@
+<!--
+  Copyright 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.
+  -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:textSize="128sp"
+    android:padding="32dp" />
diff --git a/samples/training/EffectiveNavigation/res/layout/fragment_section_dummy.xml b/samples/training/EffectiveNavigation/res/layout/fragment_section_dummy.xml
new file mode 100644
index 0000000..1c67885
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/layout/fragment_section_dummy.xml
@@ -0,0 +1,23 @@
+<!--
+  Copyright 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.
+  -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:textSize="24sp"
+    android:padding="32dp" />
diff --git a/samples/training/EffectiveNavigation/res/layout/fragment_section_launchpad.xml b/samples/training/EffectiveNavigation/res/layout/fragment_section_launchpad.xml
new file mode 100644
index 0000000..186b2f2
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/layout/fragment_section_launchpad.xml
@@ -0,0 +1,39 @@
+<!--
+  Copyright 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">
+
+    <LinearLayout android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center">
+
+        <Button android:id="@+id/demo_collection_button"
+            android:layout_width="300dp"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dp"
+            android:text="@string/demo_collection"/>
+
+        <Button android:id="@+id/demo_external_activity"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/demo_external_activity" />
+
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/samples/training/EffectiveNavigation/res/values/strings.xml b/samples/training/EffectiveNavigation/res/values/strings.xml
new file mode 100755
index 0000000..4ea2dbb
--- /dev/null
+++ b/samples/training/EffectiveNavigation/res/values/strings.xml
@@ -0,0 +1,24 @@
+<!--
+  Copyright 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">Effective Navigation</string>
+
+    <string name="dummy_section_text">Section %1d is just a dummy section.</string>
+
+    <string name="demo_external_activity">Demo External Activity</string>
+    <string name="demo_collection">Demo Collection</string>
+</resources>
diff --git a/samples/training/EffectiveNavigation/src/com/example/android/effectivenavigation/CollectionDemoActivity.java b/samples/training/EffectiveNavigation/src/com/example/android/effectivenavigation/CollectionDemoActivity.java
new file mode 100755
index 0000000..641d1a4
--- /dev/null
+++ b/samples/training/EffectiveNavigation/src/com/example/android/effectivenavigation/CollectionDemoActivity.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 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.effectivenavigation;
+
+import android.app.ActionBar;
+import android.content.Intent;
+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.app.TaskStackBuilder;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class CollectionDemoActivity extends FragmentActivity {
+
+    /**
+     * The {@link android.support.v4.view.PagerAdapter} that will provide fragments representing
+     * each object in a collection. We use a {@link android.support.v4.app.FragmentStatePagerAdapter}
+     * derivative, which will destroy and re-create fragments as needed, saving and restoring their
+     * state in the process. This is important to conserve memory and is a best practice when
+     * allowing navigation between objects in a potentially large collection.
+     */
+    DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
+
+    /**
+     * The {@link android.support.v4.view.ViewPager} that will display the object collection.
+     */
+    ViewPager mViewPager;
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_collection_demo);
+
+        // Create an adapter that when requested, will return a fragment representing an object in
+        // the collection.
+        // 
+        // ViewPager and its adapters use support library fragments, so we must use
+        // getSupportFragmentManager.
+        mDemoCollectionPagerAdapter = new DemoCollectionPagerAdapter(getSupportFragmentManager());
+
+        // Set up action bar.
+        final ActionBar actionBar = getActionBar();
+
+        // Specify that the Home button should show an "Up" caret, indicating that touching the
+        // button will take the user one step up in the application's hierarchy.
+        actionBar.setDisplayHomeAsUpEnabled(true);
+
+        // Set up the ViewPager, attaching the adapter.
+        mViewPager = (ViewPager) findViewById(R.id.pager);
+        mViewPager.setAdapter(mDemoCollectionPagerAdapter);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                // This is called when the Home (Up) button is pressed in the action bar.
+                // Create a simple intent that starts the hierarchical parent activity and
+                // use NavUtils in the Support Package to ensure proper handling of Up.
+                Intent upIntent = new Intent(this, MainActivity.class);
+                if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
+                    // This activity is not part of the application's task, so create a new task
+                    // with a synthesized back stack.
+                    TaskStackBuilder.from(this)
+                            // If there are ancestor activities, they should be added here.
+                            .addNextIntent(upIntent)
+                            .startActivities();
+                    finish();
+                } else {
+                    // This activity is part of the application's task, so simply
+                    // navigate up to the hierarchical parent activity.
+                    NavUtils.navigateUpTo(this, upIntent);
+                }
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * A {@link android.support.v4.app.FragmentStatePagerAdapter} that returns a fragment
+     * representing an object in the collection.
+     */
+    public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
+
+        public DemoCollectionPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public Fragment getItem(int i) {
+            Fragment fragment = new DemoObjectFragment();
+            Bundle args = new Bundle();
+            args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1); // Our object is just an integer :-P
+            fragment.setArguments(args);
+            return fragment;
+        }
+
+        @Override
+        public int getCount() {
+            // For this contrived example, we have a 100-object collection.
+            return 100;
+        }
+
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return "OBJECT " + (position + 1);
+        }
+    }
+
+    /**
+     * A dummy fragment representing a section of the app, but that simply displays dummy text.
+     */
+    public static class DemoObjectFragment extends Fragment {
+
+        public static final String ARG_OBJECT = "object";
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
+            Bundle args = getArguments();
+            ((TextView) rootView.findViewById(android.R.id.text1)).setText(
+                    Integer.toString(args.getInt(ARG_OBJECT)));
+            return rootView;
+        }
+    }
+}
diff --git a/samples/training/EffectiveNavigation/src/com/example/android/effectivenavigation/MainActivity.java b/samples/training/EffectiveNavigation/src/com/example/android/effectivenavigation/MainActivity.java
new file mode 100755
index 0000000..a560ee4
--- /dev/null
+++ b/samples/training/EffectiveNavigation/src/com/example/android/effectivenavigation/MainActivity.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 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.effectivenavigation;
+
+import android.app.ActionBar;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+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.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
+
+    /**
+     * The {@link android.support.v4.view.PagerAdapter} that will provide fragments for each of the
+     * three primary sections of the app. We use a {@link android.support.v4.app.FragmentPagerAdapter}
+     * derivative, which will keep every loaded fragment in memory. If this becomes too memory
+     * intensive, it may be best to switch to a {@link android.support.v4.app.FragmentStatePagerAdapter}.
+     */
+    AppSectionsPagerAdapter mAppSectionsPagerAdapter;
+
+    /**
+     * The {@link ViewPager} that will display the three primary sections of the app, one at a
+     * time.
+     */
+    ViewPager mViewPager;
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        // Create the adapter that will return a fragment for each of the three primary sections
+        // of the app.
+        mAppSectionsPagerAdapter = new AppSectionsPagerAdapter(getSupportFragmentManager());
+
+        // Set up the action bar.
+        final ActionBar actionBar = getActionBar();
+
+        // Specify that the Home/Up button should not be enabled, since there is no hierarchical
+        // parent.
+        actionBar.setHomeButtonEnabled(false);
+
+        // Specify that we will be displaying tabs in the action bar.
+        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+        // Set up the ViewPager, attaching the adapter and setting up a listener for when the
+        // user swipes between sections.
+        mViewPager = (ViewPager) findViewById(R.id.pager);
+        mViewPager.setAdapter(mAppSectionsPagerAdapter);
+        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+            @Override
+            public void onPageSelected(int position) {
+                // When swiping between different app sections, select the corresponding tab.
+                // We can also use ActionBar.Tab#select() to do this if we have a reference to the
+                // Tab.
+                actionBar.setSelectedNavigationItem(position);
+            }
+        });
+
+        // For each of the sections in the app, add a tab to the action bar.
+        for (int i = 0; i < mAppSectionsPagerAdapter.getCount(); i++) {
+            // Create a tab with text corresponding to the page title defined by the adapter.
+            // Also specify this Activity object, which implements the TabListener interface, as the
+            // listener for when this tab is selected.
+            actionBar.addTab(
+                    actionBar.newTab()
+                            .setText(mAppSectionsPagerAdapter.getPageTitle(i))
+                            .setTabListener(this));
+        }
+    }
+
+    @Override
+    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
+    }
+
+    @Override
+    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
+        // When the given tab is selected, switch to the corresponding page in the ViewPager.
+        mViewPager.setCurrentItem(tab.getPosition());
+    }
+
+    @Override
+    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
+    }
+
+    /**
+     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to one of the primary
+     * sections of the app.
+     */
+    public static class AppSectionsPagerAdapter extends FragmentPagerAdapter {
+
+        public AppSectionsPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public Fragment getItem(int i) {
+            switch (i) {
+                case 0:
+                    // The first section of the app is the most interesting -- it offers
+                    // a launchpad into the other demonstrations in this example application.
+                    return new LaunchpadSectionFragment();
+
+                default:
+                    // The other sections of the app are dummy placeholders.
+                    Fragment fragment = new DummySectionFragment();
+                    Bundle args = new Bundle();
+                    args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, i + 1);
+                    fragment.setArguments(args);
+                    return fragment;
+            }
+        }
+
+        @Override
+        public int getCount() {
+            return 3;
+        }
+
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return "Section " + (position + 1);
+        }
+    }
+
+    /**
+     * A fragment that launches other parts of the demo application.
+     */
+    public static class LaunchpadSectionFragment extends Fragment {
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View rootView = inflater.inflate(R.layout.fragment_section_launchpad, container, false);
+
+            // Demonstration of a collection-browsing activity.
+            rootView.findViewById(R.id.demo_collection_button)
+                    .setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View view) {
+                            Intent intent = new Intent(getActivity(), CollectionDemoActivity.class);
+                            startActivity(intent);
+                        }
+                    });
+
+            // Demonstration of navigating to external activities.
+            rootView.findViewById(R.id.demo_external_activity)
+                    .setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View view) {
+                            // Create an intent that asks the user to pick a photo, but using
+                            // FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, ensures that relaunching
+                            // the application from the device home screen does not return
+                            // to the external activity.
+                            Intent externalActivityIntent = new Intent(Intent.ACTION_PICK);
+                            externalActivityIntent.setType("image/*");
+                            externalActivityIntent.addFlags(
+                                    Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+                            startActivity(externalActivityIntent);
+                        }
+                    });
+
+            return rootView;
+        }
+    }
+
+    /**
+     * A dummy fragment representing a section of the app, but that simply displays dummy text.
+     */
+    public static class DummySectionFragment extends Fragment {
+
+        public static final String ARG_SECTION_NUMBER = "section_number";
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View rootView = inflater.inflate(R.layout.fragment_section_dummy, container, false);
+            Bundle args = getArguments();
+            ((TextView) rootView.findViewById(android.R.id.text1)).setText(
+                    getString(R.string.dummy_section_text, args.getInt(ARG_SECTION_NUMBER)));
+            return rootView;
+        }
+    }
+}
diff --git a/samples/training/TabCompat/AndroidManifest.xml b/samples/training/TabCompat/AndroidManifest.xml
new file mode 100644
index 0000000..78a2f6c
--- /dev/null
+++ b/samples/training/TabCompat/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<!--
+  Copyright 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.tabcompat"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="14" />
+
+    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+        <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/training/TabCompat/res/drawable-hdpi-v11/ic_tab_photos.png b/samples/training/TabCompat/res/drawable-hdpi-v11/ic_tab_photos.png
new file mode 100644
index 0000000..bfe4878
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi-v11/ic_tab_photos.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-hdpi-v11/ic_tab_videos.png b/samples/training/TabCompat/res/drawable-hdpi-v11/ic_tab_videos.png
new file mode 100644
index 0000000..842bc68
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi-v11/ic_tab_videos.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-hdpi/ic_launcher.png b/samples/training/TabCompat/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..f1bd321
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-hdpi/ic_tab_photos_selected.png b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_photos_selected.png
new file mode 100755
index 0000000..2da28bd
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_photos_selected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-hdpi/ic_tab_photos_unselected.png b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_photos_unselected.png
new file mode 100755
index 0000000..66aa430
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_photos_unselected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-hdpi/ic_tab_videos_selected.png b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_videos_selected.png
new file mode 100755
index 0000000..9c64c50
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_videos_selected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-hdpi/ic_tab_videos_unselected.png b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_videos_unselected.png
new file mode 100755
index 0000000..6dc3af6
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-hdpi/ic_tab_videos_unselected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi-v11/ic_tab_photos.png b/samples/training/TabCompat/res/drawable-mdpi-v11/ic_tab_photos.png
new file mode 100644
index 0000000..95aceeb
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi-v11/ic_tab_photos.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi-v11/ic_tab_videos.png b/samples/training/TabCompat/res/drawable-mdpi-v11/ic_tab_videos.png
new file mode 100644
index 0000000..b7a8a6e
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi-v11/ic_tab_videos.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi/ic_launcher.png b/samples/training/TabCompat/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..5ee455f
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi/ic_tab_photos_selected.png b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_photos_selected.png
new file mode 100755
index 0000000..ad79efa
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_photos_selected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi/ic_tab_photos_unselected.png b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_photos_unselected.png
new file mode 100755
index 0000000..33bd57e
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_photos_unselected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi/ic_tab_videos_selected.png b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_videos_selected.png
new file mode 100755
index 0000000..0c73e48
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_videos_selected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-mdpi/ic_tab_videos_unselected.png b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_videos_unselected.png
new file mode 100755
index 0000000..1586c1a
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-mdpi/ic_tab_videos_unselected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi-v11/ic_tab_photos.png b/samples/training/TabCompat/res/drawable-xhdpi-v11/ic_tab_photos.png
new file mode 100644
index 0000000..a8fea8d
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi-v11/ic_tab_photos.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi-v11/ic_tab_videos.png b/samples/training/TabCompat/res/drawable-xhdpi-v11/ic_tab_videos.png
new file mode 100644
index 0000000..23c7e3b
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi-v11/ic_tab_videos.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi/ic_launcher.png b/samples/training/TabCompat/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..8affb1c
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_photos_selected.png b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_photos_selected.png
new file mode 100755
index 0000000..8b0e793
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_photos_selected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_photos_unselected.png b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_photos_unselected.png
new file mode 100755
index 0000000..e770492
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_photos_unselected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_videos_selected.png b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_videos_selected.png
new file mode 100755
index 0000000..00a60e0
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_videos_selected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_videos_unselected.png b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_videos_unselected.png
new file mode 100755
index 0000000..4eeb831
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable-xhdpi/ic_tab_videos_unselected.png
Binary files differ
diff --git a/samples/training/TabCompat/res/drawable/ic_tab_photos.xml b/samples/training/TabCompat/res/drawable/ic_tab_photos.xml
new file mode 100755
index 0000000..dd5a4f5
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable/ic_tab_photos.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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:drawable="@drawable/ic_tab_photos_selected"
+        android:state_selected="true"
+        android:state_pressed="false" />
+    <item android:drawable="@drawable/ic_tab_photos_unselected" />
+</selector>
diff --git a/samples/training/TabCompat/res/drawable/ic_tab_videos.xml b/samples/training/TabCompat/res/drawable/ic_tab_videos.xml
new file mode 100755
index 0000000..b16114c
--- /dev/null
+++ b/samples/training/TabCompat/res/drawable/ic_tab_videos.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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:drawable="@drawable/ic_tab_videos_selected"
+        android:state_selected="true"
+        android:state_pressed="false" />
+    <item android:drawable="@drawable/ic_tab_videos_unselected" />
+</selector>
diff --git a/samples/training/TabCompat/res/layout-v11/main.xml b/samples/training/TabCompat/res/layout-v11/main.xml
new file mode 100644
index 0000000..144658a
--- /dev/null
+++ b/samples/training/TabCompat/res/layout-v11/main.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 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.
+  -->
+
+<!-- API level 11+ only needs the container for tab content. -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabcontent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/samples/training/TabCompat/res/layout/main.xml b/samples/training/TabCompat/res/layout/main.xml
new file mode 100644
index 0000000..6e5dd79
--- /dev/null
+++ b/samples/training/TabCompat/res/layout/main.xml
@@ -0,0 +1,40 @@
+<!--
+  Copyright 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.
+  -->
+
+<!-- This layout is for API level 5-10 only. -->
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabhost"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <TabWidget
+            android:id="@android:id/tabs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <FrameLayout
+            android:id="@android:id/tabcontent"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+    </LinearLayout>
+</TabHost>
diff --git a/samples/training/TabCompat/res/values/strings.xml b/samples/training/TabCompat/res/values/strings.xml
new file mode 100644
index 0000000..734e4d6
--- /dev/null
+++ b/samples/training/TabCompat/res/values/strings.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 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">Tab Demo</string>
+
+    <string name="tab_photos">Photos</string>
+    <string name="tab_videos">Videos</string>
+</resources>
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/MainActivity.java b/samples/training/TabCompat/src/com/example/android/tabcompat/MainActivity.java
new file mode 100644
index 0000000..6d40487
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/MainActivity.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 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.tabcompat;
+
+import com.example.android.tabcompat.lib.CompatTab;
+import com.example.android.tabcompat.lib.CompatTabListener;
+import com.example.android.tabcompat.lib.TabCompatActivity;
+import com.example.android.tabcompat.lib.TabHelper;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class MainActivity extends TabCompatActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        TabHelper tabHelper = getTabHelper();
+
+        CompatTab photosTab = tabHelper.newTab("photos")
+                .setText(R.string.tab_photos)
+                .setIcon(R.drawable.ic_tab_photos)
+                .setTabListener(new InstantiatingTabListener(this, PhotosFragment.class));
+        tabHelper.addTab(photosTab);
+
+        CompatTab videosTab = tabHelper.newTab("videos")
+                .setText(R.string.tab_videos)
+                .setIcon(R.drawable.ic_tab_videos)
+                .setTabListener(new InstantiatingTabListener(this, VideosFragment.class));
+        tabHelper.addTab(videosTab);
+    }
+
+    /**
+     * Implementation of {@link CompatTabListener} to handle tab change events. This implementation
+     * instantiates the specified fragment class with no arguments when its tab is selected.
+     */
+    public static class InstantiatingTabListener implements CompatTabListener {
+
+        private final TabCompatActivity mActivity;
+        private final Class mClass;
+
+        /**
+         * Constructor used each time a new tab is created.
+         *
+         * @param activity The host Activity, used to instantiate the fragment
+         * @param cls      The class representing the fragment to instantiate
+         */
+        public InstantiatingTabListener(TabCompatActivity activity, Class<? extends Fragment> cls) {
+            mActivity = activity;
+            mClass = cls;
+        }
+
+        /* The following are each of the ActionBar.TabListener callbacks */
+        @Override
+        public void onTabSelected(CompatTab tab, FragmentTransaction ft) {
+            // Check if the fragment is already initialized
+            Fragment fragment = tab.getFragment();
+            if (fragment == null) {
+                // If not, instantiate and add it to the activity
+                fragment = Fragment.instantiate(mActivity, mClass.getName());
+                tab.setFragment(fragment);
+                ft.add(android.R.id.tabcontent, fragment, tab.getTag());
+            } else {
+                // If it exists, simply attach it in order to show it
+                ft.attach(fragment);
+            }
+        }
+
+        @Override
+        public void onTabUnselected(CompatTab tab, FragmentTransaction ft) {
+            Fragment fragment = tab.getFragment();
+            if (fragment != null) {
+                // Detach the fragment, because another one is being attached
+                ft.detach(fragment);
+            }
+        }
+
+        @Override
+        public void onTabReselected(CompatTab tab, FragmentTransaction ft) {
+            // User selected the already selected tab. Do nothing.
+        }
+    }
+
+    public static class PhotosFragment extends Fragment {
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            TextView textView = new TextView(getActivity());
+            textView.setGravity(Gravity.CENTER);
+            textView.setText(R.string.tab_photos);
+            return textView;
+        }
+    }
+
+    public static class VideosFragment extends Fragment {
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            TextView textView = new TextView(getActivity());
+            textView.setGravity(Gravity.CENTER);
+            textView.setText(R.string.tab_videos);
+            return textView;
+        }
+    }
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTab.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTab.java
new file mode 100644
index 0000000..7c1c053
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTab.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.graphics.drawable.Drawable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Represents a single tab.
+ * The {@link TabHelper} initializes one of the subclasses of this based
+ * on the current platform version, upon call to {@link TabHelper#newTab(String)}()
+ */
+public abstract class CompatTab {
+    final FragmentActivity mActivity;
+    final String mTag;
+
+    protected CompatTab(FragmentActivity activity, String tag) {
+        mActivity = activity;
+        mTag = tag;
+    }
+
+    public abstract CompatTab setText(int resId);
+    public abstract CompatTab setIcon(int resId);
+    public abstract CompatTab setTabListener(CompatTabListener callback);
+    public abstract CompatTab setFragment(Fragment fragment);
+
+    public abstract CharSequence getText();
+    public abstract Drawable getIcon();
+    public abstract CompatTabListener getCallback();
+    public abstract Fragment getFragment();
+
+    public abstract Object getTab();
+
+    public String getTag() {
+        return mTag;
+    }
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabEclair.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabEclair.java
new file mode 100644
index 0000000..72a0096
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabEclair.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.graphics.drawable.Drawable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * A base implementation of the {@link CompatTab} interface.
+ */
+public class CompatTabEclair extends CompatTab {
+    private CompatTabListener mCallback;
+    private CharSequence mText;
+    private Drawable mIcon;
+    private Fragment mFragment;
+
+    protected CompatTabEclair(FragmentActivity activity, String tag) {
+        super(activity, tag);
+    }
+
+    @Override
+    public CompatTab setText(int resId) {
+        mText = mActivity.getResources().getText(resId);
+        return this;
+    }
+
+    @Override
+    public CompatTab setIcon(int resId) {
+        mIcon = mActivity.getResources().getDrawable(resId);
+        return this;
+    }
+
+    @Override
+    public CompatTab setTabListener(CompatTabListener callback) {
+        mCallback = callback;
+        return this;
+    }
+
+    @Override
+    public CompatTab setFragment(Fragment fragment) {
+        mFragment = fragment;
+        return this;
+    }
+
+    @Override
+    public Fragment getFragment() {
+        return mFragment;
+    }
+
+    @Override
+    public CharSequence getText() {
+        return mText;
+    }
+
+    @Override
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    @Override
+    public Object getTab() {
+        return null;
+    }
+
+    @Override
+    public CompatTabListener getCallback() {
+        return mCallback;
+    }
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabHoneycomb.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabHoneycomb.java
new file mode 100644
index 0000000..4f9988e
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabHoneycomb.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.graphics.drawable.Drawable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+
+/**
+ * An implementation of the {@link CompatTab} interface that relies on API 11 APIs.
+ */
+public class CompatTabHoneycomb extends CompatTab implements ActionBar.TabListener {
+
+    /**
+     * The native tab object that this {@link CompatTab} acts as a proxy for.
+     */
+    ActionBar.Tab mTab;
+    CompatTabListener mCallback;
+    Fragment mFragment;
+
+    protected CompatTabHoneycomb(FragmentActivity activity, String tag) {
+        super(activity, tag);
+        mTab = activity.getActionBar().newTab();
+    }
+
+    @Override
+    public CompatTab setText(int resId) {
+        mTab.setText(resId);
+        return this;
+    }
+
+    @Override
+    public CompatTab setIcon(int resId) {
+        mTab.setIcon(resId);
+        return this;
+    }
+
+    @Override
+    public CompatTab setTabListener(CompatTabListener callback) {
+        mCallback = callback;
+        mTab.setTabListener(this);
+        return this;
+    }
+
+    @Override
+    public CharSequence getText() {
+        return mTab.getText();
+    }
+
+    @Override
+    public Drawable getIcon() {
+        return mTab.getIcon();
+    }
+
+    @Override
+    public Object getTab() {
+        return mTab;
+    }
+
+    @Override
+    public CompatTabListener getCallback() {
+        return mCallback;
+    }
+
+    @Override
+    public void onTabReselected(Tab tab, android.app.FragmentTransaction f) {
+        FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
+        ft.disallowAddToBackStack();
+        mCallback.onTabReselected(this, ft);
+        ft.commit();
+    }
+
+    @Override
+    public void onTabSelected(Tab tab, android.app.FragmentTransaction f) {
+        FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
+        ft.disallowAddToBackStack();
+        mCallback.onTabSelected(this, ft);
+        ft.commit();
+    }
+
+    @Override
+    public void onTabUnselected(Tab arg0, android.app.FragmentTransaction f) {
+        FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
+        ft.disallowAddToBackStack();
+        mCallback.onTabUnselected(this, ft);
+        ft.commit();
+    }
+
+    @Override
+    public CompatTab setFragment(Fragment fragment) {
+        mFragment = fragment;
+        return this;
+    }
+
+    @Override
+    public Fragment getFragment() {
+        return mFragment;
+    }
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabListener.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabListener.java
new file mode 100644
index 0000000..3970234
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/CompatTabListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.support.v4.app.FragmentTransaction;
+
+/**
+ * @see android.app.ActionBar.TabListener
+ */
+public interface CompatTabListener {
+    /**
+     * @see android.app.ActionBar.TabListener#onTabSelected(
+     *android.app.ActionBar.Tab, android.app.FragmentTransaction)
+     */
+    public void onTabSelected(CompatTab tab, FragmentTransaction ft);
+
+    /**
+     * @see android.app.ActionBar.TabListener#onTabUnselected(
+     *android.app.ActionBar.Tab, android.app.FragmentTransaction)
+     */
+    public void onTabUnselected(CompatTab tab, FragmentTransaction ft);
+
+    /**
+     * @see android.app.ActionBar.TabListener#onTabReselected(
+     *android.app.ActionBar.Tab, android.app.FragmentTransaction)
+     */
+    public void onTabReselected(CompatTab tab, FragmentTransaction ft);
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabCompatActivity.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabCompatActivity.java
new file mode 100644
index 0000000..ba2bc7f
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabCompatActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import com.example.android.tabcompat.lib.TabHelper;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * A base activity that defers tab functionality to a {@link TabHelper}.
+ *
+ * When building an activity with tabs, extend this class in order to provide compatibility with API
+ * level 5 and above. Using this class along with the {@link TabHelper} and {@link com.example.android.tabcompat.lib.CompatTab}
+ * classes, you can build a tab UI that's built using the {@link android.app.ActionBar} on
+ * Honeycomb+ and the {@link android.widget.TabWidget} on all older versions.
+ *
+ * The {@link TabHelper} APIs obfuscate all the compatibility work for you.
+ */
+public abstract class TabCompatActivity extends FragmentActivity {
+
+    TabHelper mTabHelper;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mTabHelper = TabHelper.createInstance(this);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mTabHelper.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mTabHelper.onRestoreInstanceState(savedInstanceState);
+    }
+
+    /**
+     * Returns the {@link TabHelper} for this activity.
+     */
+    protected TabHelper getTabHelper() {
+        mTabHelper.setUp();
+        return mTabHelper;
+    }
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelper.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelper.java
new file mode 100644
index 0000000..4ae3d19
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelper.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Convenience helper to build a set of tabs for a {@link TabCompatActivity}. To use this class,
+ * extend {@link TabCompatActivity} and:
+ *
+ * Call {@link TabCompatActivity#getTabHelper()}, returning a {@link TabHelper}.
+ *
+ * Create a {@link CompatTabListener}.
+ *
+ * Call {@link TabHelper#newTab(String)} to create each tab.
+ *
+ * Call CompatTab.setText().setIcon().setTabListener() to set up your tabs.
+ *
+ * Call {@link TabHelper#addTab(CompatTab)} for each tab, and you're done.
+ */
+public abstract class TabHelper {
+
+    protected FragmentActivity mActivity;
+
+    protected TabHelper(FragmentActivity activity) {
+        mActivity = activity;
+    }
+
+    /**
+     * Factory method for creating TabHelper objects for a given activity. Depending on which device
+     * the app is running, either a basic helper or Honeycomb-specific helper will be returned.
+     * Don't call this yourself; the TabCompatActivity instantiates one. Instead call
+     * TabCompatActivity.getTabHelper().
+     */
+    public static TabHelper createInstance(FragmentActivity activity) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            return new TabHelperHoneycomb(activity);
+        } else {
+            return new TabHelperEclair(activity);
+        }
+    }
+
+    /**
+     * Create a new tab.
+     *
+     * @param tag A unique tag to associate with the tab and associated fragment
+     * @return CompatTab for the appropriate android version
+     */
+    public CompatTab newTab(String tag) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            return new CompatTabHoneycomb(mActivity, tag);
+        } else {
+            return new CompatTabEclair(mActivity, tag);
+        }
+    }
+
+    public abstract void addTab(CompatTab tab);
+
+    protected abstract void onSaveInstanceState(Bundle outState);
+
+    protected abstract void onRestoreInstanceState(Bundle savedInstanceState);
+
+    protected abstract void setUp();
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelperEclair.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelperEclair.java
new file mode 100644
index 0000000..fc8e9cd
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelperEclair.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TabHost.TabSpec;
+
+import java.util.HashMap;
+
+/**
+ * This is a helper class to build tabs on pre-Honeycomb. Call {@link
+ * TabCompatActivity#getTabHelper()} to get the generic instance for
+ * compatibility with other versions.
+ *
+ * It implements a generic mechanism for associating fragments with the tabs in a tab host.  It
+ * relies on a trick:  Normally a tab host has a simple API for supplying a View or Intent that each
+ * tab will show.  This is not sufficient for switching between fragments.  So instead we make the
+ * content part of the tab host 0dp high (it is not shown) and this supplies its own dummy view to
+ * show as the tab content.  It listens to changes in tabs, then passes the event back to the tab's
+ * callback interface so the activity can take care of switching to the correct fragment.
+ */
+public class TabHelperEclair extends TabHelper implements TabHost.OnTabChangeListener {
+
+    private final HashMap<String, CompatTab> mTabs = new HashMap<String, CompatTab>();
+    private TabHost mTabHost;
+    CompatTabListener mCallback;
+    CompatTab mLastTab;
+
+    protected TabHelperEclair(FragmentActivity activity) {
+        super(activity);
+        mActivity = activity;
+    }
+
+    @Override
+    protected void setUp() {
+        if (mTabHost == null) {
+            mTabHost = (TabHost) mActivity.findViewById(android.R.id.tabhost);
+            mTabHost.setup();
+            mTabHost.setOnTabChangedListener(this);
+        }
+    }
+
+    @Override
+    public void addTab(CompatTab tab) {
+        String tag = tab.getTag();
+        TabSpec spec;
+
+        if (tab.getIcon() != null) {
+            spec = mTabHost.newTabSpec(tag).setIndicator(tab.getText(), tab.getIcon());
+        } else {
+            spec = mTabHost.newTabSpec(tag).setIndicator(tab.getText());
+        }
+
+        spec.setContent(new DummyTabFactory(mActivity));
+
+        // Check to see if we already have a fragment for this tab, probably
+        // from a previously saved state.  If so, deactivate it, because our
+        // initial state is that a tab isn't shown.
+
+        Fragment fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);
+        tab.setFragment(fragment);
+
+        if (fragment != null && !fragment.isDetached()) {
+            FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
+            ft.detach(fragment);
+            ft.commit();
+        }
+
+        mTabs.put(tag, tab);
+        mTabHost.addTab(spec);
+    }
+
+    /**
+     * Converts the basic "tab changed" event for TabWidget into the three possible events for
+     * CompatTabListener: selected, unselected, reselected.
+     */
+    @Override
+    public void onTabChanged(String tabId) {
+        CompatTab newTab = mTabs.get(tabId);
+        FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
+
+        if (mLastTab != newTab) {
+            if (mLastTab != null) {
+                if (mLastTab.getFragment() != null) {
+                    // Pass the unselected event back to the tab's CompatTabListener
+                    mLastTab.getCallback().onTabUnselected(mLastTab, ft);
+                }
+            }
+            if (newTab != null) {
+                // Pass the selected event back to the tab's CompatTabListener
+                newTab.getCallback().onTabSelected(newTab, ft);
+            }
+
+            mLastTab = newTab;
+        } else {
+            // Pass the re-selected event back to the tab's CompatTabListener
+            newTab.getCallback().onTabReselected(newTab, ft);
+        }
+
+        ft.commit();
+        mActivity.getSupportFragmentManager().executePendingTransactions();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        // Save and restore the selected tab for rotations/restarts.
+        outState.putString("tab", mTabHost.getCurrentTabTag());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        if (savedInstanceState != null) {
+            mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
+        }
+    }
+
+    /**
+     * Backwards-compatibility mumbo jumbo
+     */
+    static class DummyTabFactory implements TabHost.TabContentFactory {
+
+        private final Context mContext;
+
+        public DummyTabFactory(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public View createTabContent(String tag) {
+            View v = new View(mContext);
+            v.setMinimumWidth(0);
+            v.setMinimumHeight(0);
+            return v;
+        }
+    }
+}
diff --git a/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelperHoneycomb.java b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelperHoneycomb.java
new file mode 100644
index 0000000..a24c43a
--- /dev/null
+++ b/samples/training/TabCompat/src/com/example/android/tabcompat/lib/TabHelperHoneycomb.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 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.tabcompat.lib;
+
+import android.app.ActionBar;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+
+/**
+ * Helper class to build tabs on Honeycomb. Call {@link TabCompatActivity#getTabHelper()}
+ * to get the generic instance for compatibility with older versions.
+ */
+public class TabHelperHoneycomb extends TabHelper {
+
+    ActionBar mActionBar;
+
+    protected TabHelperHoneycomb(FragmentActivity activity) {
+        super(activity);
+    }
+
+    @Override
+    protected void setUp() {
+        if (mActionBar == null) {
+            mActionBar = mActivity.getActionBar();
+            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+        }
+    }
+
+    @Override
+    public void addTab(CompatTab tab) {
+        String tag = tab.getTag();
+
+        // Check to see if we already have a fragment for this tab, probably
+        // from a previously saved state.  If so, deactivate it, because our
+        // initial state is that a tab isn't shown.
+
+        Fragment fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);
+        tab.setFragment(fragment);
+
+        if (fragment != null && !fragment.isDetached()) {
+            FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
+            ft.detach(fragment);
+            ft.commit();
+        }
+
+        if (tab.getCallback() == null) {
+            throw new IllegalStateException("CompatTab must have a CompatTabListener");
+        }
+
+        // We know tab is a CompatTabHoneycomb instance, so its
+        // native tab object is an ActionBar.Tab.
+        mActionBar.addTab((ActionBar.Tab) tab.getTab());
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        int position = mActionBar.getSelectedTab().getPosition();
+        outState.putInt("tab_position", position);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        int position = savedInstanceState.getInt("tab_position");
+        mActionBar.setSelectedNavigationItem(position);
+    }
+}