Merge "Move notification generation to a service."
diff --git a/res/layout/contact_detail_updates_fragment.xml b/res/layout/contact_detail_updates_fragment.xml
index 8677737..95eb0a5 100644
--- a/res/layout/contact_detail_updates_fragment.xml
+++ b/res/layout/contact_detail_updates_fragment.xml
@@ -54,9 +54,7 @@
android:id="@+id/update_list"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/detail_update_section_side_padding"
- android:paddingRight="@dimen/detail_update_section_side_padding" />
+ android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
diff --git a/res/layout/stream_item_one_column.xml b/res/layout/stream_item_one_column.xml
index 014e3f1..ecab57c 100644
--- a/res/layout/stream_item_one_column.xml
+++ b/res/layout/stream_item_one_column.xml
@@ -17,20 +17,24 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/detail_update_section_item_vertical_padding"
android:orientation="vertical">
<LinearLayout
android:id="@+id/stream_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingTop="@dimen/detail_update_section_item_vertical_padding"
android:paddingBottom="@dimen/detail_update_section_item_vertical_padding"
- android:paddingLeft="@dimen/detail_update_section_item_left_padding"
+ android:paddingLeft="@dimen/detail_update_section_item_horizontal_padding"
+ android:paddingRight="@dimen/detail_update_section_item_horizontal_padding"
+ android:background="@drawable/list_selector"
android:orientation="vertical" />
<View
android:id="@+id/horizontal_divider"
android:layout_width="match_parent"
android:layout_height="1px"
+ android:layout_marginLeft="@dimen/detail_update_section_side_padding"
+ android:layout_marginRight="@dimen/detail_update_section_side_padding"
android:background="?android:attr/dividerHorizontal" />
</LinearLayout>
diff --git a/res/layout/stream_item_text.xml b/res/layout/stream_item_text.xml
index 4c44100..861d91f 100644
--- a/res/layout/stream_item_text.xml
+++ b/res/layout/stream_item_text.xml
@@ -43,4 +43,4 @@
android:textColor="@color/social_update_comments_color" />
</LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 604ea31..69c89d8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -109,8 +109,8 @@
<!-- Vertical padding above and below individual stream items -->
<dimen name="detail_update_section_item_vertical_padding">16dip</dimen>
- <!-- Left-side padding for individual stream items -->
- <dimen name="detail_update_section_item_left_padding">8dip</dimen>
+ <!-- Horizontal padding for individual stream items -->
+ <dimen name="detail_update_section_item_horizontal_padding">24dip</dimen>
<!-- Horizontal padding between content sections within a stream item -->
<dimen name="detail_update_section_internal_padding">16dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 999531e..b1f9f6f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1019,8 +1019,8 @@
was found that could perform the selected action -->
<string name="quickcontact_missing_app">No application found to handle this action</string>
- <!-- Shown as the header name for a person when the name is missing or unknown. -->
- <string name="quickcontact_missing_name">Unknown</string>
+ <!-- Shown as the display name for a person when the name is missing or unknown. [CHAR LIMIT=18]-->
+ <string name="missing_name">(no name)</string>
<!-- The menu item to open the list of accounts -->
<string name="menu_accounts">Accounts</string>
diff --git a/src/com/android/contacts/activities/DialpadActivity.java b/src/com/android/contacts/activities/DialpadActivity.java
index cfe17f3..1221068 100644
--- a/src/com/android/contacts/activities/DialpadActivity.java
+++ b/src/com/android/contacts/activities/DialpadActivity.java
@@ -64,7 +64,7 @@
@Override
protected void onNewIntent(Intent newIntent) {
setIntent(newIntent);
- mFragment.resolveIntent(newIntent);
+ mFragment.configureScreenFromIntent(newIntent);
}
public DialpadFragment getFragment() {
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 01212f5..2040f8d 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -481,12 +481,18 @@
final String action = newIntent.getAction();
if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
setupFilterText(newIntent);
- } else if (isDialIntent(newIntent)) {
- setupDialUri(newIntent);
}
if (mInSearchUi || mSearchFragment.isVisible()) {
exitSearchUi();
}
+
+ if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) {
+ if (mDialpadFragment != null) {
+ mDialpadFragment.configureScreenFromIntent(newIntent);
+ } else {
+ Log.e(TAG, "DialpadFragment isn't ready yet when the tab is already selected.");
+ }
+ }
}
/** Returns true if the given intent contains a phone number to populate the dialer with */
@@ -535,33 +541,6 @@
}
}
- /**
- * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri
- * originally came from a dial intent received by this activity. The stored
- * uri will then be cleared after after this method returns.
- *
- * @return The stored uri
- */
- public Uri getAndClearDialUri() {
- Uri dialUri = mDialUri;
- mDialUri = null;
- return dialUri;
- }
-
- /**
- * Stores the uri associated with a dial intent. This is so child activities can
- * check if they are supposed to display new dial info.
- *
- * @param intent The intent received in {@link #onNewIntent(Intent)}
- */
- private void setupDialUri(Intent intent) {
- // If the intent was relaunched from history, don't reapply the intent.
- if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
- return;
- }
- mDialUri = intent.getData();
- }
-
@Override
public void onBackPressed() {
if (mInSearchUi) {
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index d8ba995..7144dfb 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -99,6 +99,8 @@
styledName = altDisplayName;
}
}
+ } else {
+ styledName = context.getResources().getString(R.string.missing_name);
}
return styledName;
}
@@ -251,24 +253,33 @@
* Displays the social stream items under the given layout.
*/
public static void showSocialStreamItems(LayoutInflater inflater, Context context,
- Result contactData, LinearLayout streamContainer) {
+ Result contactData, LinearLayout streamContainer, View.OnClickListener listener) {
if (streamContainer != null) {
streamContainer.removeAllViews();
List<StreamItemEntry> streamItems = contactData.getStreamItems();
for (StreamItemEntry streamItem : streamItems) {
- addStreamItemToContainer(inflater, context, streamItem, streamContainer);
+ addStreamItemToContainer(inflater, context, streamItem, streamContainer, listener);
}
}
}
- public static void addStreamItemToContainer(LayoutInflater inflater, Context context,
- StreamItemEntry streamItem, LinearLayout streamContainer) {
+ @VisibleForTesting
+ static void addStreamItemToContainer(LayoutInflater inflater, Context context,
+ StreamItemEntry streamItem, LinearLayout streamContainer,
+ View.OnClickListener listener) {
View oneColumnView = inflater.inflate(R.layout.stream_item_one_column,
streamContainer, false);
ViewGroup contentBox = (ViewGroup) oneColumnView.findViewById(R.id.stream_item_content);
int internalPadding = context.getResources().getDimensionPixelSize(
R.dimen.detail_update_section_internal_padding);
+ // Add the listener only if there is an action and corresponding URI.
+ if (streamItem.getAction() != null && streamItem.getActionUri() != null) {
+ contentBox.setTag(streamItem);
+ contentBox.setOnClickListener(listener);
+ contentBox.setFocusable(true);
+ }
+
// TODO: This is not the correct layout for a stream item with photos. Photos should be
// displayed first, then the update text either to the right of the final image (if there
// are an odd number of images) or below the last row of images (if there are an even
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index 602958d..d668429 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -19,10 +19,13 @@
import com.android.contacts.ContactLoader;
import com.android.contacts.R;
import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
+import com.android.contacts.util.StreamItemEntry;
import android.app.Fragment;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -54,6 +57,28 @@
*/
private View mTouchInterceptLayer;
+ /**
+ * Listener on clicks on a stream item.
+ * <p>
+ * It assumes the view has a tag of type {@link StreamItemEntry} associated with it.
+ */
+ private View.OnClickListener mStreamItemClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ StreamItemEntry streamItemEntry = (StreamItemEntry) view.getTag();
+ Uri uri;
+ try {
+ uri = Uri.parse(streamItemEntry.getActionUri());
+ } catch (Throwable throwable) {
+ Log.e(TAG, "invalid URI for stream item #" + streamItemEntry.getId() + ": "
+ + streamItemEntry.getActionUri());
+ return;
+ }
+ Intent streamItemIntent = new Intent(streamItemEntry.getAction(), uri);
+ startActivity(streamItemIntent);
+ }
+ };
+
public ContactDetailUpdatesFragment() {
// Explicit constructor for inflation
}
@@ -75,7 +100,7 @@
// have it.
if (mContactData != null) {
ContactDetailDisplayUtils.showSocialStreamItems(inflater, getActivity(), mContactData,
- mStreamContainer);
+ mStreamContainer, mStreamItemClickListener);
}
mAlphaLayer = rootView.findViewById(R.id.alpha_overlay);
@@ -91,7 +116,7 @@
mLookupUri = lookupUri;
mContactData = result;
ContactDetailDisplayUtils.showSocialStreamItems(mInflater, getActivity(), mContactData,
- mStreamContainer);
+ mStreamContainer, mStreamItemClickListener);
}
@Override
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index add6b80..a5db5ce 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -143,9 +143,6 @@
static final String EXTRA_SEND_EMPTY_FLASH
= "com.android.phone.extra.SEND_EMPTY_FLASH";
- /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
- private boolean mIsAddCallMode;
-
private String mCurrentCountryIso;
/**
@@ -313,7 +310,7 @@
mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
mDialpadChooser.setOnItemClickListener(this);
- resolveIntent(getActivity().getIntent());
+ configureScreenFromIntent(getActivity().getIntent());
return fragmentView;
}
@@ -323,38 +320,18 @@
}
/**
- * Handles the intent that launched us.
- *
- * We can be launched either with ACTION_DIAL or ACTION_VIEW (which
- * may include a phone number to pre-load), or ACTION_MAIN (which just
- * brings up a blank dialpad).
- *
- * @return true IFF the current intent has the DialtactsActivity.EXTRA_IGNORE_STATE
- * extra set to true, which indicates (to our container) that we should ignore
- * any possible saved state, and instead reset our state based on the parent's
- * intent.
+ * @return true when {@link #mDigits} is actually filled by the Intent.
*/
- public boolean resolveIntent(Intent intent) {
- boolean ignoreState = false;
-
- // by default we are not adding a call.
- mIsAddCallMode = false;
-
- // By default we don't show the "dialpad chooser" UI.
- boolean needToShowDialpadChooser = false;
-
- // Resolve the intent
+ private boolean fillDigitsIfNecessary(Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- // see if we are "adding a call" from the InCallScreen; false by default.
- mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
-
Uri uri = intent.getData();
if (uri != null) {
if ("tel".equals(uri.getScheme())) {
// Put the requested number into the input area
String data = uri.getSchemeSpecificPart();
setFormattedDigits(data, null);
+ return true;
} else {
String type = intent.getType();
if (People.CONTENT_ITEM_TYPE.equals(type)
@@ -364,22 +341,42 @@
new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
null, null, null);
if (c != null) {
- if (c.moveToFirst()) {
- // Put the number into the input area
- setFormattedDigits(c.getString(0), c.getString(1));
+ try {
+ if (c.moveToFirst()) {
+ // Put the number into the input area
+ setFormattedDigits(c.getString(0), c.getString(1));
+ return true;
+ }
+ } finally {
+ c.close();
}
- c.close();
}
}
}
- } else {
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @see #showDialpadChooser(boolean)
+ */
+ private static boolean needToShowDialpadChooser(Intent intent, boolean isAddCallMode) {
+ final String action = intent.getAction();
+
+ boolean needToShowDialpadChooser = false;
+
+ if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
+ Uri uri = intent.getData();
+ if (uri == null) {
// ACTION_DIAL or ACTION_VIEW with no data.
// This behaves basically like ACTION_MAIN: If there's
// already an active call, bring up an intermediate UI to
// make the user confirm what they really want to do.
// Be sure *not* to show the dialpad chooser if this is an
// explicit "Add call" action, though.
- if (!mIsAddCallMode && phoneIsInUse()) {
+ if (!isAddCallMode && phoneIsInUse()) {
needToShowDialpadChooser = true;
}
}
@@ -399,11 +396,34 @@
}
}
- // Bring up the "dialpad chooser" IFF we need to make the user
- // confirm which dialpad they really want.
- showDialpadChooser(needToShowDialpadChooser);
+ return needToShowDialpadChooser;
+ }
- return ignoreState;
+ private static boolean isAddCallMode(Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
+ // see if we are "adding a call" from the InCallScreen; false by default.
+ return intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks the given Intent and changes dialpad's UI state. For example, if the Intent requires
+ * the screen to enter "Add Call" mode, this method will show correct UI for the mode.
+ */
+ public void configureScreenFromIntent(Intent intent) {
+ boolean needToShowDialpadChooser = false;
+
+ final boolean isAddCallMode = isAddCallMode(intent);
+ if (!isAddCallMode) {
+ final boolean digitsFilled = fillDigitsIfNecessary(intent);
+ if (!digitsFilled) {
+ needToShowDialpadChooser = needToShowDialpadChooser(intent, isAddCallMode);
+ }
+ }
+ showDialpadChooser(needToShowDialpadChooser);
}
private void setFormattedDigits(String data, String normalizedNumber) {
@@ -476,13 +496,10 @@
}
Activity parent = getActivity();
- // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
- // digits in the dialer field.
if (parent instanceof DialtactsActivity) {
- Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
- if (dialUri != null) {
- resolveIntent(parent.getIntent());
- }
+ // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
+ // digits in the dialer field.
+ fillDigitsIfNecessary(parent.getIntent());
}
// While we're in the foreground, listen for phone state changes,
@@ -904,8 +921,8 @@
// ListView. We do this only once.
if (mDialpadChooserAdapter == null) {
mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
- mDialpadChooser.setAdapter(mDialpadChooserAdapter);
}
+ mDialpadChooser.setAdapter(mDialpadChooserAdapter);
} else {
// Log.i(TAG, "Displaying normal Dialer UI.");
mDigits.setVisibility(View.VISIBLE);
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index 753261a..630a397 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -56,6 +56,15 @@
public void setCursor(Cursor cursor) {
mCursor = cursor;
+
+ // If there's no selected group already and the cursor is valid, then by default, select the
+ // first group
+ if (mSelectedGroupUri == null && cursor != null && cursor.getCount() > 0) {
+ GroupListItem firstItem = getItem(0);
+ long groupId = (firstItem == null) ? null : firstItem.getGroupId();
+ mSelectedGroupUri = getGroupUriFromId(groupId);
+ }
+
notifyDataSetChanged();
}
@@ -89,6 +98,10 @@
return mSelectedGroupUri != null && mSelectedGroupUri.equals(groupUri);
}
+ public Uri getSelectedGroup() {
+ return mSelectedGroupUri;
+ }
+
@Override
public int getCount() {
return mCursor == null ? 0 : mCursor.getCount();
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index a1544cf..835400f 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -223,6 +223,7 @@
}
mListView.setEmptyView(mEmptyView);
+ mSelectedGroupUri = mAdapter.getSelectedGroup();
if (mSelectionVisible && mSelectedGroupUri != null) {
viewGroup(mSelectedGroupUri);
}
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 7322fc6..467dcc3 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -15,6 +15,8 @@
*/
package com.android.contacts.list;
+import com.android.contacts.R;
+
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@@ -28,7 +30,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
-import android.widget.QuickContactBadge;
/**
* A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
@@ -121,7 +122,7 @@
public ContactListAdapter(Context context) {
super(context);
- mUnknownNameText = context.getText(android.R.string.unknownName);
+ mUnknownNameText = context.getText(R.string.missing_name);
mViewTypeProfileEntry = getViewTypeCount() - 1;
}
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 8993cdd..1e65612 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -24,6 +24,7 @@
import android.content.ContentUris;
import android.content.Context;
+import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
@@ -50,6 +51,7 @@
private DisplayType mDisplayType;
private Listener mListener;
private Context mContext;
+ private Resources mResources;
private Cursor mContactCursor = null;
private ContactPhotoManager mPhotoManager;
@@ -114,6 +116,7 @@
DisplayType displayType) {
mListener = listener;
mContext = context;
+ mResources = context.getResources();
mColumnCount = (displayType == DisplayType.FREQUENT_ONLY ? 1 : numCols);
mDisplayType = displayType;
@@ -213,7 +216,8 @@
String lookupKey = cursor.getString(mLookupIndex);
ContactEntry contact = new ContactEntry();
- contact.name = cursor.getString(mNameIndex);
+ String name = cursor.getString(mNameIndex);
+ contact.name = (name != null) ? name : mResources.getString(R.string.missing_name);
contact.status = cursor.getString(mStatusIndex);
contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
contact.lookupKey = ContentUris.withAppendedId(
@@ -223,8 +227,8 @@
if (mDisplayType == DisplayType.STREQUENT_PHONE_ONLY) {
int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
- contact.phoneLabel = (String) Phone.getTypeLabel(mContext.getResources(),
- phoneNumberType, phoneNumberCustomLabel);
+ contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
+ phoneNumberCustomLabel);
contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
} else {
contact.status = cursor.getString(mStatusIndex);
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 47e7173..d236e01 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -228,7 +228,7 @@
// find and prepare correct header view
mPhotoContainer = findViewById(R.id.photo_container);
- setHeaderText(R.id.name, R.string.quickcontact_missing_name);
+ setHeaderNameText(R.id.name, R.string.missing_name);
setHeaderText(R.id.status, null);
setHeaderText(R.id.timestamp, null);
setHeaderImage(R.id.presence, null);
@@ -317,12 +317,33 @@
}
};
- /** Assign this string to the view, if found in {@link #mPhotoContainer}. */
+ /** Assign this string to the view if it is not empty. */
+ private void setHeaderNameText(int id, int resId) {
+ setHeaderNameText(id, getText(resId));
+ }
+
+ /** Assign this string to the view if it is not empty. */
+ private void setHeaderNameText(int id, CharSequence value) {
+ final View view = mPhotoContainer.findViewById(id);
+ if (view instanceof TextView) {
+ if (!TextUtils.isEmpty(value)) {
+ ((TextView)view).setText(value);
+ }
+ }
+ }
+
+ /**
+ * Assign this string to the view (if found in {@link #mPhotoContainer}), or hiding this view
+ * if there is no string.
+ */
private void setHeaderText(int id, int resId) {
setHeaderText(id, getText(resId));
}
- /** Assign this string to the view, if found in {@link #mPhotoContainer}. */
+ /**
+ * Assign this string to the view (if found in {@link #mPhotoContainer}), or hiding this view
+ * if there is no string.
+ */
private void setHeaderText(int id, CharSequence value) {
final View view = mPhotoContainer.findViewById(id);
if (view instanceof TextView) {
@@ -483,7 +504,7 @@
final Drawable statusIcon = ContactPresenceIconUtil.getChatCapabilityIcon(
context, presence, chatCapability);
- setHeaderText(R.id.name, name);
+ setHeaderNameText(R.id.name, name);
// TODO: Bring this back once we have a design
// setHeaderImage(R.id.presence, statusIcon);
}
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index 3d7881b..4686c81 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -186,6 +186,10 @@
SpannableStringBuilder sb = new SpannableStringBuilder();
CharSequence name = displayName;
+ // If there is no display name, use the default missing name string
+ if (TextUtils.isEmpty(name)) {
+ name = context.getString(R.string.missing_name);
+ }
if (!TextUtils.isEmpty(phoneticName)) {
name = context.getString(R.string.widget_name_and_phonetic,
name, phoneticName);
diff --git a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
index f9b33e0..98001ae 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
@@ -37,8 +37,9 @@
public class ContactDetailDisplayUtilsTest extends AndroidTestCase {
private static final String TEST_STREAM_ITEM_TEXT = "text";
- private ViewGroup mParent;
+ private LinearLayout mParent;
private LayoutInflater mLayoutInflater;
+ private FakeOnClickListener mListener = new FakeOnClickListener();
@Override
protected void setUp() throws Exception {
@@ -71,13 +72,79 @@
assertGone(streamItemView, R.id.stream_item_comments);
}
- /**
- * Calls {@link ContactDetailDisplayUtils#addStreamItemText(LayoutInflater, Context,
- * StreamItemEntry, ViewGroup)} with the default parameters and the given stream item.
- */
- private View addStreamItemText(StreamItemEntry streamItem) {
- return ContactDetailDisplayUtils.addStreamItemText(
- mLayoutInflater, getContext(), streamItem, mParent);
+ public void testAddStreamItemToContainer_NoAction() {
+ StreamItemEntry streamItem = getTestBuilder()
+ .setAction(null)
+ .setActionUri(null)
+ .build();
+ addStreamItemToContainer(streamItem, mListener);
+ assertStreamItemNotClickable();
+ }
+
+ public void testAddStreamItemToContainer_WithActionButNoActionUri() {
+ StreamItemEntry streamItem = getTestBuilder()
+ .setAction("action")
+ .setActionUri(null)
+ .build();
+ addStreamItemToContainer(streamItem, mListener);
+ assertStreamItemNotClickable();
+ }
+
+ public void testAddStreamItemToContainer_WithActionUriButNoAction() {
+ StreamItemEntry streamItem = getTestBuilder()
+ .setAction(null)
+ .setActionUri("http://www.google.com")
+ .build();
+ addStreamItemToContainer(streamItem, mListener);
+ assertStreamItemNotClickable();
+ }
+
+ public void testAddStreamItemToContainer_WithActionAndActionUri() {
+ StreamItemEntry streamItem = getTestBuilder()
+ .setAction("action")
+ .setActionUri("http://www.google.com")
+ .build();
+ addStreamItemToContainer(streamItem, mListener);
+ assertStreamItemClickable();
+ assertStreamItemHasOnClickListener();
+ assertStreamItemHasTag(streamItem);
+ }
+
+ /** Checks that the stream item view is clickable. */
+ private void assertStreamItemClickable() {
+ View streamItemView = mParent.findViewById(R.id.stream_item_content);
+ assertNotNull("should have a stream item", streamItemView);
+ assertTrue("should be clickable", streamItemView.isClickable());
+ assertTrue("should be focusable", streamItemView.isFocusable());
+ }
+
+ /** Asserts that there is a stream item but it is not clickable. */
+ private void assertStreamItemNotClickable() {
+ View streamItemView = mParent.findViewById(R.id.stream_item_content);
+ assertNotNull("should have a stream item", streamItemView);
+ assertFalse("should not be clickable", streamItemView.isClickable());
+ assertFalse("should not be focusable", streamItemView.isFocusable());
+ }
+
+ /** Checks that the stream item view has a click listener. */
+ private void assertStreamItemHasOnClickListener() {
+ // Check that the on-click listener is invoked when clicked.
+ View streamItemView = mParent.findViewById(R.id.stream_item_content);
+ assertFalse("listener should have not been invoked yet", mListener.clicked);
+ streamItemView.performClick();
+ assertTrue("listener should have been invoked", mListener.clicked);
+ }
+
+ /** Checks that the stream item view has the given stream item as its tag. */
+ private void assertStreamItemHasTag(StreamItemEntry streamItem) {
+ // The view's tag should point to the stream item entry for this view.
+ View streamItemView = mParent.findViewById(R.id.stream_item_content);
+ Object tag = streamItemView.getTag();
+ assertNotNull("should have a tag", tag);
+ assertTrue("should be a StreamItemEntry", tag instanceof StreamItemEntry);
+ StreamItemEntry streamItemTag = (StreamItemEntry) tag;
+ // The streamItem itself should be in the tag.
+ assertSame(streamItem, streamItemTag);
}
/** Checks that the given id corresponds to a visible text view with the expected text. */
@@ -101,7 +168,8 @@
*/
private void assertSpannableEquals(Spanned expected, CharSequence actualCharSequence) {
assertEquals(expected.toString(), actualCharSequence.toString());
- assertTrue(actualCharSequence instanceof Spanned);
+ assertTrue("char sequence should be an instance of Spanned",
+ actualCharSequence instanceof Spanned);
Spanned actual = (Spanned) actualCharSequence;
assertEquals(Html.toHtml(expected), Html.toHtml(actual));
}
@@ -113,6 +181,39 @@
assertEquals(View.GONE, view.getVisibility());
}
+ /**
+ * Calls {@link ContactDetailDisplayUtils#addStreamItemText(LayoutInflater, Context,
+ * StreamItemEntry, ViewGroup)} with the default parameters and the given stream item.
+ */
+ private View addStreamItemText(StreamItemEntry streamItem) {
+ return ContactDetailDisplayUtils.addStreamItemText(
+ mLayoutInflater, getContext(), streamItem, mParent);
+ }
+
+ /**
+ * Calls {@link ContactDetailDisplayUtils#addStreamItemToContainer(LayoutInflater,
+ * Context,StreamItemEntry, LinearLayout, android.view.View.OnClickListener)} with the default
+ * parameters and the given stream item and listener.
+ */
+ private void addStreamItemToContainer(StreamItemEntry streamItem,
+ View.OnClickListener listener) {
+ ContactDetailDisplayUtils.addStreamItemToContainer(mLayoutInflater, getContext(),
+ streamItem, mParent, listener);
+ }
+
+ /**
+ * Simple fake implementation of {@link View.OnClickListener} which sets a member variable to
+ * true when clicked.
+ */
+ private final class FakeOnClickListener implements View.OnClickListener {
+ public boolean clicked = false;
+
+ @Override
+ public void onClick(View view) {
+ clicked = true;
+ }
+ }
+
private static class StreamItemEntryBuilder {
private long mId;
private String mText;
@@ -136,6 +237,16 @@
return this;
}
+ public StreamItemEntryBuilder setAction(String action) {
+ mAction = action;
+ return this;
+ }
+
+ public StreamItemEntryBuilder setActionUri(String actionUri) {
+ mActionUri = actionUri;
+ return this;
+ }
+
public StreamItemEntry build() {
return new StreamItemEntry(mId, mText, mComment, mTimestamp, mAction, mActionUri,
mResPackage, mIconRes, mLabelRes);