Merge "Auto select first group in group list on tablet"
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/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index d8ba995..a22102a 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -251,24 +251,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/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);