Merge "Switches to new icons for call log items."
diff --git a/res/layout/people_activity.xml b/res/layout/people_activity.xml
index 11431b3..589e4a9 100644
--- a/res/layout/people_activity.xml
+++ b/res/layout/people_activity.xml
@@ -19,28 +19,18 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!-- Favorites -->
-    <fragment
-        android:id="@+id/favorites_fragment"
-        class="com.android.contacts.list.StrequentContactListFragment"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent" />
+    <!--
+        ViewPager for swiping between tabs.  We put StrequentContactListFragment,
+        DefaultContactBrowseListFragment and GroupBrowseListFragment at runtime.
 
-    <!-- All -->
-    <fragment
-        android:id="@+id/all_fragment"
-        class="com.android.contacts.list.DefaultContactBrowseListFragment"
+        (Adding them directly as the children of this view is not recommended.  ViewPager should
+        be treated like a ListView, which doesn't expect children to be added from the layout.)
+    -->
+    <android.support.v4.view.ViewPager
+        android:id="@+id/tab_pager"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
-    />
-
-    <!-- Groups -->
-    <fragment
-        android:id="@+id/groups_fragment"
-        class="com.android.contacts.group.GroupBrowseListFragment"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-    />
+        />
 
     <FrameLayout
         android:id="@+id/contacts_unavailable_view"
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index dc37d3e..0072592 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -33,7 +33,6 @@
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
-import android.widget.TabHost.OnTabChangeListener;
 
 /**
  * Adapter for the action bar at the top of the Contacts activity.
@@ -174,6 +173,13 @@
      * Change the current tab, and notify the listener.
      */
     public void setCurrentTab(TabState tab) {
+        setCurrentTab(tab, true);
+    }
+
+    /**
+     * Change the current tab
+     */
+    public void setCurrentTab(TabState tab, boolean notifyListener) {
         if (tab == null) throw new NullPointerException();
         if (tab == mCurrentTab) {
             return;
@@ -186,7 +192,7 @@
             mActionBar.setSelectedNavigationItem(index);
         }
 
-        if (mListener != null) mListener.onSelectedTabChanged();
+        if (notifyListener && mListener != null) mListener.onSelectedTabChanged();
     }
 
     public TabState getCurrentTab() {
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 1907a77..e11755b 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -60,7 +60,6 @@
 import com.android.contacts.widget.ContextMenuAdapter;
 
 import android.accounts.Account;
-import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
@@ -71,11 +70,14 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Parcelable;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.ProviderStatus;
 import android.provider.Settings;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -161,6 +163,10 @@
 
     private View mAddGroupImageView;
 
+    /** ViewPager for swipe, used only on the phone (i.e. one-pane mode) */
+    private ViewPager mTabPager;
+    private TabPagerAdapter mTabPagerAdapter;
+
     private ContactDetailLayoutController mContactDetailLayoutController;
 
     private final Handler mHandler = new Handler();
@@ -252,26 +258,63 @@
             // Hide all tabs (the current tab will later be reshown once a tab is selected)
             final FragmentTransaction transaction = fragmentManager.beginTransaction();
 
-            // Common fragments that exist on both 1 and 2 panes.
-            mFavoritesFragment = getFragment(R.id.favorites_fragment);
-            mFavoritesFragment.setListener(mFavoritesFragmentListener);
-            mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
+            // Prepare the fragments which are used both on 1-pane and on 2-pane.
+            if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+                mFavoritesFragment = getFragment(R.id.favorites_fragment);
+                mAllFragment = getFragment(R.id.all_fragment);
+                mGroupsFragment = getFragment(R.id.groups_fragment);
+            } else {
+                mTabPager = getView(R.id.tab_pager);
+                mTabPagerAdapter = new TabPagerAdapter();
+                mTabPager.setAdapter(mTabPagerAdapter);
+                mTabPager.setOnPageChangeListener(new TabPagerListener());
 
-            mAllFragment = getFragment(R.id.all_fragment);
+                final String FAVORITE_TAG = "tab-pager-favorite";
+                final String ALL_TAG = "tab-pager-all";
+                final String GROUPS_TAG = "tab-pager-groups";
+
+                // Create the fragments and add as children of the view pager.
+                // The pager adapter will only change the visibility; it'll never create/destroy
+                // fragments.
+                // However, if it's after screen rotation, the fragments have been re-created by
+                // the fragment manager, so first see if there're already the target fragments
+                // existing.
+                mFavoritesFragment = (StrequentContactListFragment)
+                        fragmentManager.findFragmentByTag(FAVORITE_TAG);
+                mAllFragment = (DefaultContactBrowseListFragment)
+                        fragmentManager.findFragmentByTag(ALL_TAG);
+                mGroupsFragment = (GroupBrowseListFragment)
+                        fragmentManager.findFragmentByTag(GROUPS_TAG);
+
+                if (mFavoritesFragment == null) {
+                    mFavoritesFragment = new StrequentContactListFragment();
+                    mAllFragment = new DefaultContactBrowseListFragment();
+                    mGroupsFragment = new GroupBrowseListFragment();
+
+                    transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG);
+                    transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
+                    transaction.add(R.id.tab_pager, mGroupsFragment, GROUPS_TAG);
+                }
+            }
+
+            mFavoritesFragment.setListener(mFavoritesFragmentListener);
+
             mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
             if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) {
                 mAllFragment.setContextMenuAdapter(
                         new ContactBrowseListContextMenuAdapter(mAllFragment));
             }
 
-            mGroupsFragment = getFragment(R.id.groups_fragment);
             mGroupsFragment.setListener(new GroupBrowserActionListener());
 
+            // Hide all fragments for now.  We adjust visibility when we get onSelectedTabChanged()
+            // from ActionBarAdapter.
+            transaction.hide(mFavoritesFragment);
             transaction.hide(mAllFragment);
             transaction.hide(mGroupsFragment);
 
             if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
-                mFavoritesFragment.setQuickContact(true);
+                // Prepare 2-pane only fragments/views...
 
                 // Container views for fragments
                 mFavoritesView = getView(R.id.favorites_view);
@@ -296,6 +339,12 @@
             }
             transaction.commit();
             fragmentManager.executePendingTransactions();
+
+            // These operations below are only okay if the fragment is already created.
+            // Because we create the fragment dynamically on 1-pane, this has to be done after
+            // the fragment transaction is executed.
+            mFavoritesFragment.setQuickContact(PhoneCapabilityTester.isUsingTwoPanes(this));
+            mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
         }
 
         setTitle(mRequest.getActivityTitle());
@@ -462,24 +511,38 @@
     private void updateFragmentsVisibility() {
         TabState tab = mActionBarAdapter.getCurrentTab();
 
+        // We use ViewPager on 1-pane.
+        if (!PhoneCapabilityTester.isUsingTwoPanes(this)) {
+            if (mActionBarAdapter.isSearchMode()) {
+                mTabPagerAdapter.setSearchMode(true);
+            } else {
+                mTabPagerAdapter.setSearchMode(false);
+                int tabIndex = tab.ordinal();
+                if (mTabPager.getCurrentItem() != tabIndex) {
+                    mTabPager.setCurrentItem(tab.ordinal(), false /* no smooth scroll */);
+                }
+            }
+            return;
+        }
+
+        // for the tablet...
+
         // If in search mode, we use the all list + contact details to show the result.
         if (mActionBarAdapter.isSearchMode()) {
             tab = TabState.ALL;
         }
-        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
-            switch (tab) {
-                case FAVORITES:
-                    mFavoritesView.setVisibility(View.VISIBLE);
-                    mBrowserView.setVisibility(View.GONE);
-                    mDetailsView.setVisibility(View.GONE);
-                    break;
-                case GROUPS:
-                case ALL:
-                    mFavoritesView.setVisibility(View.GONE);
-                    mBrowserView.setVisibility(View.VISIBLE);
-                    mDetailsView.setVisibility(View.VISIBLE);
-                    break;
-            }
+        switch (tab) {
+            case FAVORITES:
+                mFavoritesView.setVisibility(View.VISIBLE);
+                mBrowserView.setVisibility(View.GONE);
+                mDetailsView.setVisibility(View.GONE);
+                break;
+            case GROUPS:
+            case ALL:
+                mFavoritesView.setVisibility(View.GONE);
+                mBrowserView.setVisibility(View.VISIBLE);
+                mDetailsView.setVisibility(View.VISIBLE);
+                break;
         }
         FragmentManager fragmentManager = getFragmentManager();
         FragmentTransaction ft = fragmentManager.beginTransaction();
@@ -516,6 +579,145 @@
         }
     }
 
+    private class TabPagerListener implements ViewPager.OnPageChangeListener {
+        @Override
+        public void onPageScrollStateChanged(int state) {
+        }
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            // Make sure not in the search mode, in which case position != TabState.ordinal().
+            if (!mTabPagerAdapter.isSearchMode()) {
+                mActionBarAdapter.setCurrentTab(TabState.fromInt(position), false);
+            }
+        }
+    }
+
+    /**
+     * Adapter for the {@link ViewPager}.  Unlike {@link FragmentPagerAdapter},
+     * {@link #instantiateItem} returns existing fragments, and {@link #instantiateItem}/
+     * {@link #destroyItem} show/hide fragments instead of attaching/detaching.
+     *
+     * In search mode, we always show the "all" fragment, and disable the swipe.  We change the
+     * number of items to 1 to disable the swipe.
+     *
+     * TODO figure out a more straight way to disable swipe.
+     */
+    private class TabPagerAdapter extends PagerAdapter {
+        private final FragmentManager mFragmentManager;
+        private FragmentTransaction mCurTransaction = null;
+
+        private boolean mTabPagerAdapterSearchMode;
+
+        public TabPagerAdapter() {
+            mFragmentManager = getFragmentManager();
+        }
+
+        public boolean isSearchMode() {
+            return mTabPagerAdapterSearchMode;
+        }
+
+        public void setSearchMode(boolean searchMode) {
+            if (searchMode == mTabPagerAdapterSearchMode) {
+                return;
+            }
+            mTabPagerAdapterSearchMode = searchMode;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getCount() {
+            return mTabPagerAdapterSearchMode ? 1 : TabState.values().length;
+        }
+
+        /** Gets called when the number of items changes. */
+        @Override
+        public int getItemPosition(Object object) {
+            if (mTabPagerAdapterSearchMode) {
+                if (object == mAllFragment) {
+                    return 0; // Only 1 page in search mode
+                }
+            } else {
+                if (object == mFavoritesFragment) {
+                    return TabState.FAVORITES.ordinal();
+                }
+                if (object == mAllFragment) {
+                    return TabState.ALL.ordinal();
+                }
+                if (object == mGroupsFragment) {
+                    return TabState.GROUPS.ordinal();
+                }
+            }
+            return POSITION_NONE;
+        }
+
+        @Override
+        public void startUpdate(View container) {
+        }
+
+        private Fragment getFragment(int position) {
+            if (mTabPagerAdapterSearchMode) {
+                if (position == 0) {
+                    return mAllFragment;
+                }
+            } else {
+                if (position == TabState.FAVORITES.ordinal()) {
+                    return mFavoritesFragment;
+                } else if (position == TabState.ALL.ordinal()) {
+                    return mAllFragment;
+                } else if (position == TabState.GROUPS.ordinal()) {
+                    return mGroupsFragment;
+                }
+            }
+            throw new IllegalArgumentException("position: " + position);
+        }
+
+        @Override
+        public Object instantiateItem(View container, int position) {
+            if (mCurTransaction == null) {
+                mCurTransaction = mFragmentManager.beginTransaction();
+            }
+            Fragment f = getFragment(position);
+            mCurTransaction.show(f);
+            return f;
+        }
+
+        @Override
+        public void destroyItem(View container, int position, Object object) {
+            if (mCurTransaction == null) {
+                mCurTransaction = mFragmentManager.beginTransaction();
+            }
+            mCurTransaction.hide((Fragment) object);
+        }
+
+        @Override
+        public void finishUpdate(View container) {
+            if (mCurTransaction != null) {
+                mCurTransaction.commit();
+                mCurTransaction = null;
+                mFragmentManager.executePendingTransactions();
+            }
+        }
+
+        @Override
+        public boolean isViewFromObject(View view, Object object) {
+            return ((Fragment) object).getView() == view;
+        }
+
+        @Override
+        public Parcelable saveState() {
+            return null;
+        }
+
+        @Override
+        public void restoreState(Parcelable state, ClassLoader loader) {
+        }
+    }
+
     private void clearSearch() {
         loadSearch("");
     }
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 6ba575a..9f9f5a4 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -82,6 +82,9 @@
         <item>DIAL tel</item>
         <item>VIEW tel</item>
         <item>VIEW calls (call-log after a phone call)</item>
+        <item>VIEW calls item</item>
+        <item>CallDetailActivity (legacy)</item>
+        <item>CallLogActivity (legacy)</item>
     </string-array>
 
     <string name="pinnedHeaderList">Pinned Headers</string>
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 71012de..8c37a02 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -29,6 +29,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.CallLog.Calls;
 import android.provider.Contacts.ContactMethods;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
@@ -55,6 +56,7 @@
 public class AllIntentsActivity extends ListActivity
         implements SelectAccountDialogFragment.Listener {
 
+    /** The name of the package of the contacts application. */
     private static final String ANDROID_CONTACTS_PACKAGE = "com.android.contacts";
 
     private static final String CONTACT_LIST_ACTIVITY_CLASS_NAME =
@@ -116,7 +118,10 @@
         CALL_BUTTON,
         DIAL_tel,
         VIEW_tel,
-        VIEW_calllog;
+        VIEW_calllog,
+        VIEW_calllog_entry,
+        LEGACY_CALL_DETAILS_ACTIVITY,
+        LEGACY_CALL_LOG_ACTIVITY;
 
         public static ContactsIntent get(int ordinal) {
             return values()[ordinal];
@@ -208,15 +213,13 @@
             }
             case ACTION_CREATE_SHORTCUT_DIAL: {
                 Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                intent.setComponent(
-                        new ComponentName(ANDROID_CONTACTS_PACKAGE, "alias.DialShortcut"));
+                bindIntentToClass(intent, "alias.DialShortcut");
                 startActivityForResult(intent, 0);
                 break;
             }
             case ACTION_CREATE_SHORTCUT_MESSAGE: {
                 Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                intent.setComponent(
-                        new ComponentName(ANDROID_CONTACTS_PACKAGE, "alias.MessageShortcut"));
+                bindIntentToClass(intent, "alias.MessageShortcut");
                 startActivityForResult(intent, 0);
                 break;
             }
@@ -483,12 +486,57 @@
                 startActivity(intent);
                 break;
             }
+            case VIEW_calllog_entry: {
+                Uri uri = getCallLogUri();
+                if (uri == null) {
+                    Toast.makeText(this, "Call log is empty", Toast.LENGTH_LONG).show();
+                    break;
+                }
+                final Intent intent = new Intent(Intent.ACTION_VIEW);
+                intent.setData(uri);
+                startActivity(intent);
+                break;
+            }
+            case LEGACY_CALL_DETAILS_ACTIVITY: {
+                Uri uri = getCallLogUri();
+                if (uri == null) {
+                    Toast.makeText(this, "Call log is empty", Toast.LENGTH_LONG).show();
+                    break;
+                }
+                final Intent intent = new Intent("android.intent.action.VIEW");
+                intent.setData(uri);
+                bindIntentToClass(intent, "CallDetailActivity");
+                startActivity(intent);
+                break;
+            }
+            case LEGACY_CALL_LOG_ACTIVITY: {
+                startActivity(bindIntentToClass(new Intent(), "activities.CallLogActivity"));
+                break;
+            }
+
             default: {
                 Toast.makeText(this, "Sorry, we forgot to write this...", Toast.LENGTH_LONG).show();
             }
         }
     }
 
+    /** Returns the URI of one of the items in the call log, or null if the call log is empty. */
+    private Uri getCallLogUri() {
+        Cursor cursor = getContentResolver().query(
+                Calls.CONTENT_URI, new String[]{ Calls._ID }, null, null,
+                Calls.DEFAULT_SORT_ORDER);
+        if (!cursor.moveToNext()) {
+            return null;
+        }
+        return ContentUris.withAppendedId(Calls.CONTENT_URI, cursor.getLong(0));
+    }
+
+    /** Creates an intent that is bound to a specific activity by name. */
+    private Intent bindIntentToClass(Intent intent, String activityClassName) {
+        intent.setComponent(new ComponentName(ANDROID_CONTACTS_PACKAGE, activityClassName));
+        return intent;
+    }
+
     private Intent buildFilterIntent(int actionCode, boolean legacy) {
         Intent intent = new Intent(UI.FILTER_CONTACTS_ACTION);
         intent.putExtra(UI.FILTER_TEXT_EXTRA_KEY, "A");
@@ -499,8 +547,7 @@
     }
 
     private void startContactListActivity(Intent intent) {
-        intent.setComponent(
-                new ComponentName(ANDROID_CONTACTS_PACKAGE, CONTACT_LIST_ACTIVITY_CLASS_NAME));
+        bindIntentToClass(intent, CONTACT_LIST_ACTIVITY_CLASS_NAME);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         startActivity(intent);
     }