Merge "Fix ContactLoaderTest."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ed03e77..5969097 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -352,6 +352,11 @@
             </intent-filter>
         </activity>
 
+        <!-- Views the details of a single group -->
+        <activity android:name=".activities.GroupDetailActivity"
+            android:label=""
+            android:theme="@style/GroupDetailTheme" />
+
         <!-- Used to show QuickContact window over a translucent activity, which is a
              temporary hack until we add better framework support. -->
         <activity
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 14a6b39..63ca3bd 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -14,26 +14,35 @@
      limitations under the License.
 -->
 
-<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/tabhost"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <LinearLayout
-        android:orientation="vertical"
+    <!-- Dialer --> -->
+    <fragment
+        class="com.android.contacts.dialpad.DialpadFragment"
+        android:id="@+id/dialpad_fragment"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent" />
 
-        <TabWidget android:id="@android:id/tabs"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-        />
+    <!-- Call Log -->
+    <fragment
+        class="com.android.contacts.calllog.CallLogFragment"
+        android:id="@+id/call_log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
 
-        <FrameLayout android:id="@android:id/tabcontent"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-        />
-    </LinearLayout>
-</TabHost>
+    <!-- Contacts -->
+    <fragment
+        android:id="@+id/contacts_fragment"
+        class="com.android.contacts.list.DefaultContactBrowseListFragment"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
 
+    <!-- Favorites -->
+    <fragment
+        android:id="@+id/favorites_fragment"
+        class="com.android.contacts.list.DefaultContactBrowseListFragment"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/group_detail_activity.xml b/res/layout/group_detail_activity.xml
new file mode 100644
index 0000000..707a65c
--- /dev/null
+++ b/res/layout/group_detail_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <fragment
+        class="com.android.contacts.group.GroupDetailFragment"
+        android:id="@+id/group_detail_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
diff --git a/res/layout/group_detail_fragment.xml b/res/layout/group_detail_fragment.xml
new file mode 100644
index 0000000..70d67b6
--- /dev/null
+++ b/res/layout/group_detail_fragment.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/group_detail"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ListView
+        android:id="@+id/member_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:scrollbarStyle="outsideOverlay"/>
+
+</LinearLayout>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2469a3d..0582fd4 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -15,8 +15,27 @@
 -->
 <resources>
     <style name="DialtactsTheme" parent="android:Theme.Holo.Light">
-        <item name="android:windowNoTitle">true</item>
         <item name="android:windowContentOverlay">@null</item>
+        <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
+        <item name="activated_background">@drawable/list_item_activated_background</item>
+        <item name="section_header_background">@drawable/list_title_holo</item>
+        <item name="list_section_header_height">32dip</item>
+        <item name="list_item_divider">@drawable/list_item_divider</item>
+        <item name="list_item_padding_top">4dip</item>
+        <item name="list_item_padding_right">11dip</item>
+        <item name="list_item_padding_bottom">4dip</item>
+        <item name="list_item_padding_left">4dip</item>
+        <item name="list_item_gap_between_image_and_text">8dip</item>
+        <item name="list_item_gap_between_label_and_data">5dip</item>
+        <item name="list_item_call_button_padding">14dip</item>
+        <item name="list_item_vertical_divider_margin">5dip</item>
+        <item name="list_item_presence_icon_margin">5dip</item>
+        <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_prefix_highlight_color">#729a27</item>
+        <item name="list_item_header_text_indent">56dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
+        <item name="contact_filter_popup_width">320dip</item>
     </style>
 
     <style name="CallDetailActivityTheme" parent="android:Theme.Holo.Light">
@@ -111,6 +130,30 @@
         <item name="contact_filter_popup_width">320dip</item>
     </style>
 
+    <!-- TODO: Clean up this file so themes aren't copied. -->
+    <style name="GroupDetailTheme" parent="@android:Theme">
+        <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
+        <item name="activated_background">@drawable/list_item_activated_background</item>
+        <item name="section_header_background">@drawable/list_title_holo</item>
+        <item name="list_section_header_height">32dip</item>
+        <item name="list_item_divider">@drawable/list_item_divider</item>
+        <item name="list_item_padding_top">4dip</item>
+        <item name="list_item_padding_right">11dip</item>
+        <item name="list_item_padding_bottom">4dip</item>
+        <item name="list_item_padding_left">4dip</item>
+        <item name="list_item_gap_between_image_and_text">8dip</item>
+        <item name="list_item_gap_between_label_and_data">5dip</item>
+        <item name="list_item_call_button_padding">14dip</item>
+        <item name="list_item_vertical_divider_margin">5dip</item>
+        <item name="list_item_presence_icon_margin">5dip</item>
+        <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_prefix_highlight_color">#729a27</item>
+        <item name="list_item_header_text_indent">56dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
+        <item name="contact_filter_popup_width">320dip</item>
+    </style>
+
     <style name="ContactPickerTheme" parent="@android:Theme">
         <item name="section_header_background">@drawable/section_header</item>
         <item name="list_item_divider">@drawable/list_item_divider</item>
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index 52d7b36..0672481 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -42,7 +42,6 @@
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.contacts.util.AccountSelectionUtil;
-import com.android.contacts.util.AccountsListAdapter;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.widget.ContextMenuAdapter;
 
@@ -69,9 +68,6 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListPopupWindow;
 import android.widget.Toast;
 
 import java.util.ArrayList;
@@ -88,9 +84,8 @@
     private static final String TAG = "ContactBrowserActivity";
 
     private static final int SUBACTIVITY_NEW_CONTACT = 2;
-    private static final int SUBACTIVITY_SETTINGS = 3;
-    private static final int SUBACTIVITY_EDIT_CONTACT = 4;
-    private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 5;
+    private static final int SUBACTIVITY_EDIT_CONTACT = 3;
+    private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 4;
 
     private static final String KEY_SEARCH_MODE = "searchMode";
 
@@ -725,7 +720,7 @@
         switch (item.getItemId()) {
             case R.id.menu_settings: {
                 final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
-                startActivityForResult(intent, SUBACTIVITY_SETTINGS);
+                startActivity(intent);
                 return true;
             }
             case R.id.menu_search: {
@@ -836,9 +831,6 @@
                 break;
             }
 
-            case SUBACTIVITY_SETTINGS:
-                break;
-
             // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
             // anymore
             case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
diff --git a/src/com/android/contacts/activities/DialpadActivity.java b/src/com/android/contacts/activities/DialpadActivity.java
index a30460a..bb122df 100644
--- a/src/com/android/contacts/activities/DialpadActivity.java
+++ b/src/com/android/contacts/activities/DialpadActivity.java
@@ -108,31 +108,6 @@
     }
 
     @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
-
-        // Nothing to do here; see DialpadFragment.onCreateOptionsMenu().
-        return true;
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        // The DialpadFragment completely owns the options menu,
-        // so we don't add any items here.  We *do* however
-        // have to return false here if the DialpadFragment
-        // says there shouldn't be a menu at all.
-        if (!mFragment.allowOptionsMenu()) {
-            return false;
-        }
-
-        super.onPrepareOptionsMenu(menu);
-
-        // See DialpadFragment.onPrepareOptionsMenu() for the actual menu
-        // contents.
-        return true;
-    }
-
-    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_CALL: {
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 869ae2a..ae8f479 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -17,13 +17,26 @@
 package com.android.contacts.activities;
 
 import com.android.contacts.R;
-import com.android.contacts.activities.ContactsFrontDoor;
-import com.android.contacts.activities.ContactBrowserActivity;
-import com.android.contacts.activities.DialpadActivity;
+import com.android.contacts.calllog.CallLogFragment;
+import com.android.contacts.dialpad.DialpadFragment;
+import com.android.contacts.interactions.ImportExportInteraction;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.ContactsIntentResolver;
+import com.android.contacts.list.ContactsRequest;
+import com.android.contacts.list.DefaultContactBrowseListFragment;
+import com.android.contacts.list.DirectoryListLoader;
+import com.android.contacts.list.OnContactBrowserActionListener;
+import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.internal.telephony.ITelephony;
 
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
 import android.app.Activity;
-import android.app.TabActivity;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.net.Uri;
@@ -31,10 +44,14 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.Settings;
 import android.provider.ContactsContract.Intents.UI;
 import android.util.Log;
-import android.view.Window;
-import android.widget.TabHost;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 /**
  * The dialer activity that has one tab with the virtual 12key
@@ -43,7 +60,7 @@
  * embedded using intents.
  * The dialer tab's title is 'phone', a more common name (see strings.xml).
  */
-public class DialtactsActivity extends TabActivity implements TabHost.OnTabChangeListener {
+public class DialtactsActivity extends Activity {
     private static final String TAG = "DialtactsActivity";
 
     private static final int TAB_INDEX_DIALER = 0;
@@ -63,9 +80,13 @@
     private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab";
     private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
 
-    private TabHost mTabHost;
     private String mFilterText;
     private Uri mDialUri;
+    private DialpadFragment mDialpadFragment;
+    private CallLogFragment mCallLogFragment;
+    private DefaultContactBrowseListFragment mContactsFragment;
+    private DefaultContactBrowseListFragment mFavoritesFragment;
+    private ImportExportInteraction mImportExportInteraction;
 
     /**
      * The index of the tab that has last been manually selected (the user clicked on a tab).
@@ -80,18 +101,34 @@
         final Intent intent = getIntent();
         fixIntent(intent);
 
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
         setContentView(R.layout.dialtacts_activity);
 
-        mTabHost = getTabHost();
-        mTabHost.setOnTabChangedListener(this);
+        final FragmentManager fragmentManager = getFragmentManager();
+        mDialpadFragment = (DialpadFragment) fragmentManager
+                .findFragmentById(R.id.dialpad_fragment);
+        mCallLogFragment = (CallLogFragment) fragmentManager
+                .findFragmentById(R.id.call_log_fragment);
+        mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
+                .findFragmentById(R.id.contacts_fragment);
+        mFavoritesFragment = (DefaultContactBrowseListFragment) fragmentManager
+                .findFragmentById(R.id.favorites_fragment);
 
-        // Setup the tabs
-        setupDialerTab();
-        setupCallLogTab();
-        setupContactsTab();
-        setupFavoritesTab();
-        setupGroupsTab();
+        // Hide all tabs (the current tab will later be reshown once a tab is selected)
+        final FragmentTransaction transaction = fragmentManager.beginTransaction();
+        transaction.hide(mDialpadFragment);
+        transaction.hide(mCallLogFragment);
+        transaction.hide(mContactsFragment);
+        transaction.hide(mFavoritesFragment);
+        transaction.commit();
+
+        // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
+        setupDialer();
+        setupCallLog();
+        setupContacts();
+        setupFavorites();
+        getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+        getActionBar().setDisplayShowTitleEnabled(false);
+        getActionBar().setDisplayShowHomeEnabled(false);
 
         // Load the last manually loaded tab
         final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
@@ -110,7 +147,7 @@
     protected void onPause() {
         super.onPause();
 
-        final int currentTabIndex = mTabHost.getCurrentTab();
+        final int currentTabIndex = getActionBar().getSelectedTab().getPosition();
         final SharedPreferences.Editor editor =
                 getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit();
         if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
@@ -132,57 +169,73 @@
         }
     }
 
-    private void setupCallLogTab() {
-        // Force the class since overriding tab entries doesn't work
-        Intent intent = new Intent("com.android.phone.action.RECENT_CALLS");
-        intent.setClass(this, CallLogActivity.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("call_log")
-                .setIndicator(getString(R.string.recentCallsIconLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_recent))
-                .setContent(intent));
+    private void setupDialer() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.dialerIconLabel);
+        tab.setTabListener(new TabChangeListener(mDialpadFragment));
+        tab.setIcon(R.drawable.ic_tab_dialer);
+        getActionBar().addTab(tab);
+        mDialpadFragment.resolveIntent();
     }
 
-    private void setupDialerTab() {
-        Intent intent = new Intent("com.android.phone.action.TOUCH_DIALER");
-        intent.setClass(this, DialpadActivity.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("dialer")
-                .setIndicator(getString(R.string.dialerIconLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_dialer))
-                .setContent(intent));
+    private void setupCallLog() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.recentCallsIconLabel);
+        tab.setIcon(R.drawable.ic_tab_recent);
+        tab.setTabListener(new TabChangeListener(mCallLogFragment));
+        getActionBar().addTab(tab);
     }
 
-    private void setupContactsTab() {
+    private void setupContacts() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.contactsIconLabel);
+        tab.setIcon(R.drawable.ic_tab_contacts);
+        tab.setTabListener(new TabChangeListener(mContactsFragment));
+        getActionBar().addTab(tab);
+
+        // TODO: We should not artificially create Intents and put them into the Fragment.
+        // It would be nicer to directly pass in the UI constant
         Intent intent = new Intent(UI.LIST_ALL_CONTACTS_ACTION);
         intent.setClass(this, ContactBrowserActivity.class);
 
-        mTabHost.addTab(mTabHost.newTabSpec("contacts")
-                .setIndicator(getText(R.string.contactsIconLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_contacts))
-                .setContent(intent));
+        ContactsIntentResolver resolver = new ContactsIntentResolver(this);
+        ContactsRequest request = resolver.resolveIntent(intent);
+        final ContactListFilter filter = new ContactListFilter(
+                ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+        mContactsFragment.setFilter(filter, false);
+        mContactsFragment.setSearchMode(request.isSearchMode());
+        mContactsFragment.setQueryString(request.getQueryString(), false);
+        mContactsFragment.setContactsRequest(request);
+        mContactsFragment.setDirectorySearchMode(request.isDirectorySearchEnabled()
+                ? DirectoryListLoader.SEARCH_MODE_DEFAULT
+                : DirectoryListLoader.SEARCH_MODE_NONE);
+        mContactsFragment.setOnContactListActionListener(mListFragmentListener);
     }
 
-    private void setupFavoritesTab() {
+    private void setupFavorites() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.contactsFavoritesLabel);
+        tab.setIcon(R.drawable.ic_tab_starred);
+        tab.setTabListener(new TabChangeListener(mFavoritesFragment));
+        getActionBar().addTab(tab);
+
+        // TODO: We should not artificially create Intents and put them into the Fragment.
+        // It would be nicer to directly pass in the UI constant
         Intent intent = new Intent(UI.LIST_STREQUENT_ACTION);
         intent.setClass(this, ContactBrowserActivity.class);
 
-        mTabHost.addTab(mTabHost.newTabSpec("favorites")
-                .setIndicator(getString(R.string.contactsFavoritesLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_starred))
-                .setContent(intent));
-    }
-
-    private void setupGroupsTab() {
-        // This is a temporary intent action until the refactoring for the phone/contacts
-        // split is complete.
-        Intent intent = new Intent("com.android.phone.action.GROUPS_LIST");
-                        intent.setClass(this, GroupBrowserActivity.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("groups")
-                .setIndicator(getString(R.string.contactsGroupsLabel),
-                        getResources().getDrawable(R.drawable.ic_menu_display_all_holo_light))
-                .setContent(intent));
+        ContactsIntentResolver resolver = new ContactsIntentResolver(this);
+        ContactsRequest request = resolver.resolveIntent(intent);
+        final ContactListFilter filter = new ContactListFilter(
+                ContactListFilter.FILTER_TYPE_STARRED);
+        mFavoritesFragment.setFilter(filter, false);
+        mFavoritesFragment.setSearchMode(request.isSearchMode());
+        mFavoritesFragment.setQueryString(request.getQueryString(), false);
+        mFavoritesFragment.setContactsRequest(request);
+        mFavoritesFragment.setDirectorySearchMode(request.isDirectorySearchEnabled()
+                ? DirectoryListLoader.SEARCH_MODE_DEFAULT
+                : DirectoryListLoader.SEARCH_MODE_NONE);
+        mFavoritesFragment.setOnContactListActionListener(mListFragmentListener);
     }
 
     /**
@@ -223,13 +276,6 @@
             return;
         }
 
-        // Dismiss menu provided by any children activities
-        Activity activity = getLocalActivityManager().
-                getActivity(mTabHost.getCurrentTabTag());
-        if (activity != null) {
-            activity.closeOptionsMenu();
-        }
-
         // Tell the children activities that they should ignore any possible saved
         // state and instead reload their state from the parent's intent
         intent.putExtra(EXTRA_IGNORE_STATE, true);
@@ -246,21 +292,21 @@
             boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
                     PREF_FAVORITES_AS_CONTACTS_DEFAULT);
             if (favoritesAsContacts) {
-                mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
+                getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_FAVORITES));
             } else {
-                mTabHost.setCurrentTab(TAB_INDEX_CONTACTS);
+                getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CONTACTS));
             }
         } else {
             // Not launched through the front door, look at the component to determine the tab
             String componentName = intent.getComponent().getClassName();
             if (getClass().getName().equals(componentName)) {
                 if (recentCallsRequest) {
-                    mTabHost.setCurrentTab(TAB_INDEX_CALL_LOG);
+                    getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CALL_LOG));
                 } else {
-                    mTabHost.setCurrentTab(TAB_INDEX_DIALER);
+                    getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_DIALER));
                 }
             } else {
-                mTabHost.setCurrentTab(mLastManuallySelectedTab);
+                getActionBar().selectTab(getActionBar().getTabAt(mLastManuallySelectedTab));
             }
         }
 
@@ -371,19 +417,158 @@
         }
     }
 
-    /** {@inheritDoc} */
-    public void onTabChanged(String tabId) {
-        // Because we're using Activities as our tab children, we trigger
-        // onWindowFocusChanged() to let them know when they're active.  This may
-        // seem to duplicate the purpose of onResume(), but it's needed because
-        // onResume() can't reliably check if a keyguard is active.
-        Activity activity = getLocalActivityManager().getActivity(tabId);
-        if (activity != null) {
-            activity.onWindowFocusChanged(true);
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+
+        // Pass this lifecycle event down to the fragment
+        mDialpadFragment.onPostCreate();
+    }
+
+    /**
+     * Tab change listener that is instantiated once for each tab. Handles showing/hiding tabs
+     * and remembers manual tab selections
+     */
+    private class TabChangeListener implements TabListener {
+        private final Fragment mFragment;
+
+        public TabChangeListener(Fragment fragment) {
+            mFragment = fragment;
         }
 
-        // Remember this tab index. This function is also called, if the tab is set automatically
-        // in which case the setter (setCurrentTab) has to set this to its old value afterwards
-        mLastManuallySelectedTab = mTabHost.getCurrentTab();
+        @Override
+        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+            ft.hide(mFragment);
+        }
+
+        @Override
+        public void onTabSelected(Tab tab, FragmentTransaction ft) {
+            ft.show(mFragment);
+
+            // Remember this tab index. This function is also called, if the tab is set
+            // automatically in which case the setter (setCurrentTab) has to set this to its old
+            // value afterwards
+            mLastManuallySelectedTab = tab.getPosition();
+        }
+
+        @Override
+        public void onTabReselected(Tab tab, FragmentTransaction ft) {
+        }
+    }
+
+    private OnContactBrowserActionListener mListFragmentListener =
+            new OnContactBrowserActionListener() {
+        @Override
+        public void onViewContactAction(Uri contactLookupUri) {
+            startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
+        }
+
+        @Override
+        public void onSmsContactAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onSelectionChange() {
+        }
+
+        @Override
+        public void onRemoveFromFavoritesAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onInvalidSelection() {
+        }
+
+        @Override
+        public void onFinishAction() {
+        }
+
+        @Override
+        public void onEditContactAction(Uri contactLookupUri) {
+        }
+
+        @Override
+        public void onDeleteContactAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onCreateNewContactAction() {
+        }
+
+        @Override
+        public void onCallContactAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onAddToFavoritesAction(Uri contactUri) {
+        }
+    };
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // For now, create the menu in here. It would be nice to do this in the Fragment,
+        // but that Fragment is re-used in other views.
+        final ActionBar actionBar = getActionBar();
+        if (actionBar == null) return false;
+        final Tab tab = actionBar.getSelectedTab();
+        if (tab == null) return false;
+        final int tabIndex = tab.getPosition();
+        if (tabIndex != TAB_INDEX_CONTACTS && tabIndex != TAB_INDEX_FAVORITES) return false;
+
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.list, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // This is currently a copy of the equivalent code of ContactBrowserActivity (with the
+        // exception of menu_add, because we do not select items in the list).
+        // Should be consolidated
+        switch (item.getItemId()) {
+        case R.id.menu_settings: {
+            final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
+            startActivity(intent);
+            return true;
+        }
+        case R.id.menu_search: {
+            onSearchRequested();
+            return true;
+        }
+        case R.id.menu_add: {
+            final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+            startActivity(intent);
+            return true;
+        }
+        case R.id.menu_import_export: {
+            getImportExportInteraction().startInteraction();
+            return true;
+        }
+        case R.id.menu_accounts: {
+            final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
+            intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
+                ContactsContract.AUTHORITY
+            });
+            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+            startActivity(intent);
+            return true;
+        }
+        default:
+            return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id, Bundle bundle) {
+        Dialog dialog = getImportExportInteraction().onCreateDialog(id, bundle);
+        if (dialog != null) return dialog;
+        return super.onCreateDialog(id, bundle);
+    }
+
+    private ImportExportInteraction getImportExportInteraction() {
+        if (mImportExportInteraction == null) {
+            mImportExportInteraction = new ImportExportInteraction(this);
+        }
+        return mImportExportInteraction;
     }
 }
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
new file mode 100644
index 0000000..edc460c
--- /dev/null
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+
+import android.os.Bundle;
+
+public class GroupDetailActivity extends ContactsActivity {
+
+    private static final String TAG = "GroupDetailActivity";
+
+    public static final String KEY_ACCOUNT_TYPE = "accountType";
+    public static final String KEY_ACCOUNT_NAME = "accountName";
+    public static final String KEY_GROUP_ID = "groupId";
+    public static final String KEY_GROUP_SOURCE_ID = "groupSourceId";
+    public static final String KEY_GROUP_READ_ONLY = "groupReadOnly";
+    public static final String KEY_GROUP_TITLE = "title";
+
+    @Override
+    public void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        // TODO: Create Intent Resolver to handle the different ways users can get to this list.
+        // TODO: Handle search or key down
+
+        setContentView(R.layout.group_detail_activity);
+    }
+}
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index fc5fcd9..58ffb9e 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -516,17 +516,14 @@
                 .setIcon(R.drawable.ic_menu_wait);
     }
 
-    /** @return true if an options menu should be shown in our containing activity. */
-    public boolean allowOptionsMenu() {
-        // We never show a menu if the "choose dialpad" UI is up.
-        // Otherwise the menu is allowed (see onPrepareOptionsMenu() below.)
-        return (!dialpadChooserVisible());
-    }
-
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
-        // Note that we won't show a menu at all if the "choose dialpad" UI is up.
-        // (See allowOptionsMenu() above, along with DialogActivity.onPrepareOptionsMenu().)
+        // If we have not been inflated yet, there is no menu
+        if (mDialpadChooser == null) return;
+
+        // We never show a menu if the "choose dialpad" UI is up.
+        // Otherwise the menu is allowed (see onPrepareOptionsMenu() below.)
+        if (!dialpadChooserVisible()) return;
 
         if (isDigitsEmpty()) {
             mAddToContactMenuItem.setVisible(false);
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index ed69776..bccc207 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -69,6 +69,10 @@
         icon.setImageResource(R.drawable.ic_menu_display_all_holo_light);
         label.setText(group.getTitle());
         account.setText(group.getAccountName());
+
+        // Set the tag to be the GroupMetaData object, in order to extract group attributes from the
+        // view later.
+        convertView.setTag(group);
         return convertView;
     }
 
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index 978ce13..d9d020e 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -19,6 +19,7 @@
 import com.android.contacts.GroupMetaData;
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
+import com.android.contacts.activities.GroupDetailActivity;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -26,6 +27,7 @@
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.Context;
 import android.content.CursorLoader;
+import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
 import android.os.Bundle;
@@ -36,6 +38,8 @@
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
 
 import java.util.ArrayList;
@@ -137,6 +141,24 @@
 
         mListView.setAdapter(new GroupBrowseListAdapter(mContext, mGroupList));
         mListView.setEmptyView(mEmptyView);
+        mListView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                startGroupDetailActivity((GroupMetaData) view.getTag());
+            }
+        });
+    }
+
+    private void startGroupDetailActivity(GroupMetaData group) {
+        if (group == null) {
+            return;
+        }
+        Intent intent = new Intent(mContext, GroupDetailActivity.class);
+        intent.putExtra(GroupDetailActivity.KEY_ACCOUNT_TYPE, group.getAccountType());
+        intent.putExtra(GroupDetailActivity.KEY_ACCOUNT_NAME, group.getAccountName());
+        intent.putExtra(GroupDetailActivity.KEY_GROUP_ID, group.getGroupId());
+        intent.putExtra(GroupDetailActivity.KEY_GROUP_TITLE, group.getTitle());
+        mContext.startActivity(intent);
     }
 
     private void hideSoftKeyboard() {
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
new file mode 100644
index 0000000..98babac
--- /dev/null
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.group;
+
+import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.R;
+import com.android.contacts.activities.GroupDetailActivity;
+import com.android.contacts.list.ContactListAdapter;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.DefaultContactListAdapter;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Directory;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+/**
+ * Displays the details of a group and shows a list of actions possible for the group.
+ */
+public class GroupDetailFragment extends Fragment implements OnScrollListener {
+
+    private static final String TAG = "GroupDetailFragment";
+
+    private static final int LOADER_MEMBERS = 0;
+
+    private Context mContext;
+
+    private View mRootView;
+    private ListView mMemberListView;
+
+    private ContactListAdapter mAdapter;
+    private ContactPhotoManager mPhotoManager;
+
+    public GroupDetailFragment() {
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mContext = activity;
+        configurePhotoLoader();
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mContext = null;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
+        mMemberListView = (ListView) mRootView.findViewById(R.id.member_list);
+        mMemberListView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                // TODO: Open contact detail for this person
+            }
+        });
+        return mRootView;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        Intent intent = getActivity().getIntent();
+        String accountType = intent.getStringExtra(GroupDetailActivity.KEY_ACCOUNT_TYPE);
+        String accountName = intent.getStringExtra(GroupDetailActivity.KEY_ACCOUNT_NAME);
+        long groupId = intent.getLongExtra(GroupDetailActivity.KEY_GROUP_ID, -1);
+        String groupTitle = intent.getStringExtra(GroupDetailActivity.KEY_GROUP_TITLE);
+
+        configureAdapter(accountType, accountName, groupId, groupTitle);
+        startGroupMembersLoader();
+    }
+
+    private void configureAdapter(String accountType, String accountName,
+                long groupId, String groupTitle) {
+        mAdapter = new DefaultContactListAdapter(getActivity());
+        mAdapter.setSectionHeaderDisplayEnabled(false);
+        mAdapter.setDisplayPhotos(true);
+        mAdapter.setHasHeader(0, false);
+        mAdapter.setQuickContactEnabled(false);
+        mAdapter.setPinnedPartitionHeadersEnabled(false);
+        mAdapter.setContactNameDisplayOrder(ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY);
+        mAdapter.setSortOrder(ContactsContract.Preferences.SORT_ORDER_PRIMARY);
+        mAdapter.setPhotoLoader(mPhotoManager);
+        mAdapter.setFilter(new ContactListFilter(accountType, accountName, groupId, "", false,
+                groupTitle));
+        mMemberListView.setAdapter(mAdapter);
+    }
+
+    private void configurePhotoLoader() {
+        if (mContext != null) {
+            if (mPhotoManager == null) {
+                mPhotoManager = ContactPhotoManager.getInstance(mContext);
+            }
+            if (mMemberListView != null) {
+                mMemberListView.setOnScrollListener(this);
+            }
+            if (mAdapter != null) {
+                mAdapter.setPhotoLoader(mPhotoManager);
+            }
+        }
+    }
+
+    /**
+     * Start the loader to retrieve the list of group members.
+     */
+    private void startGroupMembersLoader() {
+        getLoaderManager().destroyLoader(LOADER_MEMBERS);
+        getLoaderManager().restartLoader(LOADER_MEMBERS, null, mGroupMemberListLoaderListener);
+    }
+
+    /**
+     * The listener for the group members list loader
+     */
+    private final LoaderManager.LoaderCallbacks<Cursor> mGroupMemberListLoaderListener =
+            new LoaderCallbacks<Cursor>() {
+
+        @Override
+        public CursorLoader onCreateLoader(int id, Bundle args) {
+            CursorLoader loader = new CursorLoader(mContext, null, null, null, null, null);
+            mAdapter.configureLoader(loader, Directory.DEFAULT);
+            return loader;
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+            mAdapter.changeCursor(loader.getId(), data);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {}
+    };
+
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+            int totalItemCount) {
+    }
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
+            mPhotoManager.pause();
+        } else {
+            mPhotoManager.resume();
+        }
+    }
+}