Implement phone search in DialtactsActivity
Includes tiny layout fixes for ContactSelectionActivity,
which also uses the same phone search fragment.
TODO: let the other Adapters like DefaultContactPickerFragment,
EmailAddressPickerFragment support grouping feature.
Change-Id: I8d7718192522a0005b9b76931560fe297cad882f
diff --git a/res/layout/contact_picker.xml b/res/layout/contact_picker.xml
index 6b03501..c3fe2fa 100644
--- a/res/layout/contact_picker.xml
+++ b/res/layout/contact_picker.xml
@@ -18,8 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.contacts.widget.FullHeightLinearLayout"
style="@style/ContactPickerLayout"
- android:paddingLeft="32dip"
- android:paddingRight="32dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
android:orientation="vertical"
android:layout_height="match_parent">
<view
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 7a9e7b2..675dcdb 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -45,4 +45,12 @@
class="com.android.contacts.list.StrequentContactListFragment"
android:layout_height="match_parent"
android:layout_width="match_parent" />
-</FrameLayout>
\ No newline at end of file
+
+ <!-- For phone search UI -->
+ <fragment
+ android:id="@+id/phone_number_picker_fragment"
+ class="com.android.contacts.list.PhoneNumberPickerFragment"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
+</FrameLayout>
diff --git a/res/layout/directory_header.xml b/res/layout/directory_header.xml
index 63d8297..2748923 100644
--- a/res/layout/directory_header.xml
+++ b/res/layout/directory_header.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/DirectoryHeader"
android:layout_width="match_parent"
- android:layout_height="56dip"
+ android:layout_height="@dimen/directory_header_height"
>
<TextView
android:id="@+id/count"
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
index 2d39186..b2f2af1 100644
--- a/res/values-xlarge/dimens.xml
+++ b/res/values-xlarge/dimens.xml
@@ -31,4 +31,5 @@
<dimen name="action_bar_search_spacing">12dip</dimen>
<dimen name="shortcut_icon_size">64dip</dimen>
<dimen name="list_section_height">37dip</dimen>
+ <dimen name="directory_header_height">56dip</dimen>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f369752..9525f54 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -135,4 +135,7 @@
<!-- Margins for the group detail fragment divider in the header -->
<dimen name="group_detail_divider_margin">15dip</dimen>
+
+ <!-- Height for directory headers in contact lists -->
+ <dimen name="directory_header_height">28dip</dimen>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6c0970f..8fd42b7 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -16,7 +16,6 @@
<resources>
<style name="DialtactsTheme" parent="android:Theme.Holo.Light">
<item name="android:windowContentOverlay">@null</item>
- <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="section_header_background">@drawable/list_title_holo</item>
<item name="list_section_header_height">32dip</item>
@@ -189,7 +188,7 @@
<item name="contact_filter_popup_width">320dip</item>
</style>
- <style name="ContactPickerTheme" parent="@android:Theme">
+ <style name="ContactPickerTheme" parent="@android:Theme.Holo.Light">
<item name="section_header_background">@drawable/section_header</item>
<item name="list_item_divider">@drawable/list_item_divider</item>
<item name="list_item_padding_top">4dip</item>
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index ed80eb1..a328b5b 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -27,11 +27,14 @@
import com.android.contacts.list.DefaultContactBrowseListFragment;
import com.android.contacts.list.DirectoryListLoader;
import com.android.contacts.list.OnContactBrowserActionListener;
+import com.android.contacts.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.list.PhoneNumberPickerFragment;
import com.android.contacts.list.StrequentContactListFragment;
import com.android.contacts.preference.ContactsPreferenceActivity;
import com.android.internal.telephony.ITelephony;
import android.app.ActionBar;
+import android.app.ActionBar.LayoutParams;
import android.app.ActionBar.Tab;
import android.app.ActionBar.TabListener;
import android.app.Activity;
@@ -49,10 +52,15 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents.UI;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
+import android.widget.SearchView;
+import android.widget.SearchView.OnCloseListener;
+import android.widget.SearchView.OnQueryTextListener;
/**
* The dialer activity that has one tab with the virtual 12key
@@ -94,6 +102,80 @@
*/
private int mLastManuallySelectedTab;
+ /**
+ * Fragment for searching phone numbers. Unlike the other Fragments, this doesn't correspond
+ * to tab but is shown by a search action.
+ */
+ private PhoneNumberPickerFragment mPhoneNumberPickerFragment;
+
+ private SearchView mSearchView;
+
+ /**
+ * True when this Activity is in its search UI (with a {@link SearchView} and
+ * {@link PhoneNumberPickerFragment}).
+ */
+ private boolean mInSearchUi;
+
+ /**
+ * Listener used when one of phone numbers in search UI is selected. This will initiate a
+ * phone call using the phone number.
+ */
+ private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
+ new OnPhoneNumberPickerActionListener() {
+ @Override
+ public void onPickPhoneNumberAction(Uri dataUri) {
+ PhoneNumberInteraction.startInteractionForPhoneCall(
+ DialtactsActivity.this, dataUri);
+ }
+
+ @Override
+ public void onShortcutIntentCreated(Intent intent) {
+ Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
+ }
+ };
+
+ /**
+ * Listener used to send search queries to the phone search fragment.
+ */
+ private final OnQueryTextListener mPhoneSearchQueryTextListener =
+ new OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ // Ignore.
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ // Show search result with non-empty text. Show a bare list otherwise.
+ mPhoneNumberPickerFragment.setQueryString(newText, true);
+ mPhoneNumberPickerFragment.setSearchMode(!TextUtils.isEmpty(newText));
+ return true;
+ }
+ };
+
+ /**
+ * Listener used to handle the "close" button on the right side of {@link SearchView}.
+ * If some text is in the search view, this will clean it up. Otherwise this will exit
+ * the search UI and let users go back to usual Phone UI.
+ *
+ * This does _not_ handle back button.
+ *
+ * TODO: need "up" button instead of close button
+ */
+ private final OnCloseListener mPhoneSearchCloseListener =
+ new OnCloseListener() {
+ @Override
+ public boolean onClose() {
+ if (TextUtils.isEmpty(mSearchView.getQuery())) {
+ exitSearchUi();
+ } else {
+ mSearchView.setQuery(null, true);
+ }
+ return true;
+ }
+ };
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -112,6 +194,10 @@
.findFragmentById(R.id.contacts_fragment);
mStrequentFragment = (StrequentContactListFragment) fragmentManager
.findFragmentById(R.id.favorites_fragment);
+ mPhoneNumberPickerFragment = (PhoneNumberPickerFragment) fragmentManager
+ .findFragmentById(R.id.phone_number_picker_fragment);
+ mPhoneNumberPickerFragment.setOnPhoneNumberPickerActionListener(
+ mPhoneNumberPickerActionListener);
// Hide all tabs (the current tab will later be reshown once a tab is selected)
final FragmentTransaction transaction = fragmentManager.beginTransaction();
@@ -119,6 +205,7 @@
transaction.hide(mCallLogFragment);
transaction.hide(mContactsFragment);
transaction.hide(mStrequentFragment);
+ transaction.hide(mPhoneNumberPickerFragment);
transaction.commit();
// Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
@@ -147,11 +234,16 @@
protected void onPause() {
super.onPause();
- final int currentTabIndex = getActionBar().getSelectedTab().getPosition();
final SharedPreferences.Editor editor =
getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit();
- if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
- editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES);
+ // selectedTab becomes null in search UI.
+ final Tab selectedTab = getActionBar().getSelectedTab();
+ if (selectedTab != null) {
+ final int currentTabIndex = selectedTab.getPosition();
+ if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
+ editor.putBoolean(
+ PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES);
+ }
}
editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedTab);
@@ -381,7 +473,10 @@
@Override
public void onBackPressed() {
- if (isTaskRoot()) {
+ if (mInSearchUi) {
+ // We should let the user go back to usual screens with tabs.
+ exitSearchUi();
+ } else if (isTaskRoot()) {
// Instead of stopping, simply push this to the back of the stack.
// This is only done when running at the top of the stack;
// otherwise, we have been launched by someone else so need to
@@ -419,6 +514,7 @@
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.show(mFragment);
+ ft.hide(mPhoneNumberPickerFragment);
// Remember this tab index. This function is also called, if the tab is set
// automatically in which case the setter (setCurrentTab) has to set this to its old
@@ -543,4 +639,71 @@
return super.onOptionsItemSelected(item);
}
}
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery,
+ Bundle appSearchData, boolean globalSearch) {
+ if (mPhoneNumberPickerFragment != null && mPhoneNumberPickerFragment.isAdded()
+ && !globalSearch) {
+ enterSearchUi();
+ } else {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ }
+ }
+
+ /**
+ * Hides every tab and shows search UI for phone lookup.
+ */
+ private void enterSearchUi() {
+ final ActionBar actionBar = getActionBar();
+
+ final Tab tab = actionBar.getSelectedTab();
+ if (tab != null) {
+ mLastManuallySelectedTab = tab.getPosition();
+ }
+
+ // Instantiate or reset SearchView in ActionBar.
+ if (mSearchView == null) {
+ // TODO: layout is not what we want. Need "up" button instead of "close" button, etc.
+ final View searchViewLayout =
+ getLayoutInflater().inflate(R.layout.custom_action_bar, null);
+ mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
+ mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
+ mSearchView.requestFocus();
+ actionBar.setCustomView(searchViewLayout,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ } else {
+ mSearchView.setQuery(null, true);
+ }
+
+ actionBar.setDisplayShowCustomEnabled(true);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+
+ // Show the search fragment and hide everything else.
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.show(mPhoneNumberPickerFragment);
+ transaction.hide(mDialpadFragment);
+ transaction.hide(mCallLogFragment);
+ transaction.hide(mContactsFragment);
+ transaction.hide(mStrequentFragment);
+ transaction.commit();
+
+ mInSearchUi = true;
+ }
+
+ /**
+ * Goes back to usual Phone UI with tags. Previously selected Tag and associated Fragment
+ * should be automatically focused again.
+ */
+ private void exitSearchUi() {
+ final ActionBar actionBar = getActionBar();
+
+ // We want to hide SearchView and show Tabs. Also focus on previously selected one.
+ actionBar.setDisplayShowCustomEnabled(false);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ mInSearchUi = false;
+ }
}
diff --git a/src/com/android/contacts/interactions/PhoneNumberInteraction.java b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
index 8430559..9762e3d 100644
--- a/src/com/android/contacts/interactions/PhoneNumberInteraction.java
+++ b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
@@ -282,13 +282,29 @@
* Initiates the interaction. This may result in a phone call or sms message started
* or a disambiguation dialog to determine which phone number should be used.
*/
- public void startInteraction(Uri contactUri) {
+ @VisibleForTesting
+ /* package */ void startInteraction(Uri uri) {
if (mLoader != null) {
mLoader.reset();
}
+ final Uri queryUri;
+ final String inputUriAsString = uri.toString();
+ if (inputUriAsString.startsWith(Contacts.CONTENT_URI.toString())) {
+ if (!inputUriAsString.endsWith(Contacts.Data.CONTENT_DIRECTORY)) {
+ queryUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
+ } else {
+ queryUri = uri;
+ }
+ } else if (inputUriAsString.startsWith(Data.CONTENT_URI.toString())) {
+ queryUri = uri;
+ } else {
+ throw new UnsupportedOperationException(
+ "Input Uri must be contact Uri or data Uri (input: \"" + uri + "\")");
+ }
+
mLoader = new CursorLoader(mContext,
- Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY),
+ queryUri,
PHONE_NUMBER_PROJECTION,
PHONE_NUMBER_SELECTION,
null,
@@ -356,29 +372,36 @@
/**
* Start call action using given contact Uri. If there are multiple candidates for the phone
* call, dialog is automatically shown and the user is asked to choose one.
+ *
+ * @param uri contact Uri (built from {@link Contacts#CONTENT_URI}) or data Uri
+ * (built from {@link Data#CONTENT_URI}). Contact Uri may show the disambiguation dialog while
+ * data Uri won't.
*/
- public static void startInteractionForPhoneCall(Activity activity, Uri contactUri) {
+ public static void startInteractionForPhoneCall(Activity activity, Uri uri) {
(new PhoneNumberInteraction(activity, InteractionType.PHONE_CALL, null))
- .startInteraction(contactUri);
+ .startInteraction(uri);
}
/**
* Start text messaging (a.k.a SMS) action using given contact Uri. If there are multiple
* candidates for the phone call, dialog is automatically shown and the user is asked to choose
* one.
+ *
+ * @param uri contact Uri (built from {@link Contacts#CONTENT_URI}) or data Uri
+ * (built from {@link Data#CONTENT_URI}). Contact Uri may show the disambiguation dialog while
+ * data Uri won't.
*/
- public static void startInteractionForTextMessage(Activity activity, Uri contactUri) {
- (new PhoneNumberInteraction(activity, InteractionType.SMS, null))
- .startInteraction(contactUri);
+ public static void startInteractionForTextMessage(Activity activity, Uri uri) {
+ (new PhoneNumberInteraction(activity, InteractionType.SMS, null)).startInteraction(uri);
}
@VisibleForTesting
- CursorLoader getLoader() {
+ /* package */ CursorLoader getLoader() {
return mLoader;
}
@VisibleForTesting
- void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
+ /* package */ void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
PhoneDisambiguationDialogFragment.show(((Activity)mContext).getFragmentManager(),
phoneList, mInteractionType);
}
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 032b60f..df862a1 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -99,8 +99,33 @@
private char[] mHighlightedPrefix;
private int mDefaultPhotoViewSize;
+ /**
+ * Can be effective even when {@link #mPhotoView} is null, as we want to have horizontal padding
+ * to align other data in this View.
+ */
private int mPhotoViewWidth;
+ /**
+ * Can be effective even when {@link #mPhotoView} is null, as we want to have vertical padding.
+ */
private int mPhotoViewHeight;
+
+ /**
+ * Only effective when {@link #mPhotoView} is null.
+ * When true all the Views on the right side of the photo should have horizontal padding on
+ * those left assuming there is a photo.
+ */
+ private boolean mKeepHorizontalPaddingForPhotoView;
+ /**
+ * Only effective when {@link #mPhotoView} is null.
+ */
+ private boolean mKeepVerticalPaddingForPhotoView;
+
+ /**
+ * True when {@link #mPhotoViewWidth} and {@link #mPhotoViewHeight} are ready for being used.
+ * False indicates those values should be updated before being used in position calculation.
+ */
+ private boolean mPhotoViewWidthAndHeightAreReady = false;
+
private int mLine1Height;
private int mLine2Height;
private int mLine3Height;
@@ -392,6 +417,9 @@
leftBound + mPhotoViewWidth,
photoTop + mPhotoViewHeight);
leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ } else if (mKeepHorizontalPaddingForPhotoView) {
+ // Draw nothing but keep the padding.
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
}
return leftBound;
}
@@ -459,7 +487,7 @@
* Extracts width and height from the style
*/
private void ensurePhotoViewSize() {
- if (mPhotoViewWidth == 0 && mPhotoViewHeight == 0) {
+ if (!mPhotoViewWidthAndHeightAreReady) {
if (mQuickContactEnabled) {
TypedArray a = mContext.obtainStyledAttributes(null,
com.android.internal.R.styleable.ViewGroup_Layout,
@@ -471,9 +499,15 @@
android.R.styleable.ViewGroup_Layout_layout_height,
ViewGroup.LayoutParams.WRAP_CONTENT);
a.recycle();
- } else {
+ } else if (mPhotoView != null) {
mPhotoViewWidth = mPhotoViewHeight = getDefaultPhotoViewSize();
+ } else {
+ final int defaultPhotoViewSize = getDefaultPhotoViewSize();
+ mPhotoViewWidth = mKeepHorizontalPaddingForPhotoView ? defaultPhotoViewSize : 0;
+ mPhotoViewHeight = mKeepVerticalPaddingForPhotoView ? defaultPhotoViewSize : 0;
}
+
+ mPhotoViewWidthAndHeightAreReady = true;
}
}
@@ -567,6 +601,7 @@
mQuickContact = new QuickContactBadge(mContext, null, QUICK_CONTACT_BADGE_STYLE);
mQuickContact.setExcludeMimes(new String[] { Contacts.CONTENT_ITEM_TYPE });
addView(mQuickContact);
+ mPhotoViewWidthAndHeightAreReady = false;
}
return mQuickContact;
}
@@ -584,15 +619,30 @@
// Quick contact style used above will set a background - remove it
mPhotoView.setBackgroundDrawable(null);
addView(mPhotoView);
+ mPhotoViewWidthAndHeightAreReady = false;
}
return mPhotoView;
}
/**
- * Removes the photo view. Should not be needed once we start handling different
- * types of views as different types of views from the List's perspective.
+ * Removes the photo view.
*/
public void removePhotoView() {
+ removePhotoView(false, true);
+ }
+
+ /**
+ * Removes the photo view.
+ *
+ * @param keepHorizontalPadding True means data on the right side will have padding on left,
+ * pretending there is still a photo view.
+ * @param keepVerticalPadding True means the View will have some height enough for
+ * accommodating a photo view.
+ */
+ public void removePhotoView(boolean keepHorizontalPadding, boolean keepVerticalPadding) {
+ mPhotoViewWidthAndHeightAreReady = false;
+ mKeepHorizontalPaddingForPhotoView = keepHorizontalPadding;
+ mKeepVerticalPaddingForPhotoView = keepVerticalPadding;
if (mPhotoView != null) {
removeView(mPhotoView);
mPhotoView = null;
@@ -821,6 +871,13 @@
getNameTextView(), displayOrder, highlightingEnabled, mHighlightedPrefix);
}
+ public void hideDisplayName() {
+ if (mNameTextView != null) {
+ removeView(mNameTextView);
+ mNameTextView = null;
+ }
+ }
+
public void showPhoneticName(Cursor cursor, int phoneticNameColumnIndex) {
cursor.copyStringToBuffer(phoneticNameColumnIndex, mPhoneticNameBuffer);
int phoneticNameSize = mPhoneticNameBuffer.sizeCopied;
@@ -831,6 +888,13 @@
}
}
+ public void hidePhoneticName() {
+ if (mPhoneticNameTextView != null) {
+ removeView(mPhoneticNameTextView);
+ mPhoneticNameTextView = null;
+ }
+ }
+
/**
* Sets the proper icon (star or presence or nothing)
*/
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index 9356bb6..c159ac8 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -103,6 +103,8 @@
}
loader.setUri(uri);
+
+ // TODO: we probably want to use default sort order in search mode.
if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
} else {
@@ -154,10 +156,46 @@
@Override
protected void bindView(View itemView, int partition, Cursor cursor, int position) {
ContactListItemView view = (ContactListItemView)itemView;
+
+ // Look at elements before and after this position, checking if contact IDs are same.
+ // If they have one same contact ID, it means they can be grouped.
+ //
+ // In one group, only the first entry will show its photo and names (display name and
+ // phonetic name), and the other entries in the group show just their data (e.g. phone
+ // number, email address).
+ cursor.moveToPosition(position);
+ boolean isFirstEntry = true;
+ boolean showBottomDivider = true;
+ final long currentContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
+ if (cursor.moveToPrevious() && !cursor.isBeforeFirst()) {
+ final long previousContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
+ if (currentContactId == previousContactId) {
+ isFirstEntry = false;
+ }
+ }
+ cursor.moveToPosition(position);
+ if (cursor.moveToNext() && !cursor.isAfterLast()) {
+ final long nextContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
+ if (currentContactId == nextContactId) {
+ // The following entry should be in the same group, which means we don't want a
+ // divider between them.
+ // TODO: we want a different divider than the divider between groups. Just hiding
+ // this divider won't be enough.
+ showBottomDivider = false;
+ }
+ }
+ cursor.moveToPosition(position);
+
bindSectionHeaderAndDivider(view, position);
- bindName(view, cursor);
- bindPhoto(view, cursor);
+ if (isFirstEntry) {
+ bindName(view, cursor);
+ bindPhoto(view, cursor);
+ } else {
+ unbindName(view);
+ unbindPhoto(view);
+ }
bindPhoneNumber(view, cursor);
+ view.setDividerVisible(showBottomDivider);
}
protected void bindPhoneNumber(ContactListItemView view, Cursor cursor) {
@@ -190,6 +228,11 @@
view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
}
+ protected void unbindName(final ContactListItemView view) {
+ view.hideDisplayName();
+ view.hidePhoneticName();
+ }
+
protected void bindPhoto(final ContactListItemView view, Cursor cursor) {
long photoId = 0;
if (!cursor.isNull(PHONE_PHOTO_ID_COLUMN_INDEX)) {
@@ -198,4 +241,8 @@
getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
}
+
+ protected void unbindPhoto(final ContactListItemView view) {
+ view.removePhotoView(true, false);
+ }
}
diff --git a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
index 2776a9f..d0e50c4 100644
--- a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
@@ -30,6 +30,7 @@
import android.os.AsyncTask;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.Smoke;
@@ -103,6 +104,24 @@
assertEquals("sms:123", intent.getDataString());
}
+ public void testSendSmsWhenDataIdIsProvided() {
+ Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, 1);
+ expectQuery(dataUri, true /* isDataUri */ )
+ .returnRow(1, "987", 0, null, Phone.TYPE_HOME, null);
+
+ TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
+ mContext, InteractionType.SMS, null);
+
+ interaction.startInteraction(dataUri);
+ interaction.getLoader().waitForLoader();
+
+ Intent intent = mContext.getIntentForStartActivity();
+ assertNotNull(intent);
+
+ assertEquals(Intent.ACTION_SENDTO, intent.getAction());
+ assertEquals("sms:987", intent.getDataString());
+ }
+
public void testSendSmsWhenThereIsPrimaryNumber() {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
@@ -186,7 +205,16 @@
}
private Query expectQuery(Uri contactUri) {
- Uri dataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
+ return expectQuery(contactUri, false);
+ }
+
+ private Query expectQuery(Uri uri, boolean isDataUri) {
+ final Uri dataUri;
+ if (isDataUri) {
+ dataUri = uri;
+ } else {
+ dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
+ }
return mContactsProvider
.expectQuery(dataUri)
.withProjection(