Merge "Show avatars in directory search"
diff --git a/res/layout/quickcontact_photo_container.xml b/res/layout/quickcontact_photo_container.xml
index e970934..3b46ef9 100644
--- a/res/layout/quickcontact_photo_container.xml
+++ b/res/layout/quickcontact_photo_container.xml
@@ -26,11 +26,6 @@
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_alignParentTop="true"
- android:background="#4CFFFFFF" />
- <View
android:id="@+id/photo_text_bar"
android:layout_width="0dip"
android:layout_height="42dip"
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index 4b297d9..98abfbc 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -319,15 +319,23 @@
// Apply a limit of 1 result to the query because we only need to
// determine whether or not at least one other contact has the same
// name. We don't need to find ALL other contacts with the same name.
- Builder builder = Contacts.CONTENT_URI.buildUpon();
+ final Builder builder = Contacts.CONTENT_URI.buildUpon();
builder.appendQueryParameter("limit", String.valueOf(1));
- Uri uri = builder.build();
+ final Uri uri = builder.build();
+ final String displayNameSelection;
+ final String[] selectionArgs;
+ if (TextUtils.isEmpty(contactDisplayName)) {
+ displayNameSelection = Contacts.DISPLAY_NAME_PRIMARY + " IS NULL";
+ selectionArgs = new String[] { String.valueOf(mContactId) };
+ } else {
+ displayNameSelection = Contacts.DISPLAY_NAME_PRIMARY + " = ?";
+ selectionArgs = new String[] { contactDisplayName, String.valueOf(mContactId) };
+ }
mQueryHandler.startQuery(TOKEN_DISAMBIGUATION_QUERY, null, uri,
new String[] { Contacts._ID } /* unused projection but a valid one was needed */,
- Contacts.DISPLAY_NAME_PRIMARY + " = ? and " + Contacts.PHOTO_ID + " is null and "
- + Contacts._ID + " <> ?",
- new String[] { contactDisplayName, String.valueOf(mContactId) }, null);
+ displayNameSelection + " AND " + Contacts.PHOTO_ID + " IS NULL AND "
+ + Contacts._ID + " <> ?", selectionArgs, null);
}
/**
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index 2a1ec5e..c9e713b 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -286,7 +286,17 @@
}
// Field changes are saved directly
+ saveValue(column, value);
+
+ // Notify listener if applicable
+ notifyEditorListener();
+ }
+
+ protected void saveValue(String column, String value) {
mEntry.put(column, value);
+ }
+
+ protected void notifyEditorListener() {
if (mListener != null) {
mListener.onRequest(EditorListener.FIELD_CHANGED);
}
diff --git a/src/com/android/contacts/editor/StructuredNameEditorView.java b/src/com/android/contacts/editor/StructuredNameEditorView.java
index 6911628..af1b3cf 100644
--- a/src/com/android/contacts/editor/StructuredNameEditorView.java
+++ b/src/com/android/contacts/editor/StructuredNameEditorView.java
@@ -77,11 +77,12 @@
if (!isFieldChanged(column, value)) {
return;
}
- super.onFieldChanged(column, value);
+ // First save the new value for the column.
+ saveValue(column, value);
mChanged = true;
- // Make sure the display name and the structured name are synced
+ // Next make sure the display name and the structured name are synced
if (hasShortAndLongForms()) {
if (areOptionalFieldsVisible()) {
rebuildFullName(getValues());
@@ -89,6 +90,10 @@
rebuildStructuredName(getValues());
}
}
+
+ // Then notify the listener, which will rely on the display and structured names to be
+ // synced (in order to provide aggregate suggestions).
+ notifyEditorListener();
}
@Override
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 22ea884..3cc54f1 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -25,7 +25,6 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
@@ -270,6 +269,7 @@
* {@link Comparator} to sort by {@link DataKind#weight}.
*/
private static Comparator<DataKind> sWeightComparator = new Comparator<DataKind>() {
+ @Override
public int compare(DataKind object1, DataKind object2) {
return object1.weight - object2.weight;
}
@@ -460,13 +460,12 @@
}
/**
- * Generic method of inflating a given {@link Cursor} into a user-readable
+ * Generic method of inflating a given {@link ContentValues} into a user-readable
* {@link CharSequence}. For example, an inflater could combine the multiple
* columns of {@link StructuredPostal} together using a string resource
* before presenting to the user.
*/
public interface StringInflater {
- public CharSequence inflateUsing(Context context, Cursor cursor);
public CharSequence inflateUsing(Context context, ContentValues values);
}
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index 4d82ece..ce74148 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -17,18 +17,13 @@
package com.android.contacts.model;
import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
-import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
@@ -48,6 +43,9 @@
import android.util.Log;
import android.view.inputmethod.EditorInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.util.List;
import java.util.Locale;
@@ -466,25 +464,7 @@
mColumnName = columnName;
}
- public CharSequence inflateUsing(Context context, Cursor cursor) {
- final int index = mColumnName != null ? cursor.getColumnIndex(mColumnName) : -1;
- final boolean validString = mStringRes > 0;
- final boolean validColumn = index != -1;
-
- final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
- final CharSequence columnValue = validColumn ? cursor.getString(index) : null;
-
- if (validString && validColumn) {
- return String.format(stringValue.toString(), columnValue);
- } else if (validString) {
- return stringValue;
- } else if (validColumn) {
- return columnValue;
- } else {
- return null;
- }
- }
-
+ @Override
public CharSequence inflateUsing(Context context, ContentValues values) {
final boolean validColumn = values.containsKey(mColumnName);
final boolean validString = mStringRes > 0;
@@ -541,12 +521,7 @@
}
}
- public CharSequence inflateUsing(Context context, Cursor cursor) {
- final Integer type = cursor.getInt(cursor.getColumnIndex(getTypeColumn()));
- final String label = cursor.getString(cursor.getColumnIndex(getLabelColumn()));
- return getTypeLabel(context.getResources(), type, label);
- }
-
+ @Override
public CharSequence inflateUsing(Context context, ContentValues values) {
final Integer type = values.getAsInteger(getTypeColumn());
final String label = values.getAsString(getLabelColumn());
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 415fa18..7c91ba8 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -21,14 +21,14 @@
import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.model.DataKind;
import com.android.contacts.util.Constants;
-import com.android.contacts.util.StructuredPostalUtils;
import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.util.StructuredPostalUtils;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.net.WebAddress;
@@ -67,7 +67,8 @@
/**
* Create an action from common {@link Data} elements.
*/
- public DataAction(Context context, String mimeType, DataKind kind, long dataId, Cursor cursor) {
+ public DataAction(Context context, String mimeType, DataKind kind, long dataId,
+ ContentValues entryValues) {
mContext = context;
mKind = kind;
mMimeType = mimeType;
@@ -75,9 +76,8 @@
// Determine type for subtitle
mSubtitle = "";
if (kind.typeColumn != null) {
- final int typeColumnIndex = cursor.getColumnIndex(kind.typeColumn);
- if (typeColumnIndex != -1) {
- final int typeValue = cursor.getInt(typeColumnIndex);
+ if (entryValues.containsKey(kind.typeColumn)) {
+ final int typeValue = entryValues.getAsInteger(kind.typeColumn);
// get type string
for (EditType type : kind.typeList) {
@@ -87,8 +87,7 @@
mSubtitle = context.getString(type.labelRes);
} else {
// Custom type. Read it from the database
- mSubtitle = cursor.getString(cursor.getColumnIndexOrThrow(
- type.customColumn));
+ mSubtitle = entryValues.getAsString(type.customColumn);
}
break;
}
@@ -96,12 +95,11 @@
}
}
- if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
- mIsPrimary = true;
- }
+ final Integer superPrimary = entryValues.getAsInteger(Data.IS_SUPER_PRIMARY);
+ mIsPrimary = superPrimary != null && superPrimary != 0;
if (mKind.actionBody != null) {
- mBody = mKind.actionBody.inflateUsing(context, cursor);
+ mBody = mKind.actionBody.inflateUsing(context, entryValues);
}
mDataId = dataId;
@@ -113,7 +111,7 @@
// Handle well-known MIME-types with special care
if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
if (PhoneCapabilityTester.isPhone(mContext)) {
- final String number = getAsString(cursor, Phone.NUMBER);
+ final String number = entryValues.getAsString(Phone.NUMBER);
if (!TextUtils.isEmpty(number)) {
final Intent phoneIntent = hasPhone ? ContactsUtils.getCallIntent(number)
@@ -136,7 +134,7 @@
}
} else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
if (PhoneCapabilityTester.isSipPhone(mContext)) {
- final String address = getAsString(cursor, SipAddress.SIP_ADDRESS);
+ final String address = entryValues.getAsString(SipAddress.SIP_ADDRESS);
if (!TextUtils.isEmpty(address)) {
final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
mIntent = ContactsUtils.getCallIntent(callUri);
@@ -149,14 +147,14 @@
}
}
} else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String address = getAsString(cursor, Email.DATA);
+ final String address = entryValues.getAsString(Email.DATA);
if (!TextUtils.isEmpty(address)) {
final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null);
mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
}
} else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String url = getAsString(cursor, Website.URL);
+ final String url = entryValues.getAsString(Website.URL);
if (!TextUtils.isEmpty(url)) {
WebAddress webAddress = new WebAddress(url);
mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
@@ -164,10 +162,10 @@
} else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
- getAsString(cursor, Data.MIMETYPE));
- if (isEmail || isProtocolValid(cursor)) {
+ entryValues.getAsString(Data.MIMETYPE));
+ if (isEmail || isProtocolValid(entryValues)) {
final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK :
- getAsInt(cursor, Im.PROTOCOL);
+ entryValues.getAsInteger(Im.PROTOCOL);
if (isEmail) {
// Use Google Talk string when using Email, and clear data
@@ -176,9 +174,8 @@
mDataUri = null;
}
- String host = getAsString(cursor, Im.CUSTOM_PROTOCOL);
- String data = getAsString(cursor,
- isEmail ? Email.DATA : Im.DATA);
+ String host = entryValues.getAsString(Im.CUSTOM_PROTOCOL);
+ String data = entryValues.getAsString(isEmail ? Email.DATA : Im.DATA);
if (protocol != Im.PROTOCOL_CUSTOM) {
// Try bringing in a well-known host for specific protocols
host = ContactsUtils.lookupProviderNameFromId(protocol);
@@ -192,7 +189,8 @@
// If the address is also available for a video chat, we'll show the capability
// as a secondary action.
- final int chatCapability = getAsInt(cursor, Data.CHAT_CAPABILITY);
+ final Integer chatCapabilityObj = entryValues.getAsInteger(Im.CHAT_CAPABILITY);
+ final int chatCapability = chatCapabilityObj == null ? 0 : chatCapabilityObj;
final boolean isVideoChatCapable =
(chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0;
final boolean isAudioChatCapable =
@@ -211,7 +209,8 @@
}
}
} else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String postalAddress = getAsString(cursor, StructuredPostal.FORMATTED_ADDRESS);
+ final String postalAddress =
+ entryValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
if (!TextUtils.isEmpty(postalAddress)) {
mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
}
@@ -227,25 +226,13 @@
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
- /** Read {@link String} from the given {@link Cursor}. */
- private static String getAsString(Cursor cursor, String columnName) {
- final int index = cursor.getColumnIndex(columnName);
- return cursor.getString(index);
- }
-
- /** Read {@link Integer} from the given {@link Cursor}. */
- private static int getAsInt(Cursor cursor, String columnName) {
- final int index = cursor.getColumnIndex(columnName);
- return cursor.getInt(index);
- }
-
- private boolean isProtocolValid(Cursor cursor) {
- final int columnIndex = cursor.getColumnIndex(Im.PROTOCOL);
- if (cursor.isNull(columnIndex)) {
+ private boolean isProtocolValid(ContentValues entryValues) {
+ final String protocol = entryValues.getAsString(Im.PROTOCOL);
+ if (protocol == null) {
return false;
}
try {
- Integer.valueOf(cursor.getString(columnIndex));
+ Integer.valueOf(protocol);
} catch (NumberFormatException e) {
return false;
}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 2c62fe4..7a209b5 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -17,44 +17,41 @@
package com.android.contacts.quickcontact;
import com.android.contacts.Collapser;
+import com.android.contacts.ContactLoader;
import com.android.contacts.ContactPhotoManager;
import com.android.contacts.R;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.DataKind;
-import com.android.contacts.util.DataStatus;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.android.contacts.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
+import android.app.LoaderManager.LoaderCallbacks;
import android.content.ActivityNotFoundException;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
+import android.content.Entity;
+import android.content.Entity.NamedContentValues;
import android.content.Intent;
+import android.content.Loader;
import android.content.pm.PackageManager;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
import android.support.v13.app.FragmentPagerAdapter;
@@ -74,7 +71,6 @@
import android.widget.TextView;
import android.widget.Toast;
-import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -96,8 +92,6 @@
@SuppressWarnings("deprecation")
private static final String LEGACY_AUTHORITY = android.provider.Contacts.AUTHORITY;
- private NotifyingAsyncQueryHandler mHandler;
-
private Uri mLookupUri;
private String[] mExcludeMimes;
private List<String> mSortedActionMimeTypes = Lists.newArrayList();
@@ -147,8 +141,8 @@
private static final List<String> TRAILING_MIMETYPES = Lists.newArrayList(
StructuredPostal.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE);
- /** Id for the background handler that loads the data */
- private static final int HANDLER_ID_DATA = 1;
+ /** Id for the background loader */
+ private static final int LOADER_ID = 0;
@Override
protected void onCreate(Bundle icicle) {
@@ -190,8 +184,6 @@
mListPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
mListPager.setOnPageChangeListener(new PageChangeListener());
- mHandler = new NotifyingAsyncQueryHandler(this, mQueryListener);
-
show();
}
@@ -225,16 +217,7 @@
mPhotoContainer = findViewById(R.id.photo_container);
setHeaderNameText(R.id.name, R.string.missing_name);
- // Start background query for data, but only select photo rows when they
- // directly match the super-primary PHOTO_ID.
- final Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
- mHandler.cancelOperation(HANDLER_ID_DATA);
-
- // Select all data items of the contact (except for photos, where we only select the display
- // photo)
- mHandler.startQuery(HANDLER_ID_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
- + "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
- + ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
+ getLoaderManager().initLoader(LOADER_ID, null, mLoaderCallbacks);
}
private boolean handleOutsideTouch() {
@@ -248,7 +231,7 @@
private void hide(boolean withAnimation) {
// cancel any pending queries
- mHandler.cancelOperation(HANDLER_ID_DATA);
+ getLoaderManager().destroyLoader(LOADER_ID);
if (withAnimation) {
mFloatingLayout.hideChild(new Runnable() {
@@ -268,47 +251,6 @@
hide(true);
}
- private final AsyncQueryListener mQueryListener = new AsyncQueryListener() {
- @Override
- public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
- try {
- if (isFinishing()) {
- hide(false);
- return;
- } else if (cursor == null || cursor.getCount() == 0) {
- Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
- Toast.LENGTH_LONG).show();
- hide(false);
- return;
- }
-
- bindData(cursor);
-
- if (TRACE_LAUNCH) {
- android.os.Debug.stopMethodTracing();
- }
-
- // Data bound and ready, pull curtain to show. Put this on the Handler to ensure
- // that the layout passes are completed
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mFloatingLayout.showChild(new Runnable() {
- @Override
- public void run() {
- mHasFinishedAnimatingIn = true;
- }
- });
- }
- });
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
- };
-
/** Assign this string to the view if it is not empty. */
private void setHeaderNameText(int id, int resId) {
setHeaderNameText(id, getText(resId));
@@ -325,35 +267,6 @@
}
/**
- * 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}), 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) {
- ((TextView)view).setText(value);
- view.setVisibility(TextUtils.isEmpty(value) ? View.GONE : View.VISIBLE);
- }
- }
-
- /** Assign this image to the view, if found in {@link #mPhotoContainer}. */
- private void setHeaderImage(int id, Drawable drawable) {
- final View view = mPhotoContainer.findViewById(id);
- if (view instanceof ImageView) {
- ((ImageView)view).setImageDrawable(drawable);
- view.setVisibility(drawable == null ? View.GONE : View.VISIBLE);
- }
- }
-
- /**
* Check if the given MIME-type appears in the list of excluded MIME-types
* that the most-recent caller requested.
*/
@@ -368,9 +281,9 @@
}
/**
- * Handle the result from the {@link #TOKEN_DATA} query.
+ * Handle the result from the ContactLoader
*/
- private void bindData(Cursor cursor) {
+ private void bindData(ContactLoader.Result data) {
final ResolveCache cache = ResolveCache.getInstance(this);
final Context context = this;
@@ -379,90 +292,62 @@
mDefaultsMap.clear();
- final DataStatus status = new DataStatus();
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
context.getApplicationContext());
final ImageView photoView = (ImageView) mPhotoContainer.findViewById(R.id.photo);
+ final byte[] photo = data.getPhotoBinaryData();
+ if (photo != null) {
+ photoView.setImageBitmap(BitmapFactory.decodeByteArray(photo, 0, photo.length));
+ } else {
+ photoView.setImageResource(
+ ContactPhotoManager.getDefaultAvatarResId(true, false));
+ }
- Bitmap photoBitmap = null;
- while (cursor.moveToNext()) {
- // Handle any social status updates from this row
- status.possibleUpdate(cursor);
+ for (Entity entity : data.getEntities()) {
+ final ContentValues entityValues = entity.getEntityValues();
+ final String accountType = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
+ for (NamedContentValues subValue : entity.getSubValues()) {
+ final ContentValues entryValues = subValue.values;
+ final String mimeType = entryValues.getAsString(Data.MIMETYPE);
- final String mimeType = cursor.getString(DataQuery.MIMETYPE);
+ // Skip this data item if MIME-type excluded
+ if (isMimeExcluded(mimeType)) continue;
- // Skip this data item if MIME-type excluded
- if (isMimeExcluded(mimeType)) continue;
+ final long dataId = entryValues.getAsLong(Data._ID);
+ final Integer primary = entryValues.getAsInteger(Data.IS_PRIMARY);
+ final boolean isPrimary = primary != null && primary != 0;
+ final Integer superPrimary = entryValues.getAsInteger(Data.IS_SUPER_PRIMARY);
+ final boolean isSuperPrimary = superPrimary != null && superPrimary != 0;
- final long dataId = cursor.getLong(DataQuery._ID);
- final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
- final String dataSet = cursor.getString(DataQuery.DATA_SET);
- final boolean isPrimary = cursor.getInt(DataQuery.IS_PRIMARY) != 0;
- final boolean isSuperPrimary = cursor.getInt(DataQuery.IS_SUPER_PRIMARY) != 0;
+ final DataKind kind =
+ accountTypes.getKindOrFallback(accountType, dataSet, mimeType);
- // Handle photos included as data row
- if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final int displayPhotoColumnIndex = cursor.getColumnIndex(Photo.PHOTO_FILE_ID);
- final boolean hasDisplayPhoto = !cursor.isNull(displayPhotoColumnIndex);
- if (hasDisplayPhoto) {
- final long displayPhotoId = cursor.getLong(displayPhotoColumnIndex);
- final Uri displayPhotoUri = ContentUris.withAppendedId(
- DisplayPhoto.CONTENT_URI, displayPhotoId);
- // Fetch and JPEG uncompress on the background thread
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- try {
- AssetFileDescriptor fd = getContentResolver()
- .openAssetFileDescriptor(displayPhotoUri, "r");
- return BitmapFactory.decodeStream(fd.createInputStream());
- } catch (IOException e) {
- Log.e(TAG, "Error getting display photo. Ignoring, as we already " +
- "have the thumbnail", e);
- return null;
- }
+ if (kind != 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(context, mimeType, kind, dataId,
+ entryValues);
+ final boolean wasAdded = considerAdd(action, cache);
+ if (wasAdded) {
+ // Remember the default
+ if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
+ mDefaultsMap.put(mimeType, action);
}
-
- @Override
- protected void onPostExecute(Bitmap result) {
- if (result == null) return;
- photoView.setImageBitmap(result);
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
- final int photoColumnIndex = cursor.getColumnIndex(Photo.PHOTO);
- final byte[] photoBlob = cursor.getBlob(photoColumnIndex);
- if (photoBlob != null) {
- photoBitmap = BitmapFactory.decodeByteArray(photoBlob, 0, photoBlob.length);
- }
- continue;
- }
-
- final DataKind kind = accountTypes.getKindOrFallback(accountType, dataSet, mimeType);
-
- if (kind != 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(context, mimeType, kind, dataId, cursor);
- final boolean wasAdded = considerAdd(action, cache);
- 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 boolean hasPresence = !cursor.isNull(DataQuery.PRESENCE);
- if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
- Im.CONTENT_ITEM_TYPE);
- if (imKind != null) {
- final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE, imKind,
- dataId, cursor);
- considerAdd(action, cache);
+ // Handle Email rows with presence data as Im entry
+ final boolean hasPresence = data.getStatuses().containsKey(dataId);
+ if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
+ Im.CONTENT_ITEM_TYPE);
+ if (imKind != null) {
+ final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE,
+ imKind, dataId, entryValues);
+ considerAdd(action, cache);
+ }
}
}
}
@@ -472,20 +357,7 @@
Collapser.collapseList(actionChildren);
}
- if (cursor.moveToLast()) {
- // Read contact name from last data row
- final String name = cursor.getString(DataQuery.DISPLAY_NAME);
- setHeaderNameText(R.id.name, name);
- }
-
- if (photoView != null) {
- // Place photo when discovered in data, otherwise show generic avatar
- if (photoBitmap != null) {
- photoView.setImageBitmap(photoBitmap);
- } else {
- photoView.setImageResource(ContactPhotoManager.getDefaultAvatarResId(true, false));
- }
- }
+ setHeaderNameText(R.id.name, data.getDisplayName());
// All the mime-types to add.
final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
@@ -515,6 +387,7 @@
}
// Add buttons for each mimetype
+ mTrack.removeAllViews();
for (String mimeType : mSortedActionMimeTypes) {
final View actionView = inflateAction(mimeType, cache, mTrack);
mTrack.addView(actionView);
@@ -574,6 +447,61 @@
listFragment.setListener(mListFragmentListener);
}
+ private LoaderCallbacks<ContactLoader.Result> mLoaderCallbacks =
+ new LoaderCallbacks<ContactLoader.Result>() {
+ @Override
+ public void onLoaderReset(Loader<ContactLoader.Result> loader) {
+ }
+
+ @Override
+ public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
+ if (isFinishing()) {
+ hide(false);
+ return;
+ }
+ if (data.isError()) {
+ // This shouldn't ever happen, so throw an exception. The {@link ContactLoader}
+ // should log the actual exception.
+ throw new IllegalStateException("Failed to load contact", data.getException());
+ }
+ if (data.isNotFound()) {
+ Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
+ Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
+ Toast.LENGTH_LONG).show();
+ hide(false);
+ return;
+ }
+
+ bindData(data);
+
+ if (TRACE_LAUNCH) {
+ android.os.Debug.stopMethodTracing();
+ }
+
+ // Data bound and ready, pull curtain to show. Put this on the Handler to ensure
+ // that the layout passes are completed
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mFloatingLayout.showChild(new Runnable() {
+ @Override
+ public void run() {
+ mHasFinishedAnimatingIn = true;
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
+ if (mLookupUri == null) {
+ Log.wtf(TAG, "Lookup uri wasn't initialized. Loader was started too early");
+ }
+ return new ContactLoader(getApplicationContext(), mLookupUri);
+ }
+ };
+
/** A type (e.g. Call/Addresses was clicked) */
private final OnClickListener mTypeViewClickListener = new OnClickListener() {
@Override
@@ -653,53 +581,4 @@
new Handler().post(startAppRunnable);
}
};
-
- private interface DataQuery {
- final String[] PROJECTION = new String[] {
- Data._ID,
-
- RawContacts.ACCOUNT_TYPE,
- RawContacts.DATA_SET,
- Contacts.STARRED,
- Contacts.DISPLAY_NAME,
-
- Data.STATUS,
- Data.STATUS_RES_PACKAGE,
- Data.STATUS_ICON,
- Data.STATUS_LABEL,
- Data.STATUS_TIMESTAMP,
- Data.PRESENCE,
- Data.CHAT_CAPABILITY,
-
- Data.RES_PACKAGE,
- Data.MIMETYPE,
- Data.IS_PRIMARY,
- Data.IS_SUPER_PRIMARY,
- Data.RAW_CONTACT_ID,
-
- Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
- Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11,
- Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
- };
-
- final int _ID = 0;
-
- final int ACCOUNT_TYPE = 1;
- final int DATA_SET = 2;
- final int STARRED = 3;
- final int DISPLAY_NAME = 4;
-
- final int STATUS = 5;
- final int STATUS_RES_PACKAGE = 6;
- final int STATUS_ICON = 7;
- final int STATUS_LABEL = 8;
- final int STATUS_TIMESTAMP = 9;
- final int PRESENCE = 10;
- final int CHAT_CAPABILITY = 11;
-
- final int RES_PACKAGE = 12;
- final int MIMETYPE = 13;
- final int IS_PRIMARY = 14;
- final int IS_SUPER_PRIMARY = 15;
- }
}
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 19ebde3..c3cbc10 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -23,7 +23,7 @@
<!-- List modes -->
<item>LIST_DEFAULT</item>
<item>LIST_ALL_CONTACTS_ACTION</item>
- <item>LIST_CONTACTS_WITH_PHONES_ACTION</item>
+ <item>LIST_CONTACTS_WITH_PHONES_ACTION (deprecated)</item>
<item>LIST_STARRED_ACTION</item>
<item>LIST_FREQUENT_ACTION</item>
<item>LIST_STREQUENT_ACTION</item>