Merge "Fix NPE in People App on phone when search is pressed."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f10ae75..88395bf 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -38,6 +38,8 @@
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+    <uses-permission android:name="com.android.voicemail.permission.READ_WRITE_OWN_VOICEMAIL" />
+    <uses-permission android:name="com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" />
 
     <application
         android:name="com.android.contacts.ContactsApplication"
diff --git a/res/layout/contact_tile_regular.xml b/res/layout/contact_tile_square.xml
similarity index 96%
rename from res/layout/contact_tile_regular.xml
rename to res/layout/contact_tile_square.xml
index 0c02318..ca79cef 100644
--- a/res/layout/contact_tile_regular.xml
+++ b/res/layout/contact_tile_square.xml
@@ -15,7 +15,7 @@
 -->
 <view
     xmlns:android="http://schemas.android.com/apk/res/android"
-    class="com.android.contacts.list.ContactTileView"
+    class="com.android.contacts.list.ContactTileSquareView"
     android:focusable="true"
     android:padding="1px"
     android:background="@drawable/list_selector" >
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 445a332..14fb137 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -4,9 +4,9 @@
      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.
@@ -18,27 +18,11 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!-- Dialer --> -->
-    <fragment
-        class="com.android.contacts.dialpad.DialpadFragment"
-        android:id="@+id/dialpad_fragment"
+    <android.support.v4.view.ViewPager
+        android:id="@+id/pager"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-    <!-- 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" />
-
-    <!-- Favorites -->
-    <fragment
-        android:id="@+id/favorites_fragment"
-        class="com.android.contacts.list.StrequentContactListFragment"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent" />
-
     <!-- For phone search UI -->
     <fragment
         android:id="@+id/phone_number_picker_fragment"
diff --git a/src/com/android/contacts/activities/DialpadActivity.java b/src/com/android/contacts/activities/DialpadActivity.java
index bb122df..cfe17f3 100644
--- a/src/com/android/contacts/activities/DialpadActivity.java
+++ b/src/com/android/contacts/activities/DialpadActivity.java
@@ -30,7 +30,6 @@
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.Menu;
 import android.view.ViewConfiguration;
 import android.view.Window;
 import android.view.inputmethod.InputMethodManager;
@@ -60,33 +59,12 @@
 
         mFragment = (DialpadFragment) getFragmentManager().findFragmentById(
                 R.id.dialpad_fragment);
-
-        // Manually run the onRestoreInstanceState() sequence here, but only if
-        // our intent does *not* have the DialtactsActivity.EXTRA_IGNORE_STATE
-        // set (see the references to EXTRA_IGNORE_STATE in DialtactsActivity).
-        // TODO: Find a cleaner way of doing this.
-        if (!mFragment.resolveIntent() && (icicle != null)) {
-            super.onRestoreInstanceState(icicle);
-        }
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle icicle) {
-        // Do nothing, state is restored in onCreate() if needed
     }
 
     @Override
     protected void onNewIntent(Intent newIntent) {
         setIntent(newIntent);
-        mFragment.resolveIntent();
-    }
-
-    @Override
-    protected void onPostCreate(Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-
-        // Pass this lifecycle event down to the fragment
-        mFragment.onPostCreate();
+        mFragment.resolveIntent(newIntent);
     }
 
     public DialpadFragment getFragment() {
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index a051dc3..a7a5410 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -21,12 +21,6 @@
 import com.android.contacts.dialpad.DialpadFragment;
 import com.android.contacts.interactions.ImportExportDialogFragment;
 import com.android.contacts.interactions.PhoneNumberInteraction;
-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.list.OnPhoneNumberPickerActionListener;
 import com.android.contacts.list.PhoneNumberPickerFragment;
 import com.android.contacts.list.StrequentContactListFragment;
@@ -53,6 +47,9 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents.UI;
 import android.provider.Settings;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
@@ -75,14 +72,13 @@
 public class DialtactsActivity extends Activity {
     private static final String TAG = "DialtactsActivity";
 
+    /** Used both by {@link ActionBar} and {@link ViewPagerAdapter} */
     private static final int TAB_INDEX_DIALER = 0;
     private static final int TAB_INDEX_CALL_LOG = 1;
     private static final int TAB_INDEX_FAVORITES = 2;
 
     private static final int TAB_INDEX_COUNT = 3;
 
-    public static final String EXTRA_IGNORE_STATE = "ignore-state";
-
     /** Name of the dialtacts shared preferences */
     static final String PREFS_DIALTACTS = "dialtacts";
     static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false;
@@ -91,11 +87,62 @@
     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;
 
+    /**
+     * Listener interface for Fragments accommodated in {@link ViewPager} enabling them to know
+     * when it becomes visible or invisible inside the ViewPager.
+     */
+    public interface ViewPagerVisibilityListener {
+        public void onVisibilityChange(boolean visible);
+    }
+
+    public class ViewPagerAdapter extends FragmentPagerAdapter {
+        public ViewPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            switch (position) {
+                case TAB_INDEX_DIALER:
+                    return mDialpadFragment;
+                case TAB_INDEX_CALL_LOG:
+                    return mCallLogFragment;
+                case TAB_INDEX_FAVORITES:
+                    return mStrequentFragment;
+            }
+            throw new IllegalStateException("No fragment at position " + position);
+        }
+
+        @Override
+        public int getCount() {
+            return TAB_INDEX_COUNT;
+        }
+    }
+
+    private class PageChangeListener implements OnPageChangeListener {
+        @Override
+        public void onPageScrolled(
+                int position, float positionOffset, int positionOffsetPixels) {
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            final ActionBar actionBar = getActionBar();
+            actionBar.selectTab(actionBar.getTabAt(position));
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+        }
+    }
+
     private String mFilterText;
     private Uri mDialUri;
+
+    /** Enables horizontal swipe between Fragments. */
+    private ViewPager mViewPager;
     private DialpadFragment mDialpadFragment;
     private CallLogFragment mCallLogFragment;
-    private DefaultContactBrowseListFragment mContactsFragment;
     private StrequentContactListFragment mStrequentFragment;
 
     /**
@@ -186,32 +233,30 @@
 
         setContentView(R.layout.dialtacts_activity);
 
-        final FragmentManager fragmentManager = getFragmentManager();
-        mDialpadFragment = (DialpadFragment) fragmentManager
-                .findFragmentById(R.id.dialpad_fragment);
+        // Instantiate Fragments which ViewPager will accommodate. At this point they aren't
+        // attached to any Activity, so no Views inside them will be ready yet.
+        mDialpadFragment = new DialpadFragment();
         mDialpadFragment.setListener(new DialpadFragment.Listener() {
             @Override
             public void onSearchButtonPressed() {
                 enterSearchUi();
             }
         });
-        mCallLogFragment = (CallLogFragment) fragmentManager
-                .findFragmentById(R.id.call_log_fragment);
-        mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
-                .findFragmentById(R.id.contacts_fragment);
-        mStrequentFragment = (StrequentContactListFragment) fragmentManager
-                .findFragmentById(R.id.favorites_fragment);
-        mPhoneNumberPickerFragment = (PhoneNumberPickerFragment) fragmentManager
+        mCallLogFragment = new CallLogFragment();
+        mStrequentFragment = new StrequentContactListFragment();
+
+        mViewPager = (ViewPager) findViewById(R.id.pager);
+        mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
+        mViewPager.setOnPageChangeListener(new PageChangeListener());
+
+        // This Fragment is _not_ maintained by ViewPager.
+        mPhoneNumberPickerFragment = (PhoneNumberPickerFragment) getFragmentManager()
                 .findFragmentById(R.id.phone_number_picker_fragment);
         mPhoneNumberPickerFragment.setOnPhoneNumberPickerActionListener(
                 mPhoneNumberPickerActionListener);
         mPhoneNumberPickerFragment.setHighlightSearchPrefix(true);
 
-        // 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(mStrequentFragment);
+        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
         transaction.hide(mPhoneNumberPickerFragment);
         transaction.commit();
 
@@ -272,7 +317,6 @@
         tab.setTabListener(new TabChangeListener(mDialpadFragment));
         tab.setIcon(R.drawable.ic_tab_dialer);
         getActionBar().addTab(tab);
-        mDialpadFragment.resolveIntent();
     }
 
     private void setupCallLog() {
@@ -283,32 +327,6 @@
         getActionBar().addTab(tab);
     }
 
-    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, PeopleActivity.class);
-
-        ContactsIntentResolver resolver = new ContactsIntentResolver(this);
-        ContactsRequest request = resolver.resolveIntent(intent);
-        final ContactListFilter filter = ContactListFilter.createFilterWithType(
-                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 setupFavorites() {
         final Tab tab = getActionBar().newTab();
         tab.setText("");  // R.string.contactsFavoritesLabel
@@ -356,28 +374,23 @@
             return;
         }
 
-        // 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);
-
         // Remember the old manually selected tab index so that it can be restored if it is
         // overwritten by one of the programmatic tab selections
         final int savedTabIndex = mLastManuallySelectedTab;
 
-        if (DialpadFragment.phoneIsInUse()) {
-            getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_DIALER));
+        final int tabIndex;
+        if (DialpadFragment.phoneIsInUse() || isDialIntent(intent)) {
+            tabIndex = TAB_INDEX_DIALER;
         } else if (recentCallsRequest) {
-            getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CALL_LOG));
+            tabIndex = TAB_INDEX_CALL_LOG;
         } else {
-            getActionBar().selectTab(getActionBar().getTabAt(mLastManuallySelectedTab));
+            tabIndex = mLastManuallySelectedTab;
         }
+        mViewPager.setCurrentItem(tabIndex);
+        getActionBar().selectTab(getActionBar().getTabAt(tabIndex));
 
         // Restore to the previous manual selection
         mLastManuallySelectedTab = savedTabIndex;
-
-        // Tell the children activities that they should honor their saved states
-        // instead of the state from the parent's intent
-        intent.putExtra(EXTRA_IGNORE_STATE, false);
     }
 
     @Override
@@ -391,8 +404,9 @@
         } else if (isDialIntent(newIntent)) {
             setupDialUri(newIntent);
         }
-        // Fill in a phone number again.
-        mDialpadFragment.resolveIntent();
+        if (mPhoneNumberPickerFragment.isVisible()) {
+            exitSearchUi();
+        }
     }
 
     /** Returns true if the given intent contains a phone number to populate the dialer with */
@@ -484,14 +498,6 @@
         }
     }
 
-    @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
@@ -505,12 +511,19 @@
 
         @Override
         public void onTabUnselected(Tab tab, FragmentTransaction ft) {
-            ft.hide(mFragment);
+            if (mFragment instanceof ViewPagerVisibilityListener) {
+                ((ViewPagerVisibilityListener) mFragment).onVisibilityChange(false);
+            }
         }
 
         @Override
         public void onTabSelected(Tab tab, FragmentTransaction ft) {
-            ft.show(mFragment);
+            if (mFragment instanceof ViewPagerVisibilityListener) {
+                ((ViewPagerVisibilityListener) mFragment).onVisibilityChange(true);
+            }
+            if (mViewPager.getCurrentItem() != tab.getPosition()) {
+                mViewPager.setCurrentItem(tab.getPosition(), false /* smoothScroll */);
+            }
             ft.hide(mPhoneNumberPickerFragment);
 
             // During the call, we don't remember the tab position.
@@ -527,56 +540,6 @@
         }
     }
 
-    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) {
-            PhoneNumberInteraction.startInteractionForPhoneCall(
-                    DialtactsActivity.this, contactUri);
-        }
-
-        @Override
-        public void onAddToFavoritesAction(Uri contactUri) {
-        }
-    };
-
     private StrequentContactListFragment.Listener mStrequentListener =
             new StrequentContactListFragment.Listener() {
         @Override
@@ -674,6 +637,8 @@
             mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
             mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
             mSearchView.requestFocus();
+            // Show soft keyboard when SearchView has a focus. Need to delay the request in order
+            // to let InputMethodManager handle it correctly.
             mSearchView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
                 @Override
                 public void onViewDetachedFromWindow(View v) {
@@ -702,10 +667,8 @@
         // Show the search fragment and hide everything else.
         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
         transaction.show(mPhoneNumberPickerFragment);
-        transaction.hide(mDialpadFragment);
-        transaction.hide(mCallLogFragment);
-        transaction.hide(mStrequentFragment);
         transaction.commit();
+        mViewPager.setVisibility(View.GONE);
 
         mInSearchUi = true;
     }
@@ -728,6 +691,12 @@
         actionBar.setDisplayShowCustomEnabled(false);
         actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
 
+        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+        transaction.hide(mPhoneNumberPickerFragment);
+        transaction.commit();
+
+        mViewPager.setVisibility(View.VISIBLE);
+
         // Request to update option menu.
         invalidateOptionsMenu();
 
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 31373cc..8b09e28 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -21,6 +21,7 @@
 import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
+import com.android.contacts.activities.DialtactsActivity.ViewPagerVisibilityListener;
 import com.android.contacts.util.ExpirableCache;
 import com.android.internal.telephony.CallerInfo;
 import com.google.common.annotations.VisibleForTesting;
@@ -43,7 +44,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.Contacts;
@@ -75,7 +75,7 @@
  * Displays a list of call log entries.
  */
 public class CallLogFragment extends ListFragment
-        implements View.OnCreateContextMenuListener {
+        implements View.OnCreateContextMenuListener, ViewPagerVisibilityListener {
     private static final String TAG = "CallLogFragment";
 
     /**
@@ -144,6 +144,9 @@
     private String mCurrentCountryIso;
     private boolean mScrollToTop;
 
+    private MenuItem mDeleteAllCallLogMenuItem;
+    private boolean mShowMenu;
+
     public static final class ContactInfo {
         public long personId;
         public String name;
@@ -315,7 +318,7 @@
             values.put(Calls.CACHED_NUMBER_LABEL, ci.label);
 
             try {
-                getActivity().getContentResolver().update(Calls.CONTENT_URI, values,
+                getActivity().getContentResolver().update(Calls.CONTENT_URI_WITH_VOICEMAIL, values,
                         Calls.NUMBER + "='" + ciq.number + "'", null);
             } catch (SQLiteDiskIOException e) {
                 Log.w(TAG, "Exception while updating call info", e);
@@ -886,7 +889,7 @@
 
         ContentValues values = new ContentValues(1);
         values.put(Calls.NEW, "0");
-        mQueryHandler.startUpdate(UPDATE_TOKEN, null, Calls.CONTENT_URI,
+        mQueryHandler.startUpdate(UPDATE_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
                 values, where.toString(), null);
     }
 
@@ -895,15 +898,21 @@
 
         // Cancel any pending queries
         mQueryHandler.cancelOperation(QUERY_TOKEN);
-        mQueryHandler.startQuery(QUERY_TOKEN, null, Calls.CONTENT_URI,
+        mQueryHandler.startQuery(QUERY_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
                 CallLogQuery._PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
     }
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
-        menu.add(0, OptionsMenuItems.DELETE_ALL, 0, R.string.recentCalls_deleteAll).setIcon(
-                android.R.drawable.ic_menu_close_clear_cancel);
+        mDeleteAllCallLogMenuItem = menu.add(0, OptionsMenuItems.DELETE_ALL,
+                0, R.string.recentCalls_deleteAll)
+                .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        mDeleteAllCallLogMenuItem.setVisible(mShowMenu);
     }
 
     @Override
@@ -1028,7 +1037,7 @@
                     sb.append(id);
                 }
 
-                getActivity().getContentResolver().delete(Calls.CONTENT_URI,
+                getActivity().getContentResolver().delete(Calls.CONTENT_URI_WITH_VOICEMAIL,
                         Calls._ID + " IN (" + sb + ")", null);
             }
         }
@@ -1121,7 +1130,7 @@
             mAdapter.toggleGroup(position);
         } else {
             Intent intent = new Intent(getActivity(), CallDetailActivity.class);
-            intent.setData(ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI, id));
+            intent.setData(ContentUris.withAppendedId(Calls.CONTENT_URI_WITH_VOICEMAIL, id));
             startActivity(intent);
         }
     }
@@ -1135,4 +1144,9 @@
     public String getVoiceMailNumber() {
         return mVoiceMailNumber;
     }
+
+    @Override
+    public void onVisibilityChange(boolean visible) {
+        mShowMenu = visible;
+    }
 }
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 2097a1d..70dd4b1 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -20,6 +20,7 @@
 import com.android.contacts.R;
 import com.android.contacts.SpecialCharSequenceMgr;
 import com.android.contacts.activities.DialtactsActivity;
+import com.android.contacts.activities.DialtactsActivity.ViewPagerVisibilityListener;
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.CallLogAsync;
 import com.android.phone.HapticFeedback;
@@ -73,7 +74,8 @@
 public class DialpadFragment extends Fragment
         implements View.OnClickListener,
         View.OnLongClickListener, View.OnKeyListener,
-        AdapterView.OnItemClickListener, TextWatcher {
+        AdapterView.OnItemClickListener, TextWatcher,
+        ViewPagerVisibilityListener {
     private static final String TAG = "DialpadFragment";
 
     private static final String EMPTY_NUMBER = "";
@@ -107,7 +109,8 @@
     private View mDialButton;
     private ListView mDialpadChooser;
     private DialpadChooserAdapter mDialpadChooserAdapter;
-    //Member variables for dialpad options
+
+    // Member variables for dialpad options
     private MenuItem m2SecPauseMenuItem;
     private MenuItem mWaitMenuItem;
     private MenuItem mCallSettingsItem;
@@ -116,6 +119,8 @@
     private static final int MENU_WAIT = 3;
     private static final int MENU_CALL_SETTINGS = 4;
 
+    private boolean mShowMenu;
+
     private boolean mHasVoicemail = false;
 
     // Last number dialed, retrieved asynchronously from the call DB
@@ -231,6 +236,7 @@
         mDigits.setKeyListener(DialerKeyListener.getInstance());
         mDigits.setOnClickListener(this);
         mDigits.setOnKeyListener(this);
+        mDigits.addTextChangedListener(this);
 
         maybeAddNumberFormatting();
 
@@ -278,6 +284,8 @@
         mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
         mDialpadChooser.setOnItemClickListener(this);
 
+        resolveIntent(getActivity().getIntent());
+
         return fragmentView;
     }
 
@@ -301,19 +309,9 @@
      *    any possible saved state, and instead reset our state based on the parent's
      *    intent.
      */
-    public boolean resolveIntent() {
+    public boolean resolveIntent(Intent intent) {
         boolean ignoreState = false;
 
-        // Find the proper intent
-        final Intent intent;
-        if (getActivity().isChild()) {
-            intent = getActivity().getParent().getIntent();
-            ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
-        } else {
-            intent = getActivity().getIntent();
-        }
-        // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
-
         // by default we are not adding a call.
         mIsAddCallMode = false;
 
@@ -420,16 +418,6 @@
         fragmentView.findViewById(R.id.pound).setOnClickListener(this);
     }
 
-    // Do some stuff that needs to happen only once, but which we
-    // can't do directly from onCreate().
-    public void onPostCreate() {
-        // This can't be done in onCreate(), since the auto-restoring of the digits
-        // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
-        // is called. This method will be called every time the activity is created, and
-        // will always happen after onRestoreSavedInstanceState().
-        mDigits.addTextChangedListener(this);
-    }
-
     @Override
     public void onResume() {
         super.onResume();
@@ -462,13 +450,13 @@
             }
         }
 
-        Activity parent = getActivity().getParent();
+        Activity parent = getActivity();
         // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
         // digits in the dialer field.
-        if (parent != null && parent instanceof DialtactsActivity) {
+        if (parent instanceof DialtactsActivity) {
             Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
             if (dialUri != null) {
-                resolveIntent();
+                resolveIntent(parent.getIntent());
             }
         }
 
@@ -543,7 +531,14 @@
             return;
         }
 
-        // We show "Call Settings" menu every time
+        if (!mShowMenu) {
+            mCallSettingsItem.setVisible(false);
+            mAddToContactMenuItem.setVisible(false);
+            m2SecPauseMenuItem.setVisible(false);
+            mWaitMenuItem.setVisible(false);
+            return;
+        }
+
         mCallSettingsItem.setVisible(true);
         Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
         settingsIntent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
@@ -1237,4 +1232,9 @@
     public void setListener(Listener listener) {
         mListener = listener;
     }
+
+    @Override
+    public void onVisibilityChange(boolean visible) {
+        mShowMenu = visible;
+    }
 }
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 86f6b33..5d5e6cb 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -40,7 +40,7 @@
  * Also allows for a configurable number of columns and {@link DisplayType}
  */
 public class ContactTileAdapter extends BaseAdapter {
-    private static final String TAG = "ContactTileAdapter";
+    private static final String TAG = ContactTileAdapter.class.getSimpleName();
 
     /**
      * mContacts2 is only used if {@link DisplayType} is Strequent
@@ -292,7 +292,7 @@
         int columnCount = -1;
 
         switch (itemViewType) {
-            case ViewTypes.REGULAR:
+            case ViewTypes.SQUARE:
                 if (contactTileRowView == null) {
                     // Creating new row if needed
                     contactTileRowView = new ContactTileRow(mContext, layoutResId, true);
@@ -327,8 +327,8 @@
 
     private int getLayoutResourceId(int viewType) {
         switch (viewType) {
-            case ViewTypes.REGULAR:
-                return R.layout.contact_tile_regular;
+            case ViewTypes.SQUARE:
+                return R.layout.contact_tile_square;
             case ViewTypes.SINGLE_ROW:
                 return R.layout.contact_tile_single;
             default:
@@ -343,8 +343,8 @@
     /**
      * Returns view type based on {@link DisplayType}.
      * {@link DisplayType#STARRED_ONLY} and {@link DisplayType#GROUP_MEMBERS}
-     * are {@link ViewTypes#REGULAR}.
-     * {@link DisplayType#FREQUENT_ONLY} is {@link ViewTypes#SMALL}.
+     * are {@link ViewTypes#SQUARE}.
+     * {@link DisplayType#FREQUENT_ONLY} is {@link ViewTypes#SINGLE_ROW}.
      * {@link DisplayType#STREQUENT} mixes both {@link ViewTypes}
      * and also adds in {@link ViewTypes#DIVIDER}.
      */
@@ -353,7 +353,7 @@
         switch (mDisplayType) {
             case STREQUENT:
                 if (position < mDividerRowIndex) {
-                    return ViewTypes.REGULAR;
+                    return ViewTypes.SQUARE;
                 } else if (position == mDividerRowIndex) {
                     return ViewTypes.DIVIDER;
                 } else {
@@ -361,7 +361,7 @@
                 }
             case STARRED_ONLY:
             case GROUP_MEMBERS:
-                return ViewTypes.REGULAR;
+                return ViewTypes.SQUARE;
             case FREQUENT_ONLY:
                 return ViewTypes.SINGLE_ROW;
             default:
@@ -399,8 +399,11 @@
             ContactTileView contactTile;
 
             if (getChildCount() <= tileIndex) {
-                contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
-                contactTile.setIsSquare(mIsContactTileSquare);
+                if (mIsContactTileSquare) {
+                    contactTile = (ContactTileSquareView) inflate(mContext, mLayoutResId, null);
+                } else {
+                    contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
+                }
                 contactTile.setLayoutParams(new LinearLayout.LayoutParams(0,
                         LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f));
                 contactTile.setPhotoManager(mPhotoManager);
@@ -430,7 +433,7 @@
 
     private static class ViewTypes {
         public static final int COUNT = 3;
-        public static final int REGULAR = 0;
+        public static final int SQUARE = 0;
         public static final int DIVIDER = 1;
         public static final int SINGLE_ROW = 2;
     }
diff --git a/src/com/android/contacts/list/ContactTileSquareView.java b/src/com/android/contacts/list/ContactTileSquareView.java
new file mode 100644
index 0000000..7716481
--- /dev/null
+++ b/src/com/android/contacts/list/ContactTileSquareView.java
@@ -0,0 +1,41 @@
+/*
+ * 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.list;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * A ContactTileSquare displays the contact's picture overlayed with their name
+ * in a perfect square.
+ */
+public class ContactTileSquareView extends ContactTileView {
+    private final static String TAG = ContactTileSquareView.class.getSimpleName();
+
+    public ContactTileSquareView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Getting how much space is currently available and telling our
+        // Children to split it.
+        int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
+        int childMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+        measureChildren(childMeasureSpec, childMeasureSpec);
+        setMeasuredDimension(width, width);
+    }
+}
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 883e3f0..ad711cf 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -32,21 +32,12 @@
  * A ContactTile displays the contact's picture overlayed with their name
  */
 public class ContactTileView extends FrameLayout {
-    private final static String TAG = "ContactTileView";
+    private final static String TAG = ContactTileView.class.getSimpleName();
 
-    /**
-     * This divides into the width to define the height when
-     * {link DisplayTypes@SINLGE_ROW} is true.
-     */
-    private final static int HEIGHT_RATIO = 5;
     private Uri mLookupUri;
     private ImageView mPhoto;
     private TextView mName;
     private ContactPhotoManager mPhotoManager = null;
-    /**
-     * Is set to true if the {@link ContactTileView} is a square.
-     */
-    private boolean mIsSquare;
 
     public ContactTileView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -59,18 +50,14 @@
         mPhoto = (ImageView) findViewById(R.id.contact_tile_image);
     }
 
-    public boolean isSquare() {
-        return mIsSquare;
-    }
-
-    public void setIsSquare(boolean isSquare) {
-        mIsSquare = isSquare;
-    }
-
     public void setPhotoManager(ContactPhotoManager photoManager) {
         mPhotoManager = photoManager;
     }
 
+    /**
+     * Populates the data members to be displayed from the
+     * fields in {@link ContactEntry}
+     */
     public void loadFromContact(ContactEntry entry) {
         if (entry != null) {
             mName.setText(entry.name);
@@ -91,14 +78,4 @@
     public Uri getLookupUri() {
         return mLookupUri;
     }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Getting how much space is currently available and telling our
-        // Children to split it.
-        int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
-        int childMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
-        measureChildren(childMeasureSpec, childMeasureSpec);
-        setMeasuredDimension(width, width / (mIsSquare ? 1 : HEIGHT_RATIO));
-    }
 }
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index e9697d3..6dc2c34 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -237,7 +237,7 @@
         final Notification notification =
                 VCardService.constructProgressNotification(mService, VCardService.TYPE_EXPORT,
                         description, tickerText, mJobId, displayName, totalCount, currentCount);
-        mNotificationManager.notify(mJobId, notification);
+        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
     }
 
     private void doCancelNotification() {
@@ -246,7 +246,7 @@
                 mExportRequest.destUri.getLastPathSegment());
         final Notification notification =
                 VCardService.constructCancelNotification(mService, description);
-        mNotificationManager.notify(mJobId, notification);
+        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
     }
 
     private void doFinishNotification(final String title, final String description) {
@@ -254,7 +254,7 @@
         final Intent intent = new Intent(mService, PeopleActivity.class);
         final Notification notification =
                 VCardService.constructFinishNotification(mService, title, description, intent);
-        mNotificationManager.notify(mJobId, notification);
+        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
     }
 
     @Override
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index 4ea1ead..1f5779a 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -180,7 +180,7 @@
                 mImportRequest.originalUri.getLastPathSegment());
         final Notification notification =
                 VCardService.constructCancelNotification(mService, description);
-        mNotificationManager.notify(mJobId, notification);
+        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
     }
 
     private void doFinishNotification(final Uri createdUri) {
@@ -199,7 +199,7 @@
         final Notification notification =
                    VCardService.constructFinishNotification(mService,
                            description, null, intent);
-        mNotificationManager.notify(mJobId, notification);
+        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
     }
 
     private boolean readOneVCard(Uri uri, int vcardType, String charset,
diff --git a/src/com/android/contacts/vcard/ImportProgressNotifier.java b/src/com/android/contacts/vcard/ImportProgressNotifier.java
index d6d0f3f..698487d 100644
--- a/src/com/android/contacts/vcard/ImportProgressNotifier.java
+++ b/src/com/android/contacts/vcard/ImportProgressNotifier.java
@@ -71,7 +71,7 @@
         final Notification notification = VCardService.constructProgressNotification(
                 mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
                 mJobId, mDisplayName, mTotalCount, mCurrentCount);
-        mNotificationManager.notify(mJobId, notification);
+        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
     }
 
     public synchronized void addTotalCount(int additionalCount) {
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 1397dd7..2cfd71a 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -107,7 +107,7 @@
     /**
      * Notification id used when error happened before sending an import request to VCardServer.
      */
-    private static final int DEFAULT_NOTIFICATION_ID = 1000;
+    private static final int FAILURE_NOTIFICATION_ID = 1;
 
     final static String CACHED_URIS = "cached_uris";
 
@@ -974,7 +974,8 @@
                 VCardService.constructImportFailureNotification(
                         ImportVCardActivity.this,
                         getString(reasonId));
-        notificationManager.notify(DEFAULT_NOTIFICATION_ID, notification);
+        notificationManager.notify(VCardService.FAILURE_NOTIFICATION_TAG, FAILURE_NOTIFICATION_ID,
+                notification);
         mHandler.post(new Runnable() {
             @Override
             public void run() {
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index e927757..261c1c8 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -59,6 +59,17 @@
 // works fine enough. Investigate the feasibility.
 public class VCardService extends Service {
     private final static String LOG_TAG = "VCardService";
+
+    /** The tag used by vCard-related notifications. */
+    /* package */ static final String DEFAULT_NOTIFICATION_TAG = "VCardServiceProgress";
+    /**
+     * The tag used by vCard-related failure notifications.
+     * <p>
+     * Use a different tag from {@link #DEFAULT_NOTIFICATION_TAG} so that failures do not get
+     * replaced by other notifications and vice-versa.
+     */
+    /* package */ static final String FAILURE_NOTIFICATION_TAG = "VCardServiceFailure";
+
     /* package */ final static boolean DEBUG = false;
 
     /* package */ static final int MSG_IMPORT_REQUEST = 1;
@@ -153,6 +164,7 @@
     /* ** vCard exporter params ** */
     // If true, VCardExporter is able to emits files longer than 8.3 format.
     private static final boolean ALLOW_LONG_FILE_NAME = false;
+
     private String mTargetDirectory;
     private String mFileNamePrefix;
     private String mFileNameSuffix;
@@ -258,7 +270,8 @@
                         constructProgressNotification(
                                 this, TYPE_IMPORT, message, message, mCurrentJobId,
                                 displayName, -1, 0);
-                mNotificationManager.notify(mCurrentJobId, notification);
+                mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mCurrentJobId,
+                        notification);
                 mCurrentJobId++;
             } else {
                 // TODO: a little unkind to show Toast in this case, which is shown just a moment.
@@ -292,7 +305,7 @@
             final Notification notification =
                     constructProgressNotification(this, TYPE_EXPORT, message, message,
                             mCurrentJobId, displayName, -1, 0);
-            mNotificationManager.notify(mCurrentJobId, notification);
+            mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mCurrentJobId, notification);
             mCurrentJobId++;
         } else {
             Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
@@ -330,7 +343,7 @@
                     getString(R.string.importing_vcard_canceled_title, request.displayName) :
                             getString(R.string.exporting_vcard_canceled_title, request.displayName);
             final Notification notification = constructCancelNotification(this, description);
-            mNotificationManager.notify(jobId, notification);
+            mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, jobId, notification);
             if (processor.getType() == TYPE_EXPORT) {
                 final String path =
                         ((ExportProcessor)processor).getRequest().destUri.getEncodedPath();