am 3c513ed9: Converting CallerInfo to new Contacts API.

Merge commit '3c513ed95cee2e0bcd7208cb7e46307f09c907c9'

* commit '3c513ed95cee2e0bcd7208cb7e46307f09c907c9':
  Converting CallerInfo to new Contacts API.
This commit is contained in:
Dmitri Plotnikov
2009-08-19 16:52:02 -07:00
committed by Android Git Automerger
4 changed files with 132 additions and 84 deletions

View File

@@ -27,8 +27,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.provider.Contacts;
import android.provider.Contacts.People;
import android.provider.ContactsContract.Contacts;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
@@ -39,35 +38,35 @@ import java.io.InputStream;
* Helper class for async access of images.
*/
public class ContactsAsyncHelper extends Handler {
private static final boolean DBG = false;
private static final String LOG_TAG = "ContactsAsyncHelper";
/**
* Interface for a WorkerHandler result return.
*/
public interface OnImageLoadCompleteListener {
/**
* Called when the image load is complete.
*
*
* @param imagePresent true if an image was found
*/
*/
public void onImageLoadComplete(int token, Object cookie, ImageView iView,
boolean imagePresent);
}
// constants
private static final int EVENT_LOAD_IMAGE = 1;
private static final int DEFAULT_TOKEN = -1;
// static objects
private static Handler sThreadHandler;
private static ContactsAsyncHelper sInstance;
static {
sInstance = new ContactsAsyncHelper();
}
private static final class WorkerArgs {
public Context context;
public ImageView view;
@@ -78,12 +77,12 @@ public class ContactsAsyncHelper extends Handler {
public OnImageLoadCompleteListener listener;
public CallerInfo info;
}
/**
* public inner class to help out the ContactsAsyncHelper callers
* with tracking the state of the CallerInfo Queries and image
* public inner class to help out the ContactsAsyncHelper callers
* with tracking the state of the CallerInfo Queries and image
* loading.
*
*
* Logic contained herein is used to remove the race conditions
* that exist as the CallerInfo queries run and mix with the image
* loads, which then mix with the Phone state changes.
@@ -94,11 +93,11 @@ public class ContactsAsyncHelper extends Handler {
public static final int DISPLAY_UNDEFINED = 0;
public static final int DISPLAY_IMAGE = -1;
public static final int DISPLAY_DEFAULT = -2;
// State of the image on the imageview.
private CallerInfo mCurrentCallerInfo;
private int displayMode;
public ImageTracker() {
mCurrentCallerInfo = null;
displayMode = DISPLAY_UNDEFINED;
@@ -107,17 +106,17 @@ public class ContactsAsyncHelper extends Handler {
/**
* Used to see if the requested call / connection has a
* different caller attached to it than the one we currently
* have in the CallCard.
* have in the CallCard.
*/
public boolean isDifferentImageRequest(CallerInfo ci) {
// note, since the connections are around for the lifetime of the
// call, and the CallerInfo-related items as well, we can
// call, and the CallerInfo-related items as well, we can
// definitely use a simple != comparison.
return (mCurrentCallerInfo != ci);
}
public boolean isDifferentImageRequest(Connection connection) {
// if the connection does not exist, see if the
// if the connection does not exist, see if the
// mCurrentCallerInfo is also null to match.
if (connection == null) {
if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null");
@@ -133,57 +132,58 @@ public class ContactsAsyncHelper extends Handler {
}
return runQuery;
}
/**
* Simple setter for the CallerInfo object.
* Simple setter for the CallerInfo object.
*/
public void setPhotoRequest(CallerInfo ci) {
mCurrentCallerInfo = ci;
mCurrentCallerInfo = ci;
}
/**
* Convenience method used to retrieve the URI
* representing the Photo file recorded in the attached
* CallerInfo Object.
* Convenience method used to retrieve the URI
* representing the Photo file recorded in the attached
* CallerInfo Object.
*/
public Uri getPhotoUri() {
if (mCurrentCallerInfo != null) {
return ContentUris.withAppendedId(People.CONTENT_URI,
return ContentUris.withAppendedId(Contacts.CONTENT_URI,
mCurrentCallerInfo.person_id);
}
return null;
return null;
}
/**
* Simple setter for the Photo state.
* Simple setter for the Photo state.
*/
public void setPhotoState(int state) {
displayMode = state;
}
/**
* Simple getter for the Photo state.
* Simple getter for the Photo state.
*/
public int getPhotoState() {
return displayMode;
}
}
/**
* Thread worker class that handles the task of opening the stream and loading
* Thread worker class that handles the task of opening the stream and loading
* the images.
*/
private class WorkerHandler extends Handler {
public WorkerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
WorkerArgs args = (WorkerArgs) msg.obj;
switch (msg.arg1) {
case EVENT_LOAD_IMAGE:
InputStream inputStream = Contacts.People.openContactPhotoInputStream(
InputStream inputStream = Contacts.openContactPhotoInputStream(
args.context.getContentResolver(), args.uri);
if (inputStream != null) {
args.result = Drawable.createFromStream(inputStream, args.uri.toString());
@@ -192,22 +192,22 @@ public class ContactsAsyncHelper extends Handler {
" token: " + msg.what + " image URI: " + args.uri);
} else {
args.result = null;
if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
" token: " + msg.what + " image URI: " + args.uri +
if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
" token: " + msg.what + " image URI: " + args.uri +
", using default image.");
}
break;
default:
}
// send the reply to the enclosing class.
// send the reply to the enclosing class.
Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what);
reply.arg1 = msg.arg1;
reply.obj = msg.obj;
reply.sendToTarget();
}
}
/**
* Private constructor for static class
*/
@@ -216,14 +216,14 @@ public class ContactsAsyncHelper extends Handler {
thread.start();
sThreadHandler = new WorkerHandler(thread.getLooper());
}
/**
* Convenience method for calls that do not want to deal with listeners and tokens.
*/
public static final void updateImageViewWithContactPhotoAsync(Context context,
public static final void updateImageViewWithContactPhotoAsync(Context context,
ImageView imageView, Uri person, int placeholderImageResource) {
// Added additional Cookie field in the callee.
updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
imageView, person, placeholderImageResource);
}
@@ -231,24 +231,24 @@ public class ContactsAsyncHelper extends Handler {
* Convenience method for calls that do not want to deal with listeners and tokens, but have
* a CallerInfo object to cache the image to.
*/
public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
ImageView imageView, Uri person, int placeholderImageResource) {
// Added additional Cookie field in the callee.
updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
imageView, person, placeholderImageResource);
}
/**
* Start an image load, attach the result to the specified CallerInfo object.
* Note, when the query is started, we make the ImageView INVISIBLE if the
* placeholderImageResource value is -1. When we're given a valid (!= -1)
* placeholderImageResource value, we make sure the image is visible.
*/
public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
OnImageLoadCompleteListener listener, Object cookie, Context context,
public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
OnImageLoadCompleteListener listener, Object cookie, Context context,
ImageView imageView, Uri person, int placeholderImageResource) {
// in case the source caller info is null, the URI will be null as well.
// just update using the placeholder image in this case.
if (person == null) {
@@ -257,10 +257,10 @@ public class ContactsAsyncHelper extends Handler {
imageView.setImageResource(placeholderImageResource);
return;
}
// Added additional Cookie field in the callee to handle arguments
// sent to the callback function.
// setup arguments
WorkerArgs args = new WorkerArgs();
args.cookie = cookie;
@@ -270,15 +270,15 @@ public class ContactsAsyncHelper extends Handler {
args.defaultResource = placeholderImageResource;
args.listener = listener;
args.info = info;
// setup message arguments
Message msg = sThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_LOAD_IMAGE;
msg.obj = args;
if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
", displaying default image for now.");
// set the default image first, when the query is complete, we will
// replace the image with the correct one.
if (placeholderImageResource != -1) {
@@ -287,11 +287,11 @@ public class ContactsAsyncHelper extends Handler {
} else {
imageView.setVisibility(View.INVISIBLE);
}
// notify the thread to begin working
sThreadHandler.sendMessage(msg);
}
/**
* Called when loading is done.
*/
@@ -316,21 +316,21 @@ public class ContactsAsyncHelper extends Handler {
args.view.setVisibility(View.VISIBLE);
args.view.setImageResource(args.defaultResource);
}
// Note that the data is cached.
if (args.info != null) {
args.info.isCachedPhotoCurrent = true;
}
// notify the listener if it is there.
if (args.listener != null) {
if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
" image: " + args.uri + " completed");
args.listener.onImageLoadComplete(msg.what, args.cookie, args.view,
imagePresent);
}
break;
default:
default:
}
}
}

View File

@@ -19,15 +19,21 @@ package android.provider;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.text.TextUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* The contract between the contacts provider and applications. Contains definitions
* for the supported URIs and columns.
@@ -299,6 +305,55 @@ public final class ContactsContract {
*/
public static final String CONTENT_DIRECTORY = "suggestions";
}
/**
* Returns a URI that can be used to retrieve the contact's default photo.
*
* @param contactUri the contact whose photo should be used
*/
public static Uri getPhotoUri(ContentResolver cr, Uri contactUri) {
long photoId = -1;
Cursor cursor = cr.query(contactUri, new String[]{Contacts.PHOTO_ID}, null, null, null);
try {
if (!cursor.moveToNext()) {
return null;
}
if (cursor.isNull(0)) {
return null;
}
photoId = cursor.getLong(0);
} finally {
cursor.close();
}
return ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, photoId);
}
/**
* Opens an InputStream for the person's default photo and returns the
* photo as a Bitmap stream.
*
* @param contactUri the contact whose photo should be used
*/
public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri) {
Uri photoUri = getPhotoUri(cr, contactUri);
Cursor cursor = cr.query(photoUri,
new String[]{ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);
try {
if (!cursor.moveToNext()) {
return null;
}
byte[] data = cursor.getBlob(0);
if (data == null) {
return null;
}
return new ByteArrayInputStream(data);
} finally {
cursor.close();
}
}
}
private interface RawContactsColumns {

View File

@@ -20,9 +20,8 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.Contacts;
import android.provider.Contacts.People;
import android.provider.Contacts.Phones;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.TextUtils;
import android.telephony.TelephonyManager;
import android.telephony.PhoneNumberUtils;
@@ -134,44 +133,39 @@ public class CallerInfo {
int columnIndex;
// Look for the name
columnIndex = cursor.getColumnIndex(People.NAME);
columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
if (columnIndex != -1) {
info.name = cursor.getString(columnIndex);
}
// Look for the number
columnIndex = cursor.getColumnIndex(Phones.NUMBER);
columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
if (columnIndex != -1) {
info.phoneNumber = cursor.getString(columnIndex);
}
// Look for the label/type combo
columnIndex = cursor.getColumnIndex(Phones.LABEL);
columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
if (columnIndex != -1) {
int typeColumnIndex = cursor.getColumnIndex(Phones.TYPE);
int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE);
if (typeColumnIndex != -1) {
info.numberType = cursor.getInt(typeColumnIndex);
info.numberLabel = cursor.getString(columnIndex);
info.phoneLabel = Contacts.Phones.getDisplayLabel(context,
info.phoneLabel = Phone.getDisplayLabel(context,
info.numberType, info.numberLabel)
.toString();
}
}
// Look for the person ID
columnIndex = cursor.getColumnIndex(Phones.PERSON_ID);
columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
if (columnIndex != -1) {
info.person_id = cursor.getLong(columnIndex);
} else {
columnIndex = cursor.getColumnIndex(People._ID);
if (columnIndex != -1) {
info.person_id = cursor.getLong(columnIndex);
}
}
// look for the custom ringtone, create from the string stored
// in the database.
columnIndex = cursor.getColumnIndex(People.CUSTOM_RINGTONE);
columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
} else {
@@ -180,7 +174,7 @@ public class CallerInfo {
// look for the send to voicemail flag, set it to true only
// under certain circumstances.
columnIndex = cursor.getColumnIndex(People.SEND_TO_VOICEMAIL);
columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
info.shouldSendToVoicemail = (columnIndex != -1) &&
((cursor.getInt(columnIndex)) == 1);
info.contactExists = true;
@@ -256,8 +250,7 @@ public class CallerInfo {
}
}
Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL,
Uri.encode(number));
Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number);
CallerInfo info = getCallerInfo(context, contactUri);

View File

@@ -24,7 +24,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -303,7 +303,7 @@ public class CallerInfoAsyncQuery {
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
OnQueryCompleteListener listener, Object cookie) {
//contruct the URI object and start Query.
Uri contactRef = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, number);
Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number);
CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
c.allocate(context, contactRef);