1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.activity; 18 19import android.content.AsyncTaskLoader; 20import android.content.ContentUris; 21import android.content.Context; 22import android.database.Cursor; 23import android.graphics.Bitmap; 24import android.graphics.BitmapFactory; 25import android.net.Uri; 26import android.provider.ContactsContract.CommonDataKinds.Email; 27import android.provider.ContactsContract.CommonDataKinds.Photo; 28import android.provider.ContactsContract.Contacts; 29import android.provider.ContactsContract.Data; 30import android.provider.ContactsContract.StatusUpdates; 31 32import com.android.emailcommon.Logging; 33import com.android.emailcommon.utility.Utility; 34import com.android.mail.utils.LogUtils; 35import com.google.common.annotations.VisibleForTesting; 36 37/** 38 * Loader to load presence statuses and the contact photo. 39 */ 40public class ContactStatusLoader extends AsyncTaskLoader<ContactStatusLoader.Result> { 41 @VisibleForTesting 42 static final int PRESENCE_UNKNOWN_RESOURCE_ID = android.R.drawable.presence_offline; 43 44 /** email address -> photo id, presence */ 45 /* package */ static final String[] PROJECTION_PHOTO_ID_PRESENCE = new String[] { 46 Contacts.PHOTO_ID, 47 Contacts.CONTACT_PRESENCE 48 }; 49 private static final int COLUMN_PHOTO_ID = 0; 50 private static final int COLUMN_PRESENCE = 1; 51 52 /** photo id -> photo data */ 53 /* package */ static final String[] PHOTO_PROJECTION = new String[] { 54 Photo.PHOTO 55 }; 56 private static final int PHOTO_COLUMN = 0; 57 58 private final Context mContext; 59 private final String mEmailAddress; 60 61 /** 62 * Class that encapsulates the result. 63 */ 64 public static class Result { 65 public static final Result UNKNOWN = new Result(null, PRESENCE_UNKNOWN_RESOURCE_ID, null); 66 67 /** Contact photo. Null if unknown */ 68 public final Bitmap mPhoto; 69 70 /** Presence image resource ID. Always has a valid value, even if unknown. */ 71 public final int mPresenceResId; 72 73 /** URI for opening quick contact. Null if unknown. */ 74 public final Uri mLookupUri; 75 76 public Result(Bitmap photo, int presenceResId, Uri lookupUri) { 77 mPhoto = photo; 78 mPresenceResId = presenceResId; 79 mLookupUri = lookupUri; 80 } 81 82 public boolean isUnknown() { 83 return PRESENCE_UNKNOWN_RESOURCE_ID == mPresenceResId; 84 } 85 } 86 87 public ContactStatusLoader(Context context, String emailAddress) { 88 super(context); 89 mContext = context; 90 mEmailAddress = emailAddress; 91 } 92 93 @Override 94 public Result loadInBackground() { 95 return getContactInfo(mContext, mEmailAddress); 96 } 97 98 /** 99 * Synchronously loads contact data. 100 * 101 * NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS) 102 */ 103 public static Result getContactInfo(Context context, String emailAddress) { 104 // Load photo-id and presence status. 105 Uri uri = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(emailAddress)); 106 Cursor c = context.getContentResolver().query( 107 uri, 108 PROJECTION_PHOTO_ID_PRESENCE, null, null, null); 109 if (c == null) { 110 return Result.UNKNOWN; 111 } 112 final long photoId; 113 final int presenceStatus; 114 try { 115 if (!c.moveToFirst()) { 116 return Result.UNKNOWN; 117 } 118 photoId = c.getLong(COLUMN_PHOTO_ID); 119 presenceStatus = c.getInt(COLUMN_PRESENCE); 120 } finally { 121 c.close(); 122 } 123 124 // Convert presence status into the res id. 125 final int presenceStatusResId = StatusUpdates.getPresenceIconResourceId(presenceStatus); 126 127 // load photo from photo-id. 128 Bitmap photo = null; 129 if (photoId != -1) { 130 final byte[] photoData = Utility.getFirstRowBlob(context, 131 ContentUris.withAppendedId(Data.CONTENT_URI, photoId), PHOTO_PROJECTION, 132 null, null, null, PHOTO_COLUMN, null); 133 if (photoData != null) { 134 try { 135 photo = BitmapFactory.decodeByteArray(photoData, 0, photoData.length, null); 136 } catch (OutOfMemoryError e) { 137 LogUtils.d(Logging.LOG_TAG, "Decoding bitmap failed with " + e.getMessage()); 138 } 139 } 140 } 141 142 // Get lookup URI 143 final Uri lookupUri = Data.getContactLookupUri(context.getContentResolver(), uri); 144 return new Result(photo, presenceStatusResId, lookupUri); 145 } 146 147 @Override 148 protected void onStartLoading() { 149 cancelLoad(); 150 forceLoad(); 151 } 152 153 @Override 154 protected void onStopLoading() { 155 cancelLoad(); 156 } 157 158 @Override 159 protected void onReset() { 160 stopLoading(); 161 } 162} 163