About Card
Change-Id: Idfe396d0a4fa24214599990c2895ed9569e5c0fa
diff --git a/res/layout/quickcontact_content.xml b/res/layout/quickcontact_content.xml
index ec2073a..74d071c 100644
--- a/res/layout/quickcontact_content.xml
+++ b/res/layout/quickcontact_content.xml
@@ -50,6 +50,16 @@
android:visibility="gone" />
</FrameLayout>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/quickcontact_card_border">
+ <com.android.contacts.quickcontact.ExpandingEntryCardView
+ style="@style/ExpandingEntryCardStyle"
+ android:id="@+id/about_card"
+ android:visibility="gone" />
+ </FrameLayout>
+
<!-- Fill the rest of the LinearLayout with the correct background color -->
<View
android:id="@+id/card_empty_space"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 643e674..6cbe798 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -667,6 +667,9 @@
<!-- Title of recent card. [CHAR LIMIT=60] -->
<string name="recent_card_title">Recent</string>
+ <!-- Title of recent card. [CHAR LIMIT=40] -->
+ <string name="about_card_title">About</string>
+
<!-- Title of sms action entry. [CHAR LIMIT=60] -->
<string name="send_message">Send message</string>
@@ -692,4 +695,19 @@
<!-- Name of the button in the date/time picker to accept the date/time change [CHAR LIMIT=15] -->
<string name="date_time_set">Set</string>
+ <!-- Header for the IM entry [CHAR LIMIT=40] -->
+ <string name="header_im_entry">IM</string>
+ <!-- Header for the Organization entry [CHAR LIMIT=40] -->
+ <string name="header_organization_entry">Organization</string>
+ <!-- Header for the Nickname entry [CHAR LIMIT=40] -->
+ <string name="header_nickname_entry">Nickname</string>
+ <!-- Header for the Note entry [CHAR LIMIT=40] -->
+ <string name="header_note_entry">Note</string>
+ <!-- Header for the Website entry [CHAR LIMIT=40] -->
+ <string name="header_website_entry">Website</string>
+ <!-- Header for the Event entry [CHAR LIMIT=40] -->
+ <string name="header_event_entry">Event</string>
+ <!-- Header for the Relation entry [CHAR LIMIT=40] -->
+ <string name="header_relation_entry">Relation</string>
+
</resources>
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index edafa32..85581d5 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -535,9 +535,9 @@
final long rawContactId = rawContact.getId();
final AccountType accountType = rawContact.getAccountType(mContext);
for (DataItem dataItem : rawContact.getDataItems()) {
- dataItem.setRawContactId(rawContactId);
+ //dataItem.setRawContactId(rawContactId);
- if (dataItem.getMimeType() == null) continue;
+ //if (dataItem.getMimeType() == null) continue;
if (dataItem instanceof GroupMembershipDataItem) {
GroupMembershipDataItem groupMembership =
@@ -628,7 +628,7 @@
final DetailViewEntry imEntry = DetailViewEntry.fromValues(mContext, im,
mContactData.isDirectoryEntry(), mContactData.getDirectoryId(),
kind);
- buildImActions(mContext, imEntry, im);
+ // buildImActions(mContext, imEntry, im);
imEntry.setPresence(status.getPresence());
imEntry.maxLines = kind.maxLinesForDisplay;
mImEntries.add(imEntry);
@@ -639,20 +639,20 @@
mPostalEntries.add(entry);
} else if (dataItem instanceof ImDataItem && hasData) {
// Build IM entries
- buildImActions(mContext, entry, (ImDataItem) dataItem);
+ /*buildImActions(mContext, entry, (ImDataItem) dataItem);
// Apply presence when available
final DataStatus status = mContactData.getStatuses().get(entry.id);
if (status != null) {
entry.setPresence(status.getPresence());
}
- mImEntries.add(entry);
+ mImEntries.add(entry);*/
} else if (dataItem instanceof OrganizationDataItem) {
// Organizations are not shown. The first one is shown in the header
// and subsequent ones are not supported anymore
} else if (dataItem instanceof NicknameDataItem && hasData) {
// Build nickname entries
- final boolean isNameRawContact =
+ /*final boolean isNameRawContact =
(mContactData.getNameRawContactId() == rawContactId);
final boolean duplicatesTitle =
@@ -662,14 +662,14 @@
if (!duplicatesTitle) {
entry.uri = null;
mNicknameEntries.add(entry);
- }
+ }*/
} else if (dataItem instanceof NoteDataItem && hasData) {
// Build note entries
- entry.uri = null;
- mNoteEntries.add(entry);
+ // entry.uri = null;
+ //mNoteEntries.add(entry);
} else if (dataItem instanceof WebsiteDataItem && hasData) {
// Build Website entries
- entry.uri = null;
+ /*entry.uri = null;
try {
WebAddress webAddress = new WebAddress(entry.data);
entry.intent = new Intent(Intent.ACTION_VIEW,
@@ -677,17 +677,17 @@
} catch (ParseException e) {
Log.e(TAG, "Couldn't parse website: " + entry.data);
}
- mWebsiteEntries.add(entry);
+ mWebsiteEntries.add(entry);*/
} else if (dataItem instanceof SipAddressDataItem && hasData) {
// Build SipAddress entries
- entry.uri = null;
+ /*entry.uri = null;
if (hasSip) {
entry.intent = CallUtil.getCallIntent(
Uri.fromParts(CallUtil.SCHEME_SIP, entry.data, null));
} else {
entry.intent = null;
}
- mSipEntries.add(entry);
+ mSipEntries.add(entry);*/
// TODO: Now that SipAddress is in its own list of entries
// (instead of grouped in mOtherEntries), consider
// repositioning it right under the phone number.
@@ -695,7 +695,7 @@
// secondary=false for this field, and tweak the weight
// of its DataKind.)
} else if (dataItem instanceof EventDataItem && hasData) {
- final Calendar cal = DateUtils.parseDate(entry.data, false);
+ /*final Calendar cal = DateUtils.parseDate(entry.data, false);
if (cal != null) {
final Date nextAnniversary =
DateUtils.getNextAnnualDate(cal);
@@ -706,12 +706,12 @@
}
entry.data = DateUtils.formatDate(mContext, entry.data);
entry.uri = null;
- mEventEntries.add(entry);
+ mEventEntries.add(entry);*/
} else if (dataItem instanceof RelationDataItem && hasData) {
- entry.intent = new Intent(Intent.ACTION_SEARCH);
+ /*entry.intent = new Intent(Intent.ACTION_SEARCH);
entry.intent.putExtra(SearchManager.QUERY, entry.data);
entry.intent.setType(Contacts.CONTENT_TYPE);
- mRelationEntries.add(entry);
+ mRelationEntries.add(entry);*/
} else {
// Handle showing custom rows
entry.intent = new Intent(Intent.ACTION_VIEW);
@@ -953,7 +953,7 @@
/**
* Writes the Instant Messaging action into the given entry value.
*/
- @VisibleForTesting
+ /*@VisibleForTesting
public static void buildImActions(Context context, DetailViewEntry entry,
ImDataItem im) {
final boolean isEmail = im.isCreatedFromEmail();
@@ -997,9 +997,9 @@
entry.intent = imIntent;
}
}
- }
+ }*/
- @VisibleForTesting
+ /*@VisibleForTesting
public static Intent getCustomIMIntent(ImDataItem im, int protocol) {
String host = im.getCustomProtocol();
final String data = im.getData();
@@ -1018,7 +1018,7 @@
authority).appendPath(data).build();
final Intent intent = new Intent(Intent.ACTION_SENDTO, imUri);
return intent;
- }
+ }*/
/**
* Show a list popup. Used for "popup-able" entry, such as "More networks".
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index 390fc28..51a8608 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -318,7 +318,10 @@
// Entry icons
if (mEntries != null) {
for (Entry entry : mEntries) {
- entry.getIcon().setColorFilter(mThemeColorFilter);
+ Drawable icon = entry.getIcon();
+ if (icon != null) {
+ icon.setColorFilter(mThemeColorFilter);
+ }
}
}
@@ -329,12 +332,17 @@
}
}
+ // TODO add accessibility content descriptions
private View createEntryView(LayoutInflater layoutInflater, Entry entry) {
View view = layoutInflater.inflate(
R.layout.expanding_entry_card_item, this, false);
ImageView icon = (ImageView) view.findViewById(R.id.icon);
- icon.setImageDrawable(entry.getIcon());
+ if (entry.getIcon() != null) {
+ icon.setImageDrawable(entry.getIcon());
+ } else {
+ icon.setVisibility(View.GONE);
+ }
TextView header = (TextView) view.findViewById(R.id.header);
if (entry.getHeader() != null) {
@@ -486,4 +494,8 @@
}
mTitleTextView.setText(title);
}
+
+ public boolean shouldShow() {
+ return mEntries != null && mEntries.size() > 0;
+ }
}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index b7b301d..7617128 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -24,38 +24,54 @@
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
+import android.app.SearchManager;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
-import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
+import android.net.ParseException;
import android.net.Uri;
+import android.net.WebAddress;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Trace;
+import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Identity;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
import android.support.v7.graphics.Palette;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
@@ -66,6 +82,9 @@
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
+import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.Collapser;
+import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.editor.SelectAccountDialogFragment;
import com.android.contacts.common.lettertiles.LetterTileDrawable;
import com.android.contacts.common.list.ShortcutIntentBuilder;
@@ -79,9 +98,18 @@
import com.android.contacts.common.model.dataitem.DataItem;
import com.android.contacts.common.model.dataitem.DataKind;
import com.android.contacts.common.model.dataitem.EmailDataItem;
+import com.android.contacts.common.model.dataitem.EventDataItem;
import com.android.contacts.common.model.dataitem.ImDataItem;
+import com.android.contacts.common.model.dataitem.NicknameDataItem;
+import com.android.contacts.common.model.dataitem.NoteDataItem;
+import com.android.contacts.common.model.dataitem.OrganizationDataItem;
import com.android.contacts.common.model.dataitem.PhoneDataItem;
-import com.android.contacts.common.util.DataStatus;
+import com.android.contacts.common.model.dataitem.RelationDataItem;
+import com.android.contacts.common.model.dataitem.SipAddressDataItem;
+import com.android.contacts.common.model.dataitem.StructuredNameDataItem;
+import com.android.contacts.common.model.dataitem.StructuredPostalDataItem;
+import com.android.contacts.common.model.dataitem.WebsiteDataItem;
+import com.android.contacts.common.util.DateUtils;
import com.android.contacts.detail.ContactDetailDisplayUtils;
import com.android.contacts.interactions.CalendarInteractionsLoader;
import com.android.contacts.interactions.CallLogInteractionsLoader;
@@ -91,16 +119,21 @@
import com.android.contacts.quickcontact.ExpandingEntryCardView.Entry;
import com.android.contacts.quickcontact.ExpandingEntryCardView.ExpandingEntryCardViewListener;
import com.android.contacts.util.ImageViewDrawableSetter;
+import com.android.contacts.util.PhoneCapabilityTester;
import com.android.contacts.util.SchedulingUtils;
+import com.android.contacts.util.StructuredPostalUtils;
import com.android.contacts.widget.MultiShrinkScroller;
import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListener;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -146,17 +179,31 @@
private ImageView mPhotoView;
private View mTransparentView;
- private ExpandingEntryCardView mCommunicationCard;
+ private ExpandingEntryCardView mContactCard;
private ExpandingEntryCardView mRecentCard;
+ private ExpandingEntryCardView mAboutCard;
+ /**
+ * This list contains all the {@link DataItem}s. Each nested list contains all data items of a
+ * specific mimetype in sorted order, using mWithinMimeTypeDataItemComparator. The mimetype
+ * lists are sorted using mAmongstMimeTypeDataItemComparator.
+ */
+ private List<List<DataItem>> mDataItemsList;
+ /**
+ * A map between a mimetype string and the corresponding list of data items. The data items
+ * are in sorted order using mWithinMimeTypeDataItemComparator.
+ */
+ private Map<String, List<DataItem>> mDataItemsMap;
private MultiShrinkScroller mScroller;
private SelectAccountDialogFragmentListener mSelectAccountFragmentListener;
- private AsyncTask<Void, Void, Void> mEntriesAndActionsTask;
+ private AsyncTask<Void, Void, Pair<List<List<DataItem>>, Map<String, List<DataItem>>>>
+ mEntriesAndActionsTask;
private ColorDrawable mWindowScrim;
private boolean mIsWaitingForOtherPieceOfExitAnimation;
private boolean mIsExitAnimationInProgress;
private boolean mHasComputedThemeColor;
+ private ComponentName mSmsComponent;
- private static final int MIN_NUM_COMMUNICATION_ENTRIES_SHOWN = 3;
+ private static final int MIN_NUM_CONTACT_ENTRIES_SHOWN = 3;
private static final int MIN_NUM_COLLAPSED_RECENT_ENTRIES_SHOWN = 3;
private Contact mContactData;
@@ -166,11 +213,6 @@
private final ImageViewDrawableSetter mPhotoSetter = new ImageViewDrawableSetter();
/**
- * Keeps the default action per mimetype. Empty if no default actions are set
- */
- private HashMap<String, Action> mDefaultsMap = new HashMap<String, Action>();
-
- /**
* {@link #LEADING_MIMETYPES} and {@link #TRAILING_MIMETYPES} are used to sort MIME-types.
*
* <p>The MIME-types in {@link #LEADING_MIMETYPES} appear in the front of the dialog,
@@ -188,6 +230,11 @@
private static final List<String> TRAILING_MIMETYPES = Lists.newArrayList(
StructuredPostal.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE);
+ private static final List<String> ABOUT_CARD_MIMETYPES = Lists.newArrayList(
+ Event.CONTENT_ITEM_TYPE, GroupMembership.CONTENT_ITEM_TYPE, Identity.CONTENT_ITEM_TYPE,
+ Im.CONTENT_ITEM_TYPE, Nickname.CONTENT_ITEM_TYPE, Note.CONTENT_ITEM_TYPE,
+ Organization.CONTENT_ITEM_TYPE, Relation.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE);
+
/** Id for the background contact loader */
private static final int LOADER_CONTACT_ID = 0;
@@ -226,7 +273,7 @@
@Override
public void onClick(View v) {
Log.i(TAG, "mEntryClickHandler onClick");
- Object intent = v.getTag();
+ final Object intent = v.getTag();
if (intent == null || !(intent instanceof Intent)) {
return;
}
@@ -316,6 +363,88 @@
}
};
+
+ /**
+ * Data items are compared to the same mimetype based off of three qualities:
+ * 1. Super primary
+ * 2. Primary
+ * 3. Times used
+ */
+ private final Comparator<DataItem> mWithinMimeTypeDataItemComparator =
+ new Comparator<DataItem>() {
+ @Override
+ public int compare(DataItem lhs, DataItem rhs) {
+ if (!lhs.getMimeType().equals(rhs.getMimeType())) {
+ Log.wtf(TAG, "Comparing DataItems with different mimetypes lhs.getMimeType(): " +
+ lhs.getMimeType() + " rhs.getMimeType(): " + rhs.getMimeType());
+ return 0;
+ }
+
+ if (lhs.isSuperPrimary()) {
+ return -1;
+ } else if (rhs.isSuperPrimary()) {
+ return 1;
+ } else if (lhs.isPrimary() && !rhs.isPrimary()) {
+ return -1;
+ } else if (!lhs.isPrimary() && rhs.isPrimary()) {
+ return 1;
+ } else {
+ final int lhsTimesUsed =
+ lhs.getTimesUsed() == null ? 0 : lhs.getTimesUsed();
+ final int rhsTimesUsed =
+ rhs.getTimesUsed() == null ? 0 : rhs.getTimesUsed();
+
+ return rhsTimesUsed - lhsTimesUsed;
+ }
+ }
+ };
+
+ private final Comparator<List<DataItem>> mAmongstMimeTypeDataItemComparator =
+ new Comparator<List<DataItem>> () {
+ @Override
+ public int compare(List<DataItem> lhsList, List<DataItem> rhsList) {
+ DataItem lhs = lhsList.get(0);
+ DataItem rhs = rhsList.get(0);
+ final int lhsTimesUsed = lhs.getTimesUsed() == null ? 0 : lhs.getTimesUsed();
+ final int rhsTimesUsed = rhs.getTimesUsed() == null ? 0 : rhs.getTimesUsed();
+ final int timesUsedDifference = rhsTimesUsed - lhsTimesUsed;
+ if (timesUsedDifference != 0) {
+ return timesUsedDifference;
+ }
+
+ final long lhsLastTimeUsed =
+ lhs.getLastTimeUsed() == null ? 0 : lhs.getLastTimeUsed();
+ final long rhsLastTimeUsed =
+ rhs.getLastTimeUsed() == null ? 0 : rhs.getLastTimeUsed();
+ final long lastTimeUsedDifference = rhsLastTimeUsed - lhsLastTimeUsed;
+ if (lastTimeUsedDifference > 0) {
+ return 1;
+ } else if (lastTimeUsedDifference < 0) {
+ return -1;
+ }
+
+ // Times used and last time used are the same. Resort to statically defined.
+ final String lhsMimeType = lhs.getMimeType();
+ final String rhsMimeType = rhs.getMimeType();
+ for (String mimeType : LEADING_MIMETYPES) {
+ if (lhsMimeType.equals(mimeType)) {
+ return -1;
+ } else if (rhsMimeType.equals(mimeType)) {
+ return 1;
+ }
+ }
+ // Trailing types come last, so flip the returns
+ for (String mimeType : TRAILING_MIMETYPES) {
+ if (lhsMimeType.equals(mimeType)) {
+ return 1;
+ } else if (rhsMimeType.equals(mimeType)) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
Trace.beginSection("onCreate()");
@@ -334,18 +463,23 @@
setContentView(R.layout.quickcontact_activity);
- mCommunicationCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
+ mSmsComponent = PhoneCapabilityTester.getSmsComponent(this);
+
+ mContactCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
mRecentCard = (ExpandingEntryCardView) findViewById(R.id.recent_card);
+ mAboutCard = (ExpandingEntryCardView) findViewById(R.id.about_card);
mScroller = (MultiShrinkScroller) findViewById(R.id.multiscroller);
- mCommunicationCard.setOnClickListener(mEntryClickHandler);
- mCommunicationCard.setTitle(getResources().getString(R.string.communication_card_title));
- mCommunicationCard.setExpandButtonText(
+ mContactCard.setOnClickListener(mEntryClickHandler);
+ mContactCard.setTitle(getResources().getString(R.string.communication_card_title));
+ mContactCard.setExpandButtonText(
getResources().getString(R.string.expanding_entry_card_view_see_all));
mRecentCard.setOnClickListener(mEntryClickHandler);
mRecentCard.setTitle(getResources().getString(R.string.recent_card_title));
+ mAboutCard.setOnClickListener(mEntryClickHandler);
+
mPhotoView = (ImageView) findViewById(R.id.photo);
mTransparentView = findViewById(R.id.transparent_view);
if (mScroller != null) {
@@ -520,8 +654,6 @@
mContactData = data;
invalidateOptionsMenu();
- mDefaultsMap.clear();
-
Trace.endSection();
Trace.beginSection("Set display photo & name");
@@ -532,28 +664,26 @@
Trace.endSection();
- // Maintain a list of phone numbers to pass into SmsInteractionsLoader
- final Set<String> phoneNumbers = new HashSet<>();
- // Maintain a list of email addresses to pass into CalendarInteractionsLoader
- final Set<String> emailAddresses = new HashSet<>();
- // List of Entry that makes up the ExpandingEntryCardView
- final List<Entry> entries = Lists.newArrayList();
+ mEntriesAndActionsTask = new AsyncTask<Void, Void,
+ Pair<List<List<DataItem>>, Map<String, List<DataItem>>>>() {
- mEntriesAndActionsTask = new AsyncTask<Void, Void, Void>() {
@Override
- protected Void doInBackground(Void... params) {
- computeEntriesAndActions(data, phoneNumbers, emailAddresses, entries);
- return null;
+ protected Pair<List<List<DataItem>>, Map<String, List<DataItem>>> doInBackground(
+ Void... params) {
+ return generateDataModelFromContact(data);
}
@Override
- protected void onPostExecute(Void aVoid) {
- super.onPostExecute(aVoid);
+ protected void onPostExecute(Pair<List<List<DataItem>>,
+ Map<String, List<DataItem>>> dataItemsPair) {
+ super.onPostExecute(dataItemsPair);
+ mDataItemsList = dataItemsPair.first;
+ mDataItemsMap = dataItemsPair.second;
// Check that original AsyncTask parameters are still valid and the activity
// is still running before binding to UI. A new intent could invalidate
// the results, for example.
if (data == mContactData && !isCancelled()) {
- bindEntriesAndActions(entries, phoneNumbers, emailAddresses);
+ bindDataToCards();
showActivity();
}
}
@@ -561,13 +691,24 @@
mEntriesAndActionsTask.execute();
}
- private void bindEntriesAndActions(List<Entry> entries,
- Set<String> phoneNumbers,
- Set<String> emailAddresses) {
- Trace.beginSection("start sms loader");
+ private void bindDataToCards() {
+ startInteractionLoaders();
+ populateContactAndAboutCard();
+ }
+
+ private void startInteractionLoaders() {
+ final List<DataItem> phoneDataItems = mDataItemsMap.get(Phone.CONTENT_ITEM_TYPE);
+ String[] phoneNumbers = null;
+ if (phoneDataItems != null) {
+ phoneNumbers = new String[phoneDataItems.size()];
+ for (int i = 0; i < phoneDataItems.size(); ++i) {
+ phoneNumbers[i] = ((PhoneDataItem) phoneDataItems.get(i)).getNumber();
+ }
+ }
final Bundle phonesExtraBundle = new Bundle();
- phonesExtraBundle.putStringArray(KEY_LOADER_EXTRA_PHONES,
- phoneNumbers.toArray(new String[phoneNumbers.size()]));
+ phonesExtraBundle.putStringArray(KEY_LOADER_EXTRA_PHONES, phoneNumbers);
+
+ Trace.beginSection("start sms loader");
getLoaderManager().initLoader(
LOADER_SMS_ID,
phonesExtraBundle,
@@ -581,27 +722,23 @@
mLoaderInteractionsCallbacks);
Trace.endSection();
+
Trace.beginSection("start calendar loader");
+ final List<DataItem> emailDataItems = mDataItemsMap.get(Email.CONTENT_ITEM_TYPE);
+ String[] emailAddresses = null;
+ if (emailDataItems != null) {
+ emailAddresses = new String[emailDataItems.size()];
+ for (int i = 0; i < emailDataItems.size(); ++i) {
+ emailAddresses[i] = ((EmailDataItem) emailDataItems.get(i)).getAddress();
+ }
+ }
final Bundle emailsExtraBundle = new Bundle();
- emailsExtraBundle.putStringArray(KEY_LOADER_EXTRA_EMAILS,
- emailAddresses.toArray(new String[emailAddresses.size()]));
+ emailsExtraBundle.putStringArray(KEY_LOADER_EXTRA_EMAILS, emailAddresses);
getLoaderManager().initLoader(
LOADER_CALENDAR_ID,
emailsExtraBundle,
mLoaderInteractionsCallbacks);
Trace.endSection();
-
- Trace.beginSection("bind communicate card");
- if (entries.size() > 0) {
- mCommunicationCard.initialize(entries,
- /* numInitialVisibleEntries = */ MIN_NUM_COMMUNICATION_ENTRIES_SHOWN,
- /* isExpanded = */ false, mExpandingEntryCardViewListener);
- }
-
- final boolean hasData = !entries.isEmpty();
- mCommunicationCard.setVisibility(hasData ? View.VISIBLE : View.GONE);
-
- Trace.endSection();
}
private void showActivity() {
@@ -617,157 +754,322 @@
}
}
- private void computeEntriesAndActions(Contact data, Set<String> phoneNumbers,
- Set<String> emailAddresses, List<Entry> entries) {
- Trace.beginSection("inflate entries and actions");
+ private void populateContactAndAboutCard() {
+ Trace.beginSection("bind contact card");
- // Map from {@link String} MIME-type to a list of {@link Action}.
- final ActionMultiMap actions = new ActionMultiMap();
+ final List<Entry> contactCardEntries = new ArrayList<>();
+ final List<Entry> aboutCardEntries = new ArrayList<>();
+
+ int topContactIndex = 0;
+ for (int i = 0; i < mDataItemsList.size(); ++i) {
+ final List<DataItem> dataItemsByMimeType = mDataItemsList.get(i);
+ final DataItem topDataItem = dataItemsByMimeType.get(0);
+ if (ABOUT_CARD_MIMETYPES.contains(topDataItem.getMimeType())) {
+ aboutCardEntries.addAll(dataItemsToEntries(mDataItemsList.get(i)));
+ } else {
+ // Add most used to the top of the contact card
+ final Entry topEntry = dataItemToEntry(topDataItem);
+ if (topEntry != null) {
+ contactCardEntries.add(topContactIndex++, dataItemToEntry(topDataItem));
+ }
+ // TODO merge SMS into secondary action
+ if (topDataItem instanceof PhoneDataItem) {
+ final PhoneDataItem phone = (PhoneDataItem) topDataItem;
+ Intent smsIntent = null;
+ if (mSmsComponent != null) {
+ smsIntent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts(CallUtil.SCHEME_SMSTO, phone.getNumber(), null));
+ smsIntent.setComponent(mSmsComponent);
+ }
+ contactCardEntries.add(topContactIndex++,
+ new Entry(
+ getResources().getDrawable(R.drawable.ic_message_24dp),
+ getResources().getString(R.string.send_message),
+ /* subHeader = */ null,
+ /* text = */ phone.buildDataString(
+ this, topDataItem.getDataKind()),
+ smsIntent,
+ /* isEditable = */ false));
+ }
+ // Add the rest of the entries to the bottom of the card
+ if (dataItemsByMimeType.size() > 1) {
+ contactCardEntries.addAll(dataItemsToEntries(
+ dataItemsByMimeType.subList(1, dataItemsByMimeType.size())));
+ }
+ }
+ }
+
+ if (contactCardEntries.size() > 0) {
+ mContactCard.initialize(contactCardEntries,
+ /* numInitialVisibleEntries = */ MIN_NUM_CONTACT_ENTRIES_SHOWN,
+ /* isExpanded = */ false,
+ mExpandingEntryCardViewListener);
+ mContactCard.setVisibility(View.VISIBLE);
+ } else {
+ mContactCard.setVisibility(View.GONE);
+ }
+ Trace.endSection();
+
+ Trace.beginSection("bind about card");
+ mAboutCard.initialize(aboutCardEntries,
+ /* numInitialVisibleEntries = */ 1,
+ /* isExpanded = */ true,
+ mExpandingEntryCardViewListener);
+ Trace.endSection();
+ }
+
+ /**
+ * Builds the {@link DataItem}s Map out of the Contact.
+ * @param data The contact to build the data from.
+ * @return A pair containing a list of data items sorted within mimetype and sorted
+ * amongst mimetype. The map goes from mimetype string to the sorted list of data items within
+ * mimetype
+ */
+ private Pair<List<List<DataItem>>, Map<String, List<DataItem>>> generateDataModelFromContact(
+ Contact data) {
+ Trace.beginSection("Build data items map");
+
+ final Map<String, List<DataItem>> dataItemsMap = new HashMap<>();
final ResolveCache cache = ResolveCache.getInstance(this);
for (RawContact rawContact : data.getRawContacts()) {
for (DataItem dataItem : rawContact.getDataItems()) {
+ dataItem.setRawContactId(rawContact.getId());
+
final String mimeType = dataItem.getMimeType();
+ if (mimeType == null) continue;
+
final AccountType accountType = rawContact.getAccountType(this);
final DataKind dataKind = AccountTypeManager.getInstance(this)
.getKindOrFallback(accountType, mimeType);
+ if (dataKind == null) continue;
- if (dataItem instanceof PhoneDataItem) {
- phoneNumbers.add(((PhoneDataItem) dataItem).getNormalizedNumber());
+ dataItem.setDataKind(dataKind);
+
+ final boolean hasData = !TextUtils.isEmpty(dataItem.buildDataString(this,
+ dataKind));
+
+ if (isMimeExcluded(mimeType) || !hasData) continue;
+
+ List<DataItem> dataItemListByType = dataItemsMap.get(mimeType);
+ if (dataItemListByType == null) {
+ dataItemListByType = new ArrayList<>();
+ dataItemsMap.put(mimeType, dataItemListByType);
}
-
- if (dataItem instanceof EmailDataItem) {
- emailAddresses.add(((EmailDataItem) dataItem).getAddress());
- }
-
- // Skip this data item if MIME-type excluded
- if (isMimeExcluded(mimeType)) continue;
-
- final long dataId = dataItem.getId();
- final boolean isPrimary = dataItem.isPrimary();
- final boolean isSuperPrimary = dataItem.isSuperPrimary();
-
- if (dataKind != null) {
- // Build an action for this data entry, find a mapping to a UI
- // element, build its summary from the cursor, and collect it
- // along with all others of this MIME-type.
- final Action action = new DataAction(getApplicationContext(),
- dataItem, dataKind);
- final boolean wasAdded = considerAdd(action, cache, isSuperPrimary, actions);
- if (wasAdded) {
- // Remember the default
- if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
- mDefaultsMap.put(mimeType, action);
- }
- }
- }
-
- // Handle Email rows with presence data as Im entry
- final DataStatus status = data.getStatuses().get(dataId);
- if (status != null && dataItem instanceof EmailDataItem) {
- final EmailDataItem email = (EmailDataItem) dataItem;
- final ImDataItem im = ImDataItem.createFromEmail(email);
- if (dataKind != null) {
- final DataAction action = new DataAction(getApplicationContext(),
- im, dataKind);
- action.setPresence(status.getPresence());
- considerAdd(action, cache, isSuperPrimary, actions);
- }
- }
+ dataItemListByType.add(dataItem);
}
}
-
Trace.endSection();
- Trace.beginSection("collapsing action list");
- Trace.endSection();
- Trace.beginSection("sort mimetypes");
-
+ Trace.beginSection("sort within mimetypes");
/*
* Sorting is a multi part step. The end result is to a have a sorted list of the most
- * used actions, one per mimetype. Then, within each mimetype, the list of actions for that
- * type is also sorted, based off of {super primary, primary, times used} in that order.
+ * used data items, one per mimetype. Then, within each mimetype, the list of data items
+ * for that type is also sorted, based off of {super primary, primary, times used} in that
+ * order.
*/
- final List<Action> topActions = new ArrayList<>();
- final List<Action> allActions = new ArrayList<>();
- for (List<Action> mimeTypeActions : actions.values()) {
- Collections.sort(mimeTypeActions, new Comparator<Action>() {
- @Override
- public int compare(Action lhs, Action rhs) {
- /*
- * Actions are compared to the same mimetype based off of three qualities:
- * 1. Super primary
- * 2. Primary
- * 3. Times used
- */
- if (lhs.isSuperPrimary()) {
- return -1;
- } else if (rhs.isSuperPrimary()) {
- return 1;
- } else if (lhs.isPrimary() && !rhs.isPrimary()) {
- return -1;
- } else if (!lhs.isPrimary() && rhs.isPrimary()) {
- return 1;
- } else {
- int lhsTimesUsed = lhs.getTimesUsed() == null ? 0 : lhs.getTimesUsed();
- int rhsTimesUsed = rhs.getTimesUsed() == null ? 0 : rhs.getTimesUsed();
+ final List<List<DataItem>> dataItemsList = new ArrayList<>();
+ for (List<DataItem> mimeTypeDataItems : dataItemsMap.values()) {
+ // Remove duplicate data items
+ Collapser.collapseList(mimeTypeDataItems, this);
+ // Sort within mimetype
+ Collections.sort(mimeTypeDataItems, mWithinMimeTypeDataItemComparator);
+ // Add to the list of data item lists
+ dataItemsList.add(mimeTypeDataItems);
+ }
+ Trace.endSection();
- return rhsTimesUsed - lhsTimesUsed;
- }
+ Trace.beginSection("sort amongst mimetypes");
+ // Sort amongst mimetypes to bubble up the top data items for the contact card
+ Collections.sort(dataItemsList, mAmongstMimeTypeDataItemComparator);
+ Trace.endSection();
+
+ return new Pair<>(dataItemsList, dataItemsMap);
+ }
+
+ /**
+ * Converts a {@link DataItem} into an {@link ExpandingEntryCardView.Entry} for display.
+ * If the {@link ExpandingEntryCardView.Entry} has no visual elements, null is returned.
+ * @param dataItem The {@link DataItem} to convert.
+ * @return The {@link ExpandingEntryCardView.Entry}, or null if no visual elements are present.
+ */
+ private Entry dataItemToEntry(DataItem dataItem) {
+ Drawable icon = null;
+ String header = null;
+ String subHeader = null;
+ Drawable subHeaderIcon = null;
+ String text = null;
+ Drawable textIcon = null;
+ Intent intent = null;
+ final boolean isEditable = false;
+
+ DataKind kind = dataItem.getDataKind();
+
+ if (dataItem instanceof ImDataItem) {
+ final ImDataItem im = (ImDataItem) dataItem;
+ intent = ContactsUtils.buildImIntent(this, im).first;
+ header = getResources().getString(R.string.header_im_entry);
+ final boolean isEmail = im.isCreatedFromEmail();
+ final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol();
+ subHeader = Im.getProtocolLabel(getResources(), protocol,
+ im.getCustomProtocol()).toString();
+ } else if (dataItem instanceof OrganizationDataItem) {
+ final OrganizationDataItem organization = (OrganizationDataItem) dataItem;
+ header = getResources().getString(R.string.header_organization_entry);
+ subHeader = organization.getCompany();
+ text = organization.getTitle();
+ } else if (dataItem instanceof NicknameDataItem) {
+ final NicknameDataItem nickname = (NicknameDataItem) dataItem;
+ // Build nickname entries
+ final boolean isNameRawContact =
+ (mContactData.getNameRawContactId() == dataItem.getRawContactId());
+
+ final boolean duplicatesTitle =
+ isNameRawContact
+ && mContactData.getDisplayNameSource() == DisplayNameSources.NICKNAME;
+
+ if (!duplicatesTitle) {
+ header = getResources().getString(R.string.header_nickname_entry);
+ subHeader = nickname.getName();
+ }
+ } else if (dataItem instanceof NoteDataItem) {
+ final NoteDataItem note = (NoteDataItem) dataItem;
+ header = getResources().getString(R.string.header_note_entry);
+ subHeader = note.getNote();
+ } else if (dataItem instanceof WebsiteDataItem) {
+ final WebsiteDataItem website = (WebsiteDataItem) dataItem;
+ header = getResources().getString(R.string.header_website_entry);
+ subHeader = website.getUrl();
+ try {
+ final WebAddress webAddress = new WebAddress(website.buildDataString(this, kind));
+ intent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
+ } catch (final ParseException e) {
+ Log.e(TAG, "Couldn't parse website: " + website.buildDataString(this, kind));
+ }
+ } else if (dataItem instanceof EventDataItem) {
+ final EventDataItem event = (EventDataItem) dataItem;
+ final String dataString = event.buildDataString(this, kind);
+ final Calendar cal = DateUtils.parseDate(dataString, false);
+ if (cal != null) {
+ final Date nextAnniversary =
+ DateUtils.getNextAnnualDate(cal);
+ final Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
+ builder.appendPath("time");
+ ContentUris.appendId(builder, nextAnniversary.getTime());
+ intent = new Intent(Intent.ACTION_VIEW).setData(builder.build());
+ }
+ header = getResources().getString(R.string.header_event_entry);
+ subHeader = getResources().getString(Event.getTypeResource(
+ event.getKindTypeColumn(kind)));
+ text = DateUtils.formatDate(this, dataString);
+ } else if (dataItem instanceof RelationDataItem) {
+ final RelationDataItem relation = (RelationDataItem) dataItem;
+ final String dataString = relation.buildDataString(this, kind);
+ if (!TextUtils.isEmpty(dataString)) {
+ intent = new Intent(Intent.ACTION_SEARCH);
+ intent.putExtra(SearchManager.QUERY, dataString);
+ intent.setType(Contacts.CONTENT_TYPE);
+ }
+ header = getResources().getString(R.string.header_relation_entry);
+ subHeader = relation.getName();
+ text = Relation.getTypeLabel(getResources(), relation.getKindTypeColumn(kind),
+ relation.getLabel()).toString();
+ } else if (dataItem instanceof PhoneDataItem) {
+ final PhoneDataItem phone = (PhoneDataItem) dataItem;
+ if (!TextUtils.isEmpty(phone.getNumber())) {
+ header = phone.buildDataString(this, kind);
+ text = Phone.getTypeLabel(getResources(), phone.getKindTypeColumn(kind),
+ phone.getLabel()).toString();
+ icon = getResources().getDrawable(R.drawable.ic_phone_24dp);
+ if (PhoneCapabilityTester.isPhone(this)) {
+ intent = CallUtil.getCallIntent(phone.getNumber());
}
- });
- topActions.add(mimeTypeActions.get(0));
- // Add all the other actions and remove the top one
- allActions.addAll(mimeTypeActions);
- allActions.remove(mimeTypeActions.get(0));
+ }
+ } else if (dataItem instanceof EmailDataItem) {
+ final EmailDataItem email = (EmailDataItem) dataItem;
+ final String address = email.getData();
+ if (!TextUtils.isEmpty(address)) {
+ final Uri mailUri = Uri.fromParts(CallUtil.SCHEME_MAILTO, address, null);
+ intent = new Intent(Intent.ACTION_SENDTO, mailUri);
+ header = email.getAddress();
+ text = Email.getTypeLabel(getResources(), email.getKindTypeColumn(kind),
+ email.getLabel()).toString();
+ icon = getResources().getDrawable(R.drawable.ic_email_24dp);
+ }
+ } else if (dataItem instanceof StructuredPostalDataItem) {
+ StructuredPostalDataItem postal = (StructuredPostalDataItem) dataItem;
+ final String postalAddress = postal.getFormattedAddress();
+ if (!TextUtils.isEmpty(postalAddress)) {
+ intent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
+ header = postal.getFormattedAddress();
+ text = StructuredPostal.getTypeLabel(getResources(), postal.getKindTypeColumn(kind),
+ postal.getLabel()).toString();
+ icon = getResources().getDrawable(R.drawable.ic_place_24dp);
+ }
+ } else if (dataItem instanceof SipAddressDataItem) {
+ if (PhoneCapabilityTester.isSipPhone(this)) {
+ final SipAddressDataItem sip = (SipAddressDataItem) dataItem;
+ final String address = sip.getSipAddress();
+ if (!TextUtils.isEmpty(address)) {
+ final Uri callUri = Uri.fromParts(CallUtil.SCHEME_SIP, address, null);
+ intent = CallUtil.getCallIntent(callUri);
+ // Note that this item will get a SIP-specific variant
+ // of the "call phone" icon, rather than the standard
+ // app icon for the Phone app (which we show for
+ // regular phone numbers.) That's because the phone
+ // app explicitly specifies an android:icon attribute
+ // for the SIP-related intent-filters in its manifest.
+ }
+ icon = ResolveCache.getInstance(this).getIcon(sip.getMimeType(), intent);
+ // Call mutate to create a new Drawable.ConstantState for color filtering
+ if (icon != null) {
+ icon.mutate();
+ }
+ }
+ } else if (dataItem instanceof StructuredNameDataItem) {
+ final String givenName = ((StructuredNameDataItem) dataItem).getGivenName();
+ if (!TextUtils.isEmpty(givenName)) {
+ mAboutCard.setTitle(getResources().getString(R.string.about_card_title) +
+ " " + givenName);
+ } else {
+ mAboutCard.setTitle(getResources().getString(R.string.about_card_title));
+ }
+ } else {
+ // Custom DataItem
+ header = dataItem.buildDataStringForDisplay(this, kind);
+ text = kind.typeColumn;
+ intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse(dataItem.buildDataString(this, kind)),
+ dataItem.getMimeType());
+ icon = ResolveCache.getInstance(this).getIcon(dataItem.getMimeType(), intent);
}
- // topActions now contains the top action for each mimetype. This list now needs to be
- // sorted, based off of {times used, last used, statically defined} in that order.
- Collections.sort(topActions, new Comparator<Action>() {
- @Override
- public int compare(Action lhs, Action rhs) {
- int lhsTimesUsed = lhs.getTimesUsed() == null ? 0 : lhs.getTimesUsed();
- int rhsTimesUsed = rhs.getTimesUsed() == null ? 0 : rhs.getTimesUsed();
- int timesUsedDifference = rhsTimesUsed - lhsTimesUsed;
- if (timesUsedDifference != 0) {
- return timesUsedDifference;
- }
-
- long lhsLastTimeUsed = lhs.getLastTimeUsed() == null ? 0 : lhs.getLastTimeUsed();
- long rhsLastTimeUsed = rhs.getLastTimeUsed() == null ? 0 : rhs.getLastTimeUsed();
- long lastTimeUsedDifference = rhsLastTimeUsed - lhsLastTimeUsed;
- if (lastTimeUsedDifference > 0) {
- return 1;
- } else if (lastTimeUsedDifference < 0) {
- return -1;
- }
-
- // Times used and last time used are the same. Resort to statically defined.
- String lhsMimeType = lhs.getMimeType();
- String rhsMimeType = rhs.getMimeType();
- for (String mimeType : LEADING_MIMETYPES) {
- if (lhsMimeType.equals(mimeType)) {
- return -1;
- } else if (rhsMimeType.equals(mimeType)) {
- return 1;
- }
- }
- // Trailing types come last, so flip the returns
- for (String mimeType : TRAILING_MIMETYPES) {
- if (lhsMimeType.equals(mimeType)) {
- return 1;
- } else if (rhsMimeType.equals(mimeType)) {
- return -1;
- }
- }
- return 0;
+ if (intent != null) {
+ // Do not set the intent is there are no resolves
+ if (!PhoneCapabilityTester.isIntentRegistered(this, intent)) {
+ intent = null;
}
- });
+ }
- entries.addAll(actionsToEntries(topActions));
- entries.addAll(actionsToEntries(allActions));
- Trace.endSection();
+ // If the Entry has no visual elements, return null
+ if (icon == null && TextUtils.isEmpty(header) && TextUtils.isEmpty(subHeader) &&
+ subHeaderIcon == null && TextUtils.isEmpty(text) && textIcon == null) {
+ return null;
+ }
+
+ return new Entry(icon, header, subHeader, subHeaderIcon, text, textIcon,
+ intent, isEditable);
+ }
+
+ private List<Entry> dataItemsToEntries(List<DataItem> dataItems) {
+ final List<Entry> entries = new ArrayList<>();
+ for (DataItem dataItem : dataItems) {
+ final Entry entry = dataItemToEntry(dataItem);
+ if (entry != null) {
+ entries.add(entry);
+ }
+ }
+ return entries;
}
/**
@@ -856,8 +1158,9 @@
mColorFilter =
new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
- mCommunicationCard.setColorAndFilter(color, mColorFilter);
+ mContactCard.setColorAndFilter(color, mColorFilter);
mRecentCard.setColorAndFilter(color, mColorFilter);
+ mAboutCard.setColorAndFilter(color, mColorFilter);
}
private void updateStatusBarColor() {
@@ -891,65 +1194,8 @@
return 0;
}
- /**
- * Consider adding the given {@link Action}, which will only happen if
- * {@link PackageManager} finds an application to handle
- * {@link Action#getIntent()}.
- * @param action the action to handle
- * @param resolveCache cache of applications that can handle actions
- * @param front indicates whether to add the action to the front of the list
- * @param actions where to put the action.
- * @return true if action has been added
- */
- private boolean considerAdd(Action action, ResolveCache resolveCache, boolean front,
- ActionMultiMap actions) {
- if (resolveCache.hasResolve(action)) {
- actions.put(action.getMimeType(), action, front);
- return true;
- }
- return false;
- }
-
- /**
- * Converts a list of Action into a list of Entry
- * @param actions The list of Action to convert
- * @return The converted list of Entry
- */
- private List<Entry> actionsToEntries(List<Action> actions) {
- List<Entry> entries = new ArrayList<>();
- for (Action action : actions) {
- final String header = action.getBody() == null ? null : action.getBody().toString();
- final String footer = action.getBody() == null ? null : action.getBody().toString();
- String body = null;
- Drawable icon = null;
- switch (action.getMimeType()) {
- case Phone.CONTENT_ITEM_TYPE:
- icon = getResources().getDrawable(R.drawable.ic_phone_24dp);
- break;
- case Email.CONTENT_ITEM_TYPE:
- icon = getResources().getDrawable(R.drawable.ic_email_24dp);
- break;
- case StructuredPostal.CONTENT_ITEM_TYPE:
- icon = getResources().getDrawable(R.drawable.ic_place_24dp);
- break;
- default:
- icon = ResolveCache.getInstance(this).getIcon(action);
- }
- entries.add(new Entry(icon, header, body, footer, action.getIntent(),
- /* isEditable= */ false));
-
- // Add SMS in addition to phone calls
- if (action.getMimeType().equals(Phone.CONTENT_ITEM_TYPE)) {
- entries.add(new Entry(getResources().getDrawable(R.drawable.ic_message_24dp),
- getResources().getString(R.string.send_message), null, header,
- action.getAlternateIntent(), /* isEditable = */ false));
- }
- }
- return entries;
- }
-
private List<Entry> contactInteractionsToEntries(List<ContactInteraction> interactions) {
- List<Entry> entries = new ArrayList<>();
+ final List<Entry> entries = new ArrayList<>();
for (ContactInteraction interaction : interactions) {
entries.add(new Entry(interaction.getIcon(this),
interaction.getViewHeader(this),
@@ -963,7 +1209,7 @@
return entries;
}
- private LoaderCallbacks<Contact> mLoaderContactCallbacks =
+ private final LoaderCallbacks<Contact> mLoaderContactCallbacks =
new LoaderCallbacks<Contact>() {
@Override
public void onLoaderReset(Loader<Contact> loader) {
@@ -1029,7 +1275,7 @@
overridePendingTransition(0, 0);
}
- private LoaderCallbacks<List<ContactInteraction>> mLoaderInteractionsCallbacks =
+ private final LoaderCallbacks<List<ContactInteraction>> mLoaderInteractionsCallbacks =
new LoaderCallbacks<List<ContactInteraction>>() {
@Override
@@ -1091,7 +1337,7 @@
}
private void bindRecentData() {
- List<ContactInteraction> allInteractions = new ArrayList<>();
+ final List<ContactInteraction> allInteractions = new ArrayList<>();
for (List<ContactInteraction> loaderInteractions : mRecentLoaderResults.values()) {
allInteractions.addAll(loaderInteractions);
}
@@ -1110,6 +1356,15 @@
/* isExpanded = */ false, mExpandingEntryCardViewListener);
mRecentCard.setVisibility(View.VISIBLE);
}
+
+ // About card is initialized along with the contact card, but since it appears after
+ // the recent card in the UI, we hold off until making it visible until the recent card
+ // is also ready to avoid stuttering.
+ if (mAboutCard.shouldShow()) {
+ mAboutCard.setVisibility(View.VISIBLE);
+ } else {
+ mAboutCard.setVisibility(View.GONE);
+ }
}
@Override
@@ -1153,7 +1408,7 @@
!isStarred);
// Now perform the real save
- Intent intent = ContactSaveService.createSetStarredIntent(
+ final Intent intent = ContactSaveService.createSetStarredIntent(
QuickContactActivity.this, mLookupUri, !isStarred);
startService(intent);
}
@@ -1197,7 +1452,7 @@
try {
this.startActivity(chooseIntent);
- } catch (ActivityNotFoundException ex) {
+ } catch (final ActivityNotFoundException ex) {
Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
}
}
@@ -1229,7 +1484,7 @@
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
+ final MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.quickcontact, menu);
return true;
}
diff --git a/src/com/android/contacts/quickcontact/ResolveCache.java b/src/com/android/contacts/quickcontact/ResolveCache.java
index af5d160..2df867a 100644
--- a/src/com/android/contacts/quickcontact/ResolveCache.java
+++ b/src/com/android/contacts/quickcontact/ResolveCache.java
@@ -117,16 +117,14 @@
}
/**
- * Get the {@link Entry} best associated with the given {@link Action},
+ * Get the {@link Entry} best associated with the given mimetype and intent,
* or create and populate a new one if it doesn't exist.
*/
- protected Entry getEntry(Action action) {
- final String mimeType = action.getMimeType();
+ protected Entry getEntry(String mimeType, Intent intent) {
Entry entry = mCache.get(mimeType);
if (entry != null) return entry;
entry = new Entry();
- Intent intent = action.getIntent();
if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)
&& !PhoneCapabilityTester.isSipPhone(mContext)) {
intent = null;
@@ -196,28 +194,10 @@
/**
* Check {@link PackageManager} to see if any apps offer to handle the
- * given {@link Action}.
+ * given {@link Intent}.
*/
- public boolean hasResolve(Action action) {
- return getEntry(action).bestResolve != null;
- }
-
- /**
- * Find the best description for the given {@link Action}, usually used
- * for accessibility purposes.
- */
- public CharSequence getDescription(Action action, String name) {
- final ResolveInfo info = getEntry(action).bestResolve;
- final CharSequence infoStr = info != null ? info.loadLabel(mPackageManager) : null;
- CharSequence actionDesc = action.getSubtitle();
- CharSequence strs[];
- if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(actionDesc)) {
- strs = new CharSequence[]{infoStr, name, actionDesc};
- } else {
- strs = new CharSequence[]{infoStr, action.getBody()};
- }
- CharSequence desc = TextUtils.join(" ", strs);
- return !TextUtils.isEmpty(desc) ? desc : null;
+ public boolean hasResolve(String mimeType, Intent intent) {
+ return getEntry(mimeType, intent).bestResolve != null;
}
/**
@@ -225,8 +205,8 @@
* based on the {@link ResolveInfo} found through a
* {@link PackageManager} query.
*/
- public Drawable getIcon(Action action) {
- return getEntry(action).icon;
+ public Drawable getIcon(String mimeType, Intent intent) {
+ return getEntry(mimeType, intent).icon;
}
public void clear() {
diff --git a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
deleted file mode 100644
index 9c52161..0000000
--- a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2009 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.detail;
-
-import android.content.ContentValues;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.contacts.detail.ContactDetailFragment.DetailViewEntry;
-import com.android.contacts.common.model.dataitem.DataItem;
-import com.android.contacts.common.model.dataitem.EmailDataItem;
-import com.android.contacts.common.model.dataitem.ImDataItem;
-
-/**
- * Tests for {@link ContactDetailFragment}.
- */
-@SmallTest
-public class ContactDetailFragmentTests extends AndroidTestCase {
- private static final String TEST_ADDRESS = "user@example.org";
- private static final String TEST_PROTOCOL = "prot%col";
-
- public void testImIntent() throws Exception {
- // Test GTalk XMPP URI. No chat capabilities provided
- final ContentValues values = new ContentValues();
- values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
- values.put(Im.TYPE, Im.TYPE_HOME);
- values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
- values.put(Im.DATA, TEST_ADDRESS);
- ImDataItem im = (ImDataItem) DataItem.createFrom(values);
-
- DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
- ContactDetailFragment.buildImActions(mContext, entry, im);
- assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
-
- assertNull(entry.secondaryIntent);
- }
-
- public void testImIntentWithAudio() throws Exception {
- // Test GTalk XMPP URI. Audio chat capabilities provided
- final ContentValues values = new ContentValues();
- values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
- values.put(Im.TYPE, Im.TYPE_HOME);
- values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
- values.put(Im.DATA, TEST_ADDRESS);
- values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO);
- ImDataItem im = (ImDataItem) DataItem.createFrom(values);
-
- DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
- ContactDetailFragment.buildImActions(mContext, entry, im);
- assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
-
- assertEquals(Intent.ACTION_SENDTO, entry.secondaryIntent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?call", entry.secondaryIntent.getData().toString());
- }
-
- public void testImIntentWithVideo() throws Exception {
- // Test GTalk XMPP URI. Video chat capabilities provided
- final ContentValues values = new ContentValues();
- values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
- values.put(Im.TYPE, Im.TYPE_HOME);
- values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
- values.put(Im.DATA, TEST_ADDRESS);
- values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
- Im.CAPABILITY_HAS_VOICE);
- ImDataItem im = (ImDataItem) DataItem.createFrom(values);
-
- DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
- ContactDetailFragment.buildImActions(mContext, entry, im);
- assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
-
- assertEquals(Intent.ACTION_SENDTO, entry.secondaryIntent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?call", entry.secondaryIntent.getData().toString());
- }
-
- public void testImIntentCustom() throws Exception {
- // Custom IM types have encoded authority. We send the imto Intent here, because
- // legacy third party apps might not accept xmpp yet
- final ContentValues values = new ContentValues();
- values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
- values.put(Im.TYPE, Im.TYPE_HOME);
- values.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
- values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
- values.put(Im.DATA, TEST_ADDRESS);
- ImDataItem im = (ImDataItem) DataItem.createFrom(values);
-
- DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
- final Intent imIntent =
- ContactDetailFragment.getCustomIMIntent(im, Im.PROTOCOL_CUSTOM);
- assertEquals(Intent.ACTION_SENDTO, imIntent.getAction());
-
- final Uri data = imIntent.getData();
- assertEquals("imto", data.getScheme());
- assertEquals(TEST_PROTOCOL, data.getAuthority());
- assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
-
- assertNull(entry.secondaryIntent);
- }
-
- public void testImEmailIntent() throws Exception {
- // Email addresses are treated as Google Talk entries
- // This test only tests the VIDEO+CAMERA case. The other cases have been addressed by the
- // Im tests
- final ContentValues values = new ContentValues();
- values.put(Email.MIMETYPE, Email.CONTENT_ITEM_TYPE);
- values.put(Email.TYPE, Email.TYPE_HOME);
- values.put(Email.DATA, TEST_ADDRESS);
- values.put(Email.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
- Im.CAPABILITY_HAS_VOICE);
- ImDataItem im = ImDataItem.createFromEmail(
- (EmailDataItem) DataItem.createFrom(values));
-
- DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
- ContactDetailFragment.buildImActions(mContext, entry, im);
- assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
-
- assertEquals(Intent.ACTION_SENDTO, entry.secondaryIntent.getAction());
- assertEquals("xmpp:" + TEST_ADDRESS + "?call", entry.secondaryIntent.getData().toString());
- }
-}