Merge "Relax phone number collapser"
diff --git a/res/layout/stream_item_container.xml b/res/layout/stream_item_container.xml
index 6fa15b1..ee32596 100644
--- a/res/layout/stream_item_container.xml
+++ b/res/layout/stream_item_container.xml
@@ -50,20 +50,31 @@
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary" />
- <TextView android:id="@+id/stream_item_attribution"
- android:layout_width="wrap_content"
+ <!--
+ Attribution (e.g. timestamp) and comments (e.g. +1, like) should align horizontally.
+ Can't merge this with the parent list view.
+ -->
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:ellipsize="end"
- android:maxLines="1" />
- <TextView android:id="@+id/stream_item_comments"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/detail_update_section_attribution_comments_padding"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="1"/>
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/stream_item_attribution"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="end"
+ android:maxLines="1" />
+ <TextView android:id="@+id/stream_item_comments"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft=
+ "@dimen/detail_update_section_attribution_comments_padding"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"/>
+ </LinearLayout>
</LinearLayout>
<View
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index 405ba6f..17cd1e7 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -25,6 +25,7 @@
import com.android.contacts.util.StreamItemPhotoEntry;
import com.android.contacts.util.UriUtils;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -732,8 +733,7 @@
if (!resultIsCached) loadPhotoBinaryData(result);
// Note ME profile should never have "Add connection"
- if (mLoadInvitableAccountTypes && result.getInvitableAccountTypes() == null &&
- !result.isUserProfile()) {
+ if (mLoadInvitableAccountTypes && result.getInvitableAccountTypes() == null) {
loadInvitableAccountTypes(result);
}
}
@@ -855,25 +855,29 @@
* Sets the "invitable" account types to {@link Result#mInvitableAccountTypes}.
*/
private void loadInvitableAccountTypes(Result contactData) {
- Map<AccountTypeWithDataSet, AccountType> invitables =
- AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
- if (invitables.isEmpty()) {
- return;
- }
+ final ArrayList<AccountType> resultList = Lists.newArrayList();
+ if (!contactData.isUserProfile()) {
+ Map<AccountTypeWithDataSet, AccountType> invitables =
+ AccountTypeManager.getInstance(getContext()).getUsableInvitableAccountTypes();
+ if (!invitables.isEmpty()) {
+ final Map<AccountTypeWithDataSet, AccountType> resultMap =
+ Maps.newHashMap(invitables);
- Map<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap(invitables);
+ // Remove the ones that already have a raw contact in the current contact
+ for (Entity entity : contactData.getEntities()) {
+ final ContentValues values = entity.getEntityValues();
+ final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
+ values.getAsString(RawContacts.ACCOUNT_TYPE),
+ values.getAsString(RawContacts.DATA_SET));
+ resultMap.remove(type);
+ }
- // Remove the ones that already have a raw contact in the current contact
- for (Entity entity : contactData.getEntities()) {
- final ContentValues values = entity.getEntityValues();
- final AccountTypeWithDataSet type = AccountTypeWithDataSet.get(
- values.getAsString(RawContacts.ACCOUNT_TYPE),
- values.getAsString(RawContacts.DATA_SET));
- result.remove(type);
+ resultList.addAll(resultMap.values());
+ }
}
// Set to mInvitableAccountTypes
- contactData.mInvitableAccountTypes = new ArrayList<AccountType>(result.values());
+ contactData.mInvitableAccountTypes = resultList;
}
/**
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 4089434..ae8fe09 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -497,6 +497,7 @@
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
mViewPager.setOnPageChangeListener(mPageChangeListener);
+ mViewPager.setOffscreenPageLimit(2);
// Do same width calculation as ActionBar does
DisplayMetrics dm = getResources().getDisplayMetrics();
diff --git a/src/com/android/contacts/list/PhoneFavoriteFragment.java b/src/com/android/contacts/list/PhoneFavoriteFragment.java
index 2e62d1a..011a811 100644
--- a/src/com/android/contacts/list/PhoneFavoriteFragment.java
+++ b/src/com/android/contacts/list/PhoneFavoriteFragment.java
@@ -237,10 +237,57 @@
private final ScrollListener mScrollListener = new ScrollListener();
@Override
+ public void onAttach(Activity activity) {
+ if (DEBUG) Log.d(TAG, "onAttach()");
+ super.onAttach(activity);
+
+ mContactsPrefs = new ContactsPreferences(activity);
+
+ // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
+ // We don't construct the resultant adapter at this moment since it requires LayoutInflater
+ // that will be available on onCreateView().
+
+ mContactTileAdapter = new ContactTileAdapter(activity, mContactTileAdapterListener,
+ getResources().getInteger(R.integer.contact_tile_column_count),
+ ContactTileAdapter.DisplayType.STREQUENT_PHONE_ONLY);
+ mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
+
+ // Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
+ mAllContactsAdapter = new PhoneNumberListAdapter(activity);
+ mAllContactsAdapter.setDisplayPhotos(true);
+ mAllContactsAdapter.setQuickContactEnabled(true);
+ mAllContactsAdapter.setSearchMode(false);
+ mAllContactsAdapter.setIncludeProfile(false);
+ mAllContactsAdapter.setSelectionVisible(false);
+ mAllContactsAdapter.setDarkTheme(true);
+ mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
+ // Disable directory header.
+ mAllContactsAdapter.setHasHeader(0, false);
+ // Show A-Z section index.
+ mAllContactsAdapter.setSectionHeaderDisplayEnabled(true);
+ // Disable pinned header. It doesn't work with this fragment.
+ mAllContactsAdapter.setPinnedPartitionHeadersEnabled(false);
+ // Put photos on left for consistency with "frequent" contacts section.
+ mAllContactsAdapter.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
+
+ // Use Callable.CONTENT_URI which will include not only phone numbers but also SIP
+ // addresses.
+ mAllContactsAdapter.setUseCallableUri(true);
+
+ mAllContactsAdapter.setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
+ mAllContactsAdapter.setSortOrder(mContactsPrefs.getSortOrder());
+ }
+
+ @Override
public void onCreate(Bundle savedState) {
+ if (DEBUG) Log.d(TAG, "onCreate()");
super.onCreate(savedState);
if (savedState != null) {
mFilter = savedState.getParcelable(KEY_FILTER);
+
+ if (mFilter != null) {
+ mAllContactsAdapter.setFilter(mFilter);
+ }
}
setHasOptionsMenu(true);
}
@@ -252,13 +299,6 @@
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- mContactsPrefs = new ContactsPreferences(activity);
- }
-
- @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View listLayout = inflater.inflate(
@@ -271,7 +311,16 @@
mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
- initAdapters(getActivity(), inflater);
+ // Create the account filter header but keep it hidden until "all" contacts are loaded.
+ mAccountFilterHeaderContainer = new FrameLayout(getActivity(), null);
+ mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header_for_phone_favorite,
+ mListView, false);
+ mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
+ mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
+ mAccountFilterHeaderContainer.setVisibility(View.GONE);
+
+ mAdapter = new PhoneFavoriteMergedAdapter(getActivity(),
+ mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter);
mListView.setAdapter(mAdapter);
@@ -288,59 +337,6 @@
return listLayout;
}
- /**
- * Constructs and initializes {@link #mContactTileAdapter}, {@link #mAllContactsAdapter}, and
- * {@link #mAllContactsAdapter}.
- *
- * TODO: Move all the code here to {@link PhoneFavoriteMergedAdapter} if possible.
- * There are two problems: account header (whose content changes depending on filter settings)
- * and OnClickListener (which initiates {@link Activity#startActivityForResult(Intent, int)}).
- * See also issue 5429203, 5269692, and 5432286. If we are able to have a singleton for filter,
- * this work will become easier.
- */
- private void initAdapters(Context context, LayoutInflater inflater) {
- mContactTileAdapter = new ContactTileAdapter(context, mContactTileAdapterListener,
- getResources().getInteger(R.integer.contact_tile_column_count),
- ContactTileAdapter.DisplayType.STREQUENT_PHONE_ONLY);
- mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(context));
-
- // Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
- mAllContactsAdapter = new PhoneNumberListAdapter(context);
- mAllContactsAdapter.setDisplayPhotos(true);
- mAllContactsAdapter.setQuickContactEnabled(true);
- mAllContactsAdapter.setSearchMode(false);
- mAllContactsAdapter.setIncludeProfile(false);
- mAllContactsAdapter.setSelectionVisible(false);
- mAllContactsAdapter.setDarkTheme(true);
- mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(context));
- // Disable directory header.
- mAllContactsAdapter.setHasHeader(0, false);
- // Show A-Z section index.
- mAllContactsAdapter.setSectionHeaderDisplayEnabled(true);
- // Disable pinned header. It doesn't work with this fragment.
- mAllContactsAdapter.setPinnedPartitionHeadersEnabled(false);
- // Put photos on left for consistency with "frequent" contacts section.
- mAllContactsAdapter.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
-
- mAllContactsAdapter.setUseCallableUri(true);
-
- if (mFilter != null) {
- mAllContactsAdapter.setFilter(mFilter);
- }
-
- // Create the account filter header but keep it hidden until "all" contacts are loaded.
- mAccountFilterHeaderContainer = new FrameLayout(context, null);
- mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header_for_phone_favorite,
- mListView, false);
- mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
- mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
- mAccountFilterHeaderContainer.setVisibility(View.GONE);
-
- mAdapter = new PhoneFavoriteMergedAdapter(context,
- mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter);
-
- }
-
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
@@ -381,7 +377,7 @@
mAllContactsForceReload = true;
}
- // Use initLoader() instead of reloadLoader() to refraing unnecessary reload.
+ // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
// This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
// be called, on which we'll check if "all" contacts should be reloaded again or not.
getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
@@ -431,13 +427,15 @@
}
boolean changed = false;
- if (mAllContactsAdapter.getContactNameDisplayOrder() != mContactsPrefs.getDisplayOrder()) {
- mAllContactsAdapter.setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
+ final int currentDisplayOrder = mContactsPrefs.getDisplayOrder();
+ if (mAllContactsAdapter.getContactNameDisplayOrder() != currentDisplayOrder) {
+ mAllContactsAdapter.setContactNameDisplayOrder(currentDisplayOrder);
changed = true;
}
- if (mAllContactsAdapter.getSortOrder() != mContactsPrefs.getSortOrder()) {
- mAllContactsAdapter.setSortOrder(mContactsPrefs.getSortOrder());
+ final int currentSortOrder = mContactsPrefs.getSortOrder();
+ if (mAllContactsAdapter.getSortOrder() != currentSortOrder) {
+ mAllContactsAdapter.setSortOrder(currentSortOrder);
changed = true;
}
diff --git a/src/com/android/contacts/util/StreamItemEntry.java b/src/com/android/contacts/util/StreamItemEntry.java
index 6c8210f..46684e8 100644
--- a/src/com/android/contacts/util/StreamItemEntry.java
+++ b/src/com/android/contacts/util/StreamItemEntry.java
@@ -141,6 +141,12 @@
return mPhotos;
}
+ /**
+ * Make {@link #getDecodedText} and {@link #getDecodedComments} available. Must be called
+ * before calling those.
+ *
+ * We can't do this automatically in the getters, because it'll require a {@link Context}.
+ */
public void decodeHtml(Context context) {
final Html.ImageGetter imageGetter = ContactDetailDisplayUtils.getImageGetter(context);
if (mText != null) {
@@ -152,13 +158,21 @@
}
public CharSequence getDecodedText() {
+ checkDecoded(mText, mDecodedText);
return mDecodedText;
}
public CharSequence getDecodedComments() {
+ checkDecoded(mComments, mDecodedComments);
return mDecodedComments;
}
+ private static void checkDecoded(CharSequence original, CharSequence decoded) {
+ if (original != null && decoded == null) {
+ throw new IllegalStateException("decodeHtml must have been called");
+ }
+ }
+
private static String getString(Cursor cursor, String columnName) {
return cursor.getString(cursor.getColumnIndex(columnName));
}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 04782fa..d80a35d 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -19,6 +19,8 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
diff --git a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
index fd30390..419cac8 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
@@ -51,19 +51,20 @@
}
public void testAddStreamItemText_IncludesComments() {
- StreamItemEntry streamItem = getTestBuilder().setComment("1 comment").build();
+ StreamItemEntry streamItem = getTestBuilder().setComment("1 comment").build(getContext());
View streamItemView = addStreamItemText(streamItem);
assertHasText(streamItemView, R.id.stream_item_comments, "1 comment");
}
public void testAddStreamItemText_IncludesHtmlComments() {
- StreamItemEntry streamItem = getTestBuilder().setComment("1 <b>comment</b>").build();
+ StreamItemEntry streamItem = getTestBuilder().setComment("1 <b>comment</b>")
+ .build(getContext());
View streamItemView = addStreamItemText(streamItem);
assertHasHtmlText(streamItemView, R.id.stream_item_comments, "1 <b>comment<b>");
}
public void testAddStreamItemText_NoComments() {
- StreamItemEntry streamItem = getTestBuilder().setComment(null).build();
+ StreamItemEntry streamItem = getTestBuilder().setComment(null).build(getContext());
View streamItemView = addStreamItemText(streamItem);
assertGone(streamItemView, R.id.stream_item_comments);
}
diff --git a/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java b/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java
index 131af96..cd2d6bf 100644
--- a/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java
+++ b/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java
@@ -85,7 +85,7 @@
private ArrayList<StreamItemEntry> createStreamItemList(int count) {
ArrayList<StreamItemEntry> list = Lists.newArrayList();
for (int index = 0; index < count; ++index) {
- list.add(createStreamItemEntryBuilder().build());
+ list.add(createStreamItemEntryBuilder().build(getContext()));
}
return list;
}
diff --git a/tests/src/com/android/contacts/format/SpannedTestUtils.java b/tests/src/com/android/contacts/format/SpannedTestUtils.java
index 646a7ec..ce228a7 100644
--- a/tests/src/com/android/contacts/format/SpannedTestUtils.java
+++ b/tests/src/com/android/contacts/format/SpannedTestUtils.java
@@ -41,7 +41,7 @@
// If the text is empty, it does not add the <p></p> bits to it.
Assert.assertEquals("", actualHtmlText);
} else {
- Assert.assertEquals("<p>" + expectedHtmlText + "</p>\n", actualHtmlText);
+ Assert.assertEquals("<p dir=ltr>" + expectedHtmlText + "</p>\n", actualHtmlText);
}
}
diff --git a/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java b/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java
index 319ba48..7fd9307 100644
--- a/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java
+++ b/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java
@@ -16,6 +16,10 @@
package com.android.contacts.util;
+import com.android.contacts.util.StreamItemEntry;
+
+import android.content.Context;
+
/**
* Builder for {@link StreamItemEntry}s to make writing tests easier.
*/
@@ -58,8 +62,10 @@
return this;
}
- public StreamItemEntry build() {
- return new StreamItemEntry(mId, mText, mComment, mTimestamp, mAccountType, mAccountName,
- mDataSet, mResPackage, mIconRes, mLabelRes);
+ public StreamItemEntry build(Context context) {
+ StreamItemEntry ret = new StreamItemEntry(mId, mText, mComment, mTimestamp, mAccountType,
+ mAccountName, mDataSet, mResPackage, mIconRes, mLabelRes);
+ ret.decodeHtml(context);
+ return ret;
}
}
\ No newline at end of file