summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIsaac Katzenelson <isaack@android.com>2011-07-29 18:24:53 -0700
committerIsaac Katzenelson <isaack@android.com>2011-08-08 12:57:08 -0700
commitead19c5eafee0ffb43b02a4ae75ac5244ad3f853 (patch)
treed30838c543d661435151a7af3e15195b0c47e479
parent485f5eb5529c69c4864516c2e2762ab581c7e4ca (diff)
downloadpackages_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
-rw-r--r--res/layout/contacts_list_content.xml94
-rw-r--r--res/layout/user_profile_button.xml27
-rw-r--r--res/layout/user_profile_header.xml60
-rw-r--r--res/values/attrs.xml1
-rw-r--r--res/values/strings.xml4
-rw-r--r--res/values/styles.xml1
-rw-r--r--src/com/android/contacts/ContactLoader.java17
-rw-r--r--src/com/android/contacts/ContactSaveService.java9
-rw-r--r--src/com/android/contacts/detail/ContactDetailDisplayUtils.java2
-rw-r--r--src/com/android/contacts/detail/ContactDetailFragment.java5
-rw-r--r--src/com/android/contacts/editor/ContactEditorFragment.java34
-rw-r--r--src/com/android/contacts/list/ContactEntryListAdapter.java76
-rw-r--r--src/com/android/contacts/list/ContactEntryListFragment.java14
-rw-r--r--src/com/android/contacts/list/ContactListAdapter.java53
-rw-r--r--src/com/android/contacts/list/ContactListItemView.java42
-rw-r--r--src/com/android/contacts/list/ContactListPinnedHeaderView.java40
-rw-r--r--src/com/android/contacts/list/ContactListProfileItemView.java51
-rw-r--r--src/com/android/contacts/list/ContactsSectionIndexer.java6
-rw-r--r--src/com/android/contacts/list/DefaultContactBrowseListFragment.java70
-rw-r--r--src/com/android/contacts/list/DefaultContactListAdapter.java2
-rw-r--r--src/com/android/contacts/list/JoinContactListAdapter.java2
-rw-r--r--src/com/android/contacts/model/EntityDelta.java24
-rw-r--r--src/com/android/contacts/widget/IndexerListAdapter.java16
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)) {