diff options
author | Isaac Katzenelson <isaack@android.com> | 2011-07-29 18:24:53 -0700 |
---|---|---|
committer | Isaac Katzenelson <isaack@android.com> | 2011-08-08 12:57:08 -0700 |
commit | ead19c5eafee0ffb43b02a4ae75ac5244ad3f853 (patch) | |
tree | d30838c543d661435151a7af3e15195b0c47e479 | |
parent | 485f5eb5529c69c4864516c2e2762ab581c7e4ca (diff) | |
download | packages_apps_Contacts-ead19c5eafee0ffb43b02a4ae75ac5244ad3f853.tar.gz packages_apps_Contacts-ead19c5eafee0ffb43b02a4ae75ac5244ad3f853.tar.bz2 packages_apps_Contacts-ead19c5eafee0ffb43b02a4ae75ac5244ad3f853.zip |
Support for local profile
Bug: 5121834 Support local profile
5086184 Account name is overlapped by number of contacts
5082317 Text is chopped on the top in contact list
1. New headers were added at the top of the contact list to
present an empty local profile.
2. Clicking the empty local profile opens the editor to allow
the user to create a local profile.
3. Profiles are shown at the top of the contacts list with the
"ME" header and the number of contatcs.
4. "Add to contacts" button and the starred button were removed
from the details view when it is a profile.
5. Fixed a problem with a header view that remained when you had
a profile or was in search mode.
6. Fixed problem with contacts count apearing in search mode
Change-Id: I45615616e03a603759888d9e7169a853b3328b14
23 files changed, 468 insertions, 182 deletions
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contacts_list_content.xml index 92841b61e..567e63df7 100644 --- a/res/layout/contacts_list_content.xml +++ b/res/layout/contacts_list_content.xml @@ -18,68 +18,54 @@ android:layout_marginTop is ignored when used with <fragment></fragment>, which only happens in Tablet UI since we rely on ViewPager in Phone UI. Instead, android:layout_marginTop inside <fragment /> is effective. --> -<FrameLayout + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pinned_header_list_layout" android:paddingTop="@dimen/contact_browser_list_top_margin" + android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > + + <!-- Shown only when an Account filter is set. --> <LinearLayout + android:id="@+id/account_filter_header_container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - - <!-- Shown only when an Account filter is set. --> - <LinearLayout - android:id="@+id/account_filter_header_container" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin" + android:layout_marginRight="@dimen/contact_browser_list_header_right_margin" + android:visibility="gone"> + <TextView + android:id="@+id/account_filter_header" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin" - android:layout_marginRight="@dimen/contact_browser_list_header_right_margin" - android:visibility="gone"> - <TextView - android:id="@+id/account_filter_header" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="@dimen/contact_filter_header_min_height" - android:layout_marginLeft="8dip" - android:singleLine="true" - android:ellipsize="end" - android:gravity="left" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="?android:attr/textColorSecondary" /> - <View - android:id="@+id/account_filter_header_bottom_divider" - style="@style/SectionDivider" /> - </LinearLayout> + android:minHeight="@dimen/contact_filter_header_min_height" + android:layout_marginLeft="8dip" + android:singleLine="true" + android:ellipsize="end" + android:gravity="left" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" /> + <View + android:id="@+id/account_filter_header_bottom_divider" + style="@style/SectionDivider" /> + </LinearLayout> - <view - class="com.android.contacts.list.ContactEntryListView" - android:id="@android:id/list" - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_marginLeft="@dimen/contact_browser_list_left_margin" - android:layout_marginRight="@dimen/contact_browser_list_right_margin" - android:fastScrollEnabled="true" - android:layout_weight="1" /> + <view + class="com.android.contacts.list.ContactEntryListView" + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dip" + android:layout_marginLeft="@dimen/contact_browser_list_left_margin" + android:layout_marginRight="@dimen/contact_browser_list_right_margin" + android:fastScrollEnabled="true" + android:layout_weight="1" /> - <ViewStub - android:id="@+id/footer_stub" - android:layout="@layout/footer_panel" - android:layout_width="fill_parent" - android:layout_height="wrap_content" /> - </LinearLayout> - <TextView - android:id="@+id/contacts_count" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginRight="@dimen/contacts_count_right_margin" - android:singleLine="true" - android:ellipsize="end" - android:gravity="right" - android:layout_gravity="top|right" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="@color/contact_count_text_color" - android:background="@color/contact_browser_list_bk_color" /> -</FrameLayout> + <ViewStub + android:id="@+id/footer_stub" + android:layout="@layout/footer_panel" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + +</LinearLayout> diff --git a/res/layout/user_profile_button.xml b/res/layout/user_profile_button.xml new file mode 100644 index 000000000..b7b5e1d7e --- /dev/null +++ b/res/layout/user_profile_button.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. +--> + +<Button + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="@dimen/contact_browser_list_left_margin" + android:singleLine="true" + android:text="@string/profile_display_name" + android:ellipsize="end" + android:gravity="left|center_vertical" + android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/res/layout/user_profile_header.xml b/res/layout/user_profile_header.xml new file mode 100644 index 000000000..ae803bae2 --- /dev/null +++ b/res/layout/user_profile_header.xml @@ -0,0 +1,60 @@ +<?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:background="@color/contact_browser_list_bk_color" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <TextView + android:id="@+id/profile_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/contact_browser_list_left_margin" + android:singleLine="true" + android:text="@string/user_profile_contacts_list_header" + android:textStyle="bold" + android:ellipsize="end" + android:gravity="left" + android:layout_weight="1" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/people_app_theme_color" /> + + <TextView + android:id="@+id/contacts_count" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="end" + android:layout_gravity="right" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/contact_count_text_color" /> + </LinearLayout> + + <View + android:background="@color/people_app_theme_color" + android:layout_marginLeft="@dimen/contact_browser_list_left_margin" + android:layout_width="match_parent" + android:layout_height="1px" /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 79d12f93a..4d4050e13 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -95,6 +95,7 @@ <attr name="list_item_header_height" format="dimension" /> <attr name="list_item_header_underline_height" format="dimension" /> <attr name="list_item_header_underline_color" format="color" /> + <attr name="list_item_contacts_count_text_color" format="color" /> </declare-styleable> <declare-styleable name="CallLog"> diff --git a/res/values/strings.xml b/res/values/strings.xml index abe1d015e..87461d70a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1598,7 +1598,7 @@ <!-- Text displayed in place of the display name for the contact that represents the user's personal profile entry [CHAR LIMIT=64] --> - <string name="profile_display_name">My profile</string> + <string name="profile_display_name">Set up my profile</string> <!-- Label to instruct the user to type in a contact's name to add the contact as a member of the current group. [CHAR LIMIT=64] --> <string name="enter_contact_name">Enter contact\'s name</string> @@ -1755,4 +1755,6 @@ <!-- The string used to represent an unknown location for a phone number in the call log [CHAR LIMIT=3] --> <string name="call_log_empty_gecode">-</string> + <!-- String describing the text on the header of the profile contact in the contacts list [CHAR LIMIT=20] --> + <string name="user_profile_contacts_list_header">ME</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index b25f0afb1..423466f7d 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -149,6 +149,7 @@ <item name="list_item_header_height">26dip</item> <item name="list_item_header_underline_height">1px</item> <item name="list_item_header_underline_color">@color/people_app_theme_color</item> + <item name="list_item_contacts_count_text_color">@color/contact_count_text_color</item> <item name="contact_filter_popup_width">320dip</item> <!-- Favorites --> <item name="favorites_padding_bottom">0dip</item> diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java index 9daa1e0e0..841672162 100644 --- a/src/com/android/contacts/ContactLoader.java +++ b/src/com/android/contacts/ContactLoader.java @@ -128,6 +128,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { private byte[] mPhotoBinaryData; private boolean mSendToVoicemail; private String mCustomRingtone; + private boolean mIsUserProfile; /** * Constructor for case "no contact found". This must only be used for the @@ -154,6 +155,8 @@ public class ContactLoader extends Loader<ContactLoader.Result> { mInvitableAccountTypes = null; mSendToVoicemail = false; mCustomRingtone = null; + mIsUserProfile = false; + } /** @@ -162,7 +165,8 @@ public class ContactLoader extends Loader<ContactLoader.Result> { private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id, long nameRawContactId, int displayNameSource, long photoId, String photoUri, String displayName, String altDisplayName, String phoneticName, boolean starred, - Integer presence, boolean sendToVoicemail, String customRingtone) { + Integer presence, boolean sendToVoicemail, String customRingtone, + boolean isUserProfile) { mLookupUri = lookupUri; mUri = uri; mDirectoryId = directoryId; @@ -183,6 +187,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { mInvitableAccountTypes = Lists.newArrayList(); mSendToVoicemail = sendToVoicemail; mCustomRingtone = customRingtone; + mIsUserProfile = isUserProfile; } private Result(Result from) { @@ -217,6 +222,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { mPhotoBinaryData = from.mPhotoBinaryData; mSendToVoicemail = from.mSendToVoicemail; mCustomRingtone = from.mCustomRingtone; + mIsUserProfile = from.mIsUserProfile; } /** @@ -394,6 +400,10 @@ public class ContactLoader extends Loader<ContactLoader.Result> { public String getCustomRingtone() { return mCustomRingtone; } + + public boolean isUserProfile() { + return mIsUserProfile; + } } /** @@ -471,6 +481,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { Contacts.PHOTO_URI, Contacts.SEND_TO_VOICEMAIL, Contacts.CUSTOM_RINGTONE, + Contacts.IS_USER_PROFILE, }; public final static int NAME_RAW_CONTACT_ID = 0; @@ -542,6 +553,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { public final static int PHOTO_URI = 61; public final static int SEND_TO_VOICEMAIL = 62; public final static int CUSTOM_RINGTONE = 63; + public final static int IS_USER_PROFILE = 64; } /** @@ -808,6 +820,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { : cursor.getInt(ContactQuery.CONTACT_PRESENCE); final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1; final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE); + final boolean isUserProfile = cursor.getInt(ContactQuery.IS_USER_PROFILE) == 1; Uri lookupUri; if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) { @@ -820,7 +833,7 @@ public class ContactLoader extends Loader<ContactLoader.Result> { return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId, nameRawContactId, displayNameSource, photoId, photoUri, displayName, altDisplayName, phoneticName, starred, presence, sendToVoicemail, - customRingtone); + customRingtone, isUserProfile); } /** diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java index 221796a60..3bb330fa0 100644 --- a/src/com/android/contacts/ContactSaveService.java +++ b/src/com/android/contacts/ContactSaveService.java @@ -46,6 +46,7 @@ import android.provider.ContactsContract.CommonDataKinds.GroupMembership; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Groups; +import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; import android.util.Log; import android.widget.Toast; @@ -75,6 +76,7 @@ public class ContactSaveService extends IntentService { public static final String ACTION_SAVE_CONTACT = "saveContact"; public static final String EXTRA_CONTACT_STATE = "state"; public static final String EXTRA_SAVE_MODE = "saveMode"; + public static final String EXTRA_SAVE_IS_PROFILE = "saveIsProfile"; public static final String ACTION_CREATE_GROUP = "createGroup"; public static final String ACTION_RENAME_GROUP = "renameGroup"; @@ -269,12 +271,13 @@ public class ContactSaveService extends IntentService { * using data presented as a set of ContentValues. */ public static Intent createSaveContactIntent(Context context, EntityDeltaList state, - String saveModeExtraKey, int saveMode, Class<?> callbackActivity, + String saveModeExtraKey, int saveMode, boolean isProfile, Class<?> callbackActivity, String callbackAction) { Intent serviceIntent = new Intent( context, ContactSaveService.class); serviceIntent.setAction(ContactSaveService.ACTION_SAVE_CONTACT); serviceIntent.putExtra(EXTRA_CONTACT_STATE, (Parcelable) state); + serviceIntent.putExtra(EXTRA_SAVE_IS_PROFILE, isProfile); // Callback intent will be invoked by the service once the contact is // saved. The service will put the URI of the new contact as "data" on @@ -289,6 +292,7 @@ public class ContactSaveService extends IntentService { private void saveContact(Intent intent) { EntityDeltaList state = intent.getParcelableExtra(EXTRA_CONTACT_STATE); Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); + boolean isProfile = intent.getBooleanExtra(EXTRA_SAVE_IS_PROFILE, false); // Trim any empty fields, and RawContacts, before persisting final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this); @@ -321,7 +325,8 @@ public class ContactSaveService extends IntentService { throw new IllegalStateException("Could not determine RawContact ID after save"); } final Uri rawContactUri = ContentUris.withAppendedId( - RawContacts.CONTENT_URI, rawContactId); + isProfile ? Profile.CONTENT_RAW_CONTACTS_URI : RawContacts.CONTENT_URI, + rawContactId); lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri); Log.v(TAG, "Saved contact. New URI: " + lookupUri); break; diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java index 0c59695c3..3bff950cd 100644 --- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java +++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java @@ -209,7 +209,7 @@ public class ContactDetailDisplayUtils { */ public static void setStarred(Result contactData, CheckBox starredView) { // Check if the starred state should be visible - if (!contactData.isDirectoryEntry()) { + if (!contactData.isDirectoryEntry() && !contactData.isUserProfile()) { starredView.setVisibility(View.VISIBLE); starredView.setChecked(contactData.getStarred()); } else { diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java index 02e74dd1d..383f23bf3 100644 --- a/src/com/android/contacts/detail/ContactDetailFragment.java +++ b/src/com/android/contacts/detail/ContactDetailFragment.java @@ -1851,6 +1851,9 @@ public class ContactDetailFragment extends Fragment implements FragmentKeyListen // Only local contacts if (mContactData == null || mContactData.isDirectoryEntry()) return false; + // User profile cannot be added to contacts + if (mContactData.isUserProfile()) return false; + // Only if exactly one raw contact if (mContactData.getEntities().size() != 1) return false; @@ -1923,7 +1926,7 @@ public class ContactDetailFragment extends Fragment implements FragmentKeyListen // and fire off the intent. we don't need a callback, as the database listener // should update the ui final Intent intent = ContactSaveService.createSaveContactIntent(getActivity(), - contactDeltaList, "", 0, getActivity().getClass(), + contactDeltaList, "", 0, false, getActivity().getClass(), UI.LIST_ALL_CONTACTS_ACTION); getActivity().startService(intent); } diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java index 0227b1320..41264250c 100644 --- a/src/com/android/contacts/editor/ContactEditorFragment.java +++ b/src/com/android/contacts/editor/ContactEditorFragment.java @@ -116,6 +116,8 @@ public class ContactEditorFragment extends Fragment implements private static final String KEY_SHOW_JOIN_SUGGESTIONS = "showJoinSuggestions"; private static final String KEY_ENABLED = "enabled"; private static final String KEY_STATUS = "status"; + private static final String KEY_NEW_LOCAL_PROFILE = "newLocalProfile"; + private static final String KEY_IS_USER_PROFILE = "isUserProfile"; public static final String SAVE_MODE_EXTRA_KEY = "saveMode"; @@ -125,6 +127,8 @@ public class ContactEditorFragment extends Fragment implements */ public static final String INTENT_EXTRA_ADD_TO_DEFAULT_DIRECTORY = "addToDefaultDirectory"; + public static final String INTENT_EXTRA_NEW_LOCAL_PROFILE = "newLocalProfile"; + /** * Modes that specify what the AsyncTask has to perform after saving */ @@ -245,6 +249,8 @@ public class ContactEditorFragment extends Fragment implements private boolean mEnabled = true; private boolean mRequestFocus; + private boolean mNewLocalProfile = false; + private boolean mIsUserProfile = false; public ContactEditorFragment() { } @@ -348,6 +354,8 @@ public class ContactEditorFragment extends Fragment implements mIntentExtras = intentExtras; mAutoAddToDefaultGroup = mIntentExtras != null && mIntentExtras.containsKey(INTENT_EXTRA_ADD_TO_DEFAULT_DIRECTORY); + mNewLocalProfile = mIntentExtras != null + && mIntentExtras.getBoolean(INTENT_EXTRA_NEW_LOCAL_PROFILE); } public void setListener(Listener value) { @@ -383,6 +391,8 @@ public class ContactEditorFragment extends Fragment implements mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS); mEnabled = savedState.getBoolean(KEY_ENABLED); mStatus = savedState.getInt(KEY_STATUS); + mNewLocalProfile = savedState.getBoolean(KEY_NEW_LOCAL_PROFILE); + mIsUserProfile = savedState.getBoolean(KEY_IS_USER_PROFILE); } } @@ -430,6 +440,13 @@ public class ContactEditorFragment extends Fragment implements setIntentExtras(mIntentExtras); mIntentExtras = null; + // For user profile, change the contacts query URI + mIsUserProfile = data.isUserProfile(); + if (mIsUserProfile) { + for (EntityDelta state : mState) { + state.setProfileQueryUri(); + } + } mRequestFocus = true; bindEditors(); @@ -463,8 +480,8 @@ public class ContactEditorFragment extends Fragment implements private void createContact() { final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(mContext).getAccounts(true); - // No Accounts available. Create a phone-local contact. - if (accounts.isEmpty()) { + // No Accounts available or creating a local profile. Create a phone-local contact. + if (accounts.isEmpty() || mNewLocalProfile) { createContact(null); return; // Don't show a dialog. } @@ -559,6 +576,11 @@ public class ContactEditorFragment extends Fragment implements EntityModifier.ensureKindExists(insert, newAccountType, Event.CONTENT_ITEM_TYPE); EntityModifier.ensureKindExists(insert, newAccountType, StructuredPostal.CONTENT_ITEM_TYPE); + // Set the correct URI for saving the contact as a profile + if (mNewLocalProfile) { + insert.setProfileQueryUri(); + } + if (mState == null) { // Create state if none exists yet mState = EntityDeltaList.fromSingle(insert); @@ -606,7 +628,7 @@ public class ContactEditorFragment extends Fragment implements if (Intent.ACTION_INSERT.equals(mAction) && numRawContacts == 1) { final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(mContext).getAccounts(true); - if (accounts.size() > 1) { + if (accounts.size() > 1 && !mNewLocalProfile) { addAccountSwitcher(mState.get(0), editor); } else { disableAccountSwitcher(editor); @@ -901,8 +923,8 @@ public class ContactEditorFragment extends Fragment implements setEnabled(false); Intent intent = ContactSaveService.createSaveContactIntent(getActivity(), mState, - SAVE_MODE_EXTRA_KEY, saveMode, getActivity().getClass(), - ContactEditorActivity.ACTION_SAVE_COMPLETED); + SAVE_MODE_EXTRA_KEY, saveMode, mNewLocalProfile || mIsUserProfile, + getActivity().getClass(), ContactEditorActivity.ACTION_SAVE_COMPLETED); getActivity().startService(intent); return true; } @@ -1499,6 +1521,8 @@ public class ContactEditorFragment extends Fragment implements outState.putBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN, mContactWritableForJoin); outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId); outState.putBoolean(KEY_ENABLED, mEnabled); + outState.putBoolean(KEY_NEW_LOCAL_PROFILE, mNewLocalProfile); + outState.putBoolean(KEY_IS_USER_PROFILE, mIsUserProfile); outState.putInt(KEY_STATUS, mStatus); super.onSaveInstanceState(outState); } diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java index f95dea3b2..4ac54dcd4 100644 --- a/src/com/android/contacts/list/ContactEntryListAdapter.java +++ b/src/com/android/contacts/list/ContactEntryListAdapter.java @@ -35,6 +35,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.QuickContactBadge; +import android.widget.SectionIndexer; import android.widget.TextView; import java.util.HashSet; @@ -63,7 +64,17 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { private boolean mDisplayPhotos; private boolean mQuickContactEnabled; + + /** + * indicates if contact queries include profile + */ private boolean mIncludeProfile; + + /** + * indicates if query results includes a profile + */ + private boolean mProfileExists; + private ContactPhotoManager mPhotoLoader; private String mQueryString; @@ -78,6 +89,7 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { private boolean mSelectionVisible; private ContactListFilter mFilter; + private String mContactsCount = ""; public ContactEntryListAdapter(Context context) { super(context); @@ -94,6 +106,20 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { ((ContactListPinnedHeaderView)pinnedHeaderView).setSectionHeader(title); } + protected void setPinnedHeaderContactsCount(View header) { + // Update the header with the contacts count only if a profile header exists + // otherwise, the contacts count are shown in the empty profile header view + if (mProfileExists) { + ((ContactListPinnedHeaderView)header).setCountView(mContactsCount); + } else { + clearPinnedHeaderContactsCount(header); + } + } + + protected void clearPinnedHeaderContactsCount(View header) { + ((ContactListPinnedHeaderView)header).setCountView(null); + } + protected void addPartitions() { addPartition(createDefaultDirectoryPartition()); } @@ -278,6 +304,22 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { mIncludeProfile = includeProfile; } + public void setProfileExists(boolean exists) { + mProfileExists = exists; + // Stick the "ME" header for the profile + if (exists) { + SectionIndexer indexer = getIndexer(); + if (indexer != null) { + ((ContactsSectionIndexer) indexer).setProfileHeader( + getContext().getString(R.string.user_profile_contacts_list_header)); + } + } + } + + public boolean hasProfile() { + return mProfileExists; + } + public void configureDirectoryLoader(DirectoryListLoader loader) { loader.setDirectorySearchMode(mDirectorySearchMode); loader.setLocalInvisibleDirectoryEnabled(LOCAL_INVISIBLE_DIRECTORY_ENABLED); @@ -571,32 +613,6 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { mFilter = filter; } - @Override - public Placement getItemPlacementInSection(int position) { - // Special case code to prevent a section header from being displayed above the user's - // profile entry. - if (isUserProfile(position)) { - // The user profile entry shouldn't display a section header above; the header should be - // displayed on top of the item below. - Placement placement = new Placement(); - placement.firstInSection = false; - placement.lastInSection = false; - placement.sectionHeader = null; - return placement; - } else if (position > 0 && isUserProfile(position - 1)) { - // If the item in the previous position is the user's profile, behave as if this entry - // is the first in the section. - Placement profilePlacement = super.getItemPlacementInSection(position - 1); - String profileHeader = profilePlacement.sectionHeader; - Placement placement = super.getItemPlacementInSection(position); - placement.firstInSection = true; - placement.sectionHeader = profileHeader; - return placement; - } else { - return super.getItemPlacementInSection(position); - } - } - // TODO: move sharable logic (bindXX() methods) to here with extra arguments protected void bindQuickContact(final ContactListItemView view, int partitionIndex, @@ -624,4 +640,12 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { } return uri; } + + public void setContactsCount(String count) { + mContactsCount = count; + } + + public String getContactsCount() { + return mContactsCount; + } } diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java index 73d085972..4ddba7595 100644 --- a/src/com/android/contacts/list/ContactEntryListFragment.java +++ b/src/com/android/contacts/list/ContactEntryListFragment.java @@ -134,6 +134,8 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter private boolean mForceLoad; + protected boolean mUserProfileExists; + private static final int STATUS_NOT_LOADED = 0; private static final int STATUS_LOADING = 1; private static final int STATUS_LOADED = 2; @@ -444,6 +446,7 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter } mAdapter.changeCursor(partitionIndex, data); + setProfileHeader(); showCount(partitionIndex, data); if (!isLoading()) { @@ -499,6 +502,14 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter } /** + * Shows a view at the top of the list with a pseudo local profile prompting the user to add + * a local profile. Default implementation does nothing. + */ + protected void setProfileHeader() { + mUserProfileExists = false; + } + + /** * Provides logic that dismisses this fragment. The default implementation * does nothing. */ @@ -585,6 +596,9 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter public void setIncludeProfile(boolean flag) { mIncludeProfile = flag; + if(mAdapter != null) { + mAdapter.setIncludeProfile(flag); + } } public void setSearchMode(boolean flag) { diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java index 48fd34203..51cc96504 100644 --- a/src/com/android/contacts/list/ContactListAdapter.java +++ b/src/com/android/contacts/list/ContactListAdapter.java @@ -113,14 +113,12 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter { private String mSelectedContactLookupKey; private long mSelectedContactId; - // View types for entries in the list view. - private final int mViewTypeProfileEntry; + private ContactListFilter mFilter; public ContactListAdapter(Context context) { super(context); mUnknownNameText = context.getText(R.string.missing_name); - mViewTypeProfileEntry = getViewTypeCount() - 1; } public CharSequence getUnknownNameText() { @@ -226,12 +224,7 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter { @Override protected View newView(Context context, int partition, Cursor cursor, int position, ViewGroup parent) { - ContactListItemView view; - if (getItemViewType(position) == mViewTypeProfileEntry) { - view = new ContactListProfileItemView(context, null); - } else { - view = new ContactListItemView(context, null); - } + ContactListItemView view = new ContactListItemView(context, null); view.setUnknownNameText(mUnknownNameText); view.setTextWithHighlightingFactory(getTextWithHighlightingFactory()); view.setQuickContactEnabled(isQuickContactEnabled()); @@ -239,32 +232,23 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter { return view; } - @Override - public int getItemViewType(int position) { - return isUserProfile(position) - ? mViewTypeProfileEntry - : super.getItemViewType(position); - } - - @Override - public int getItemViewTypeCount() { - return super.getItemViewTypeCount() + 1; - } - - @Override - public int getViewTypeCount() { - // One extra view type - the user's profile entry view. - return super.getViewTypeCount() + 1; - } - - protected void bindSectionHeaderAndDivider(ContactListItemView view, int position) { + protected void bindSectionHeaderAndDivider(ContactListItemView view, int position, + Cursor cursor) { if (isSectionHeaderDisplayEnabled()) { Placement placement = getItemPlacementInSection(position); - view.setSectionHeader(placement.firstInSection ? placement.sectionHeader : null); + + // First position, set the contacts number string + if (position == 0 && cursor.getInt(CONTACT_IS_USER_PROFILE) == 1) { + view.setCountView(getContactsCount()); + } else { + view.setCountView(null); + } + view.setSectionHeader(placement.sectionHeader); view.setDividerVisible(!placement.lastInSection); } else { view.setSectionHeader(null); view.setDividerVisible(true); + view.setCountView(null); } } @@ -384,4 +368,15 @@ public abstract class ContactListAdapter extends ContactEntryListAdapter { return null; } + + @Override + public void changeCursor(int partitionIndex, Cursor cursor) { + super.changeCursor(partitionIndex, cursor); + + // Check if a profile exists + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + setProfileExists(cursor.getInt(CONTACT_IS_USER_PROFILE) == 1); + } + } } diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java index 4c20dcacf..0fe8d0d1f 100644 --- a/src/com/android/contacts/list/ContactListItemView.java +++ b/src/com/android/contacts/list/ContactListItemView.java @@ -115,6 +115,7 @@ public class ContactListItemView extends ViewGroup private TextView mDataView; private TextView mSnippetView; private TextView mStatusView; + private TextView mCountView; private ImageView mPresenceIcon; private char[] mHighlightedPrefix; @@ -346,6 +347,11 @@ public class ContactListItemView extends ViewGroup mHeaderTextView.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY)); + if (mCountView != null) { + mCountView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY)); + } mHeaderBackgroundHeight = Math.max(mHeaderBackgroundHeight, mHeaderTextView.getMeasuredHeight()); height += (mHeaderBackgroundHeight + mHeaderUnderlineHeight); @@ -370,6 +376,12 @@ public class ContactListItemView extends ViewGroup 0, width - mPaddingRight, mHeaderBackgroundHeight); + if (mCountView != null) { + mCountView.layout(width - mPaddingRight - mCountView.getMeasuredWidth(), + 0, + width - mPaddingRight, + mHeaderBackgroundHeight); + } mHeaderDivider.layout(leftBound, mHeaderBackgroundHeight, width - mPaddingRight, @@ -921,6 +933,36 @@ public class ContactListItemView extends ViewGroup } /** + * Returns the text view for the contacts count, creating it if necessary. + */ + public TextView getCountView() { + if (mCountView == null) { + mCountView = new TextView(mContext); + mCountView.setSingleLine(true); + mCountView.setEllipsize(getTextEllipsis()); + mCountView.setTextAppearance(mContext, android.R.style.TextAppearance_Medium); + mCountView.setTextColor(R.color.contact_count_text_color); + addView(mCountView); + } + return mCountView; + } + + /** + * Adds or updates a text view for the contacts count. + */ + public void setCountView(CharSequence text) { + if (TextUtils.isEmpty(text)) { + if (mCountView != null) { + mCountView.setVisibility(View.GONE); + } + } else { + getCountView(); + mCountView.setText(text); + mCountView.setVisibility(VISIBLE); + } + } + + /** * Adds or updates a text view for the status. */ public void setStatus(CharSequence text) { diff --git a/src/com/android/contacts/list/ContactListPinnedHeaderView.java b/src/com/android/contacts/list/ContactListPinnedHeaderView.java index 5d0d7b1d5..a68904540 100644 --- a/src/com/android/contacts/list/ContactListPinnedHeaderView.java +++ b/src/com/android/contacts/list/ContactListPinnedHeaderView.java @@ -45,9 +45,11 @@ public class ContactListPinnedHeaderView extends ViewGroup { private final int mHeaderUnderlineColor; private final int mPaddingRight; private final int mPaddingLeft; + private final int mContactsCountTextColor; private int mHeaderBackgroundHeight; private TextView mHeaderTextView; + private TextView mCountTextView = null; private View mHeaderDivider; public ContactListPinnedHeaderView(Context context, AttributeSet attrs) { @@ -72,6 +74,8 @@ public class ContactListPinnedHeaderView extends ViewGroup { R.styleable.ContactListItemView_list_item_padding_left, 0); mPaddingRight = a.getDimensionPixelOffset( R.styleable.ContactListItemView_list_item_padding_right, 0); + mContactsCountTextColor = a.getColor( + R.styleable.ContactListItemView_list_item_contacts_count_text_color, Color.BLACK); a.recycle(); @@ -93,8 +97,13 @@ public class ContactListPinnedHeaderView extends ViewGroup { int width = resolveSize(0, widthMeasureSpec); mHeaderTextView.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY)); + if (isViewMeasurable(mCountTextView)) { + mCountTextView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY)); + } setMeasuredDimension(width, mHeaderBackgroundHeight + mHeaderUnderlineHeight); } @@ -105,8 +114,16 @@ public class ContactListPinnedHeaderView extends ViewGroup { mHeaderTextView.layout(mHeaderTextIndent + mPaddingLeft, 0, - width, + mHeaderTextView.getMeasuredWidth() + mHeaderTextIndent + mPaddingLeft, mHeaderBackgroundHeight); + + if (isViewMeasurable(mCountTextView)) { + mCountTextView.layout(width - mPaddingRight - mCountTextView.getMeasuredWidth(), + 0, + width, + mHeaderBackgroundHeight); + } + mHeaderDivider.layout(mPaddingLeft, mHeaderBackgroundHeight, width, @@ -134,4 +151,23 @@ public class ContactListPinnedHeaderView extends ViewGroup { // view (ListView). forceLayout(); } + + public void setCountView(String count) { + if (mCountTextView == null) { + mCountTextView = new TextView(mContext); + mCountTextView.setTextColor(mContactsCountTextColor); + mCountTextView.setTextSize(mHeaderTextSize); + addView(mCountTextView); + } + mCountTextView.setText(count); + if (count == null || count.isEmpty()) { + mCountTextView.setVisibility(View.GONE); + } else { + mCountTextView.setVisibility(View.VISIBLE); + } + } + + private boolean isViewMeasurable(View view) { + return (view != null && view.getVisibility() == View.VISIBLE); + } } diff --git a/src/com/android/contacts/list/ContactListProfileItemView.java b/src/com/android/contacts/list/ContactListProfileItemView.java deleted file mode 100644 index f18936640..000000000 --- a/src/com/android/contacts/list/ContactListProfileItemView.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 com.android.contacts.R; - -import android.content.Context; -import android.content.res.TypedArray; -import android.database.Cursor; -import android.util.AttributeSet; -import android.widget.TextView; - -/** - * Contact list entry that represents the user's personal profile data. - */ -public class ContactListProfileItemView extends ContactListItemView { - - public ContactListProfileItemView(Context context, AttributeSet attrs) { - super(context, attrs); - - TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView); - setDefaultPhotoViewSize(a.getDimensionPixelOffset( - R.styleable.ContactListItemView_list_item_profile_photo_size, 0)); - } - - @Override - public TextView getNameTextView() { - TextView nameTextView = super.getNameTextView(); - nameTextView.setTextAppearance(getContext(), android.R.style.TextAppearance_Large); - return nameTextView; - } - - @Override - public void showDisplayName(Cursor cursor, int nameColumnIndex, int alternativeNameColumnIndex, - boolean highlightingEnabled, int displayOrder) { - getNameTextView().setText(getContext().getText(R.string.profile_display_name)); - } -} diff --git a/src/com/android/contacts/list/ContactsSectionIndexer.java b/src/com/android/contacts/list/ContactsSectionIndexer.java index a80d1debb..109b8bad2 100644 --- a/src/com/android/contacts/list/ContactsSectionIndexer.java +++ b/src/com/android/contacts/list/ContactsSectionIndexer.java @@ -93,4 +93,10 @@ public class ContactsSectionIndexer implements SectionIndexer { */ return index >= 0 ? index : -index - 2; } + + public void setProfileHeader(String header) { + if (mSections != null && mSections.length > 0) { + mSections[0] = header; + } + } } diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java index b6a5be000..8590c2983 100644 --- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java +++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java @@ -16,13 +16,18 @@ package com.android.contacts.list; import com.android.contacts.R; +import com.android.contacts.editor.ContactEditorFragment; +import android.content.Intent; import android.database.Cursor; +import android.provider.ContactsContract.Contacts; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.FrameLayout; +import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; @@ -36,6 +41,10 @@ public class DefaultContactBrowseListFragment extends ContactBrowseListFragment private View mSearchHeaderView; private TextView mAccountFilterHeaderView; private View mAccountFilterHeaderContainer; + private View mProfileHeader; + private Button mProfileMessage; + private FrameLayout mMessageContainer; + private View mProfileTitle; public DefaultContactBrowseListFragment() { setPhotoLoaderEnabled(true); @@ -70,6 +79,11 @@ public class DefaultContactBrowseListFragment extends ContactBrowseListFragment getView().findViewById(R.id.account_filter_header_container); mCounterHeaderView = (TextView) getView().findViewById(R.id.contacts_count); + // Create an empty user profile header and hide it for now (it will be visible if the + // contacts list will have no user profile). + addEmptyUserProfileHeader(inflater); + showEmptyUserProfile(false); + // Putting the header view inside a container will allow us to make // it invisible later. See checkHeaderViewVisibility() FrameLayout headerContainer = new FrameLayout(inflater.getContext()); @@ -125,7 +139,11 @@ public class DefaultContactBrowseListFragment extends ContactBrowseListFragment if (count != 0) { String format = getResources().getQuantityText( R.plurals.listTotalAllContacts, count).toString(); - mCounterHeaderView.setText(String.format(format, count)); + if (mUserProfileExists) { + getAdapter().setContactsCount(String.format(format, count)); + } else { + mCounterHeaderView.setText(String.format(format, count)); + } } else { ContactListFilter filter = getFilter(); int filterType = filter != null ? filter.filterType @@ -176,6 +194,56 @@ public class DefaultContactBrowseListFragment extends ContactBrowseListFragment } mSearchHeaderView.setVisibility(View.VISIBLE); } + showEmptyUserProfile(false); } } + + @Override + protected void setProfileHeader() { + mUserProfileExists = getAdapter().hasProfile(); + showEmptyUserProfile(!mUserProfileExists && !isSearchMode()); + } + + private void showEmptyUserProfile(boolean show) { + // Changing visibility of just the mProfileHeader doesn't do anything unless + // you change visibility of its children, hence the call to mCounterHeaderView + // and mProfileTitle + mProfileHeader.setVisibility(show ? View.VISIBLE : View.GONE); + mCounterHeaderView.setVisibility(show ? View.VISIBLE : View.GONE); + mProfileTitle.setVisibility(show ? View.VISIBLE : View.GONE); + mMessageContainer.setVisibility(show ? View.VISIBLE : View.GONE); + mProfileMessage.setVisibility(show ? View.VISIBLE : View.GONE); + } + + /** + * This method creates a pseudo user profile contact. When the returned query doesn't have + * a profile, this methods creates 2 views that are inserted as headers to the listview: + * 1. A header view with the "ME" title and the contacts count. + * 2. A button that prompts the user to create a local profile + */ + private void addEmptyUserProfileHeader(LayoutInflater inflater) { + + ListView list = getListView(); + // Put a header with the "ME" name and a view for the number of contacts + mProfileHeader = inflater.inflate(R.layout.user_profile_header, null, false); + mCounterHeaderView = (TextView) mProfileHeader.findViewById(R.id.contacts_count); + mProfileTitle = mProfileHeader.findViewById(R.id.profile_title); + list.addHeaderView(mProfileHeader, null, false); + + // Add a selectable view with a message inviting the user to create a local profile + // The view is embedded in a frame view since you cannot change the visibility of a + // view in a ListView without having a parent view. + mMessageContainer = new FrameLayout(inflater.getContext()); + mProfileMessage = (Button)inflater.inflate(R.layout.user_profile_button, null, false); + mMessageContainer.addView(mProfileMessage); + list.addHeaderView(mMessageContainer, null, true); + + mProfileMessage.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); + intent.putExtra(ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE, true); + startActivity(intent); + } + }); + } } diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java index 8e96690d9..e9804f55f 100644 --- a/src/com/android/contacts/list/DefaultContactListAdapter.java +++ b/src/com/android/contacts/list/DefaultContactListAdapter.java @@ -229,7 +229,7 @@ public class DefaultContactListAdapter extends ContactListAdapter { view.setActivated(isSelectedContact(partition, cursor)); } - bindSectionHeaderAndDivider(view, position); + bindSectionHeaderAndDivider(view, position, cursor); if (isQuickContactEnabled()) { bindQuickContact(view, partition, cursor, diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java index e7a9eb98a..e7641258c 100644 --- a/src/com/android/contacts/list/JoinContactListAdapter.java +++ b/src/com/android/contacts/list/JoinContactListAdapter.java @@ -217,7 +217,7 @@ public class JoinContactListAdapter extends ContactListAdapter { } case PARTITION_ALL_CONTACTS: { final ContactListItemView view = (ContactListItemView)itemView; - bindSectionHeaderAndDivider(view, position); + bindSectionHeaderAndDivider(view, position, cursor); bindPhoto(view, partition, cursor); bindName(view, cursor); break; diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java index fe084f435..97ab347aa 100644 --- a/src/com/android/contacts/model/EntityDelta.java +++ b/src/com/android/contacts/model/EntityDelta.java @@ -31,6 +31,7 @@ import android.os.Parcelable; import android.provider.BaseColumns; import android.provider.ContactsContract.CommonDataKinds.GroupMembership; import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; import android.util.Log; @@ -66,6 +67,12 @@ public class EntityDelta implements Parcelable { private ValuesDelta mValues; /** + * URI used for contacts queries, by default it is set to query raw contacts. + * It can be set to query the profile's raw contact(s). + */ + private Uri mContactsQueryUri = RawContacts.CONTENT_URI; + + /** * Internal map of children values from {@link Entity#getSubValues()}, which * we store here sorted into {@link Data#MIMETYPE} bins. */ @@ -367,7 +374,7 @@ public class EntityDelta implements Parcelable { if (beforeId == null || beforeVersion == null) return; final ContentProviderOperation.Builder builder = ContentProviderOperation - .newAssertQuery(RawContacts.CONTENT_URI); + .newAssertQuery(mContactsQueryUri); builder.withSelection(RawContacts._ID + "=" + beforeId, null); builder.withValue(RawContacts.VERSION, beforeVersion); buildInto.add(builder.build()); @@ -398,7 +405,7 @@ public class EntityDelta implements Parcelable { } // Build possible operation at Contact level - builder = mValues.buildDiff(RawContacts.CONTENT_URI); + builder = mValues.buildDiff(mContactsQueryUri); possibleAdd(buildInto, builder); // Build operations for all children @@ -435,7 +442,7 @@ public class EntityDelta implements Parcelable { buildInto.add(builder.build()); } else if (isContactInsert) { // Restore aggregation mode as last operation - builder = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI); + builder = ContentProviderOperation.newUpdate(mContactsQueryUri); builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT); builder.withSelection(RawContacts._ID + "=?", new String[1]); builder.withSelectionBackReference(0, firstIndex); @@ -448,7 +455,7 @@ public class EntityDelta implements Parcelable { * {@link RawContacts#AGGREGATION_MODE} to the given value. */ protected Builder buildSetAggregationMode(Long beforeId, int mode) { - Builder builder = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI); + Builder builder = ContentProviderOperation.newUpdate(mContactsQueryUri); builder.withValue(RawContacts.AGGREGATION_MODE, mode); builder.withSelection(RawContacts._ID + "=" + beforeId, null); return builder; @@ -465,6 +472,7 @@ public class EntityDelta implements Parcelable { final int size = this.getEntryCount(false); dest.writeInt(size); dest.writeParcelable(mValues, flags); + dest.writeParcelable(mContactsQueryUri, flags); for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) { for (ValuesDelta child : mimeEntries) { dest.writeParcelable(child, flags); @@ -476,12 +484,20 @@ public class EntityDelta implements Parcelable { final ClassLoader loader = getClass().getClassLoader(); final int size = source.readInt(); mValues = source.<ValuesDelta> readParcelable(loader); + mContactsQueryUri = source.<Uri> readParcelable(loader); for (int i = 0; i < size; i++) { final ValuesDelta child = source.<ValuesDelta> readParcelable(loader); this.addEntry(child); } } + /** + * Used to set the query URI to the profile URI to store profiles. + */ + public void setProfileQueryUri() { + mContactsQueryUri = Profile.CONTENT_RAW_CONTACTS_URI; + } + public static final Parcelable.Creator<EntityDelta> CREATOR = new Parcelable.Creator<EntityDelta>() { public EntityDelta createFromParcel(Parcel in) { final EntityDelta state = new EntityDelta(); diff --git a/src/com/android/contacts/widget/IndexerListAdapter.java b/src/com/android/contacts/widget/IndexerListAdapter.java index 4cd7af0ff..a5aeb0f5c 100644 --- a/src/com/android/contacts/widget/IndexerListAdapter.java +++ b/src/com/android/contacts/widget/IndexerListAdapter.java @@ -70,6 +70,16 @@ public abstract class IndexerListAdapter extends PinnedHeaderListAdapter impleme */ protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title); + /** + * Sets the contacts count in the pinned header. + */ + protected abstract void setPinnedHeaderContactsCount(View header); + + /** + * clears the contacts count in the pinned header and makes the view invisible. + */ + protected abstract void clearPinnedHeaderContactsCount(View header); + public boolean isSectionHeaderDisplayEnabled() { return mSectionHeaderDisplayEnabled; } @@ -174,7 +184,11 @@ public abstract class IndexerListAdapter extends PinnedHeaderListAdapter impleme listView.setHeaderInvisible(index, false); } else { setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]); - + if (section == 0) { + setPinnedHeaderContactsCount(mHeader); + } else { + clearPinnedHeaderContactsCount(mHeader); + } // Compute the item position where the current partition begins int partitionStart = getPositionForPartition(mIndexedPartition); if (hasHeader(mIndexedPartition)) { |