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);
+ }
+}