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