1 /* 2 * Copyright (C) 2009 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 17 package android.widget; 18 19 import com.android.internal.R; 20 21 import android.content.AsyncQueryHandler; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.res.TypedArray; 26 import android.database.Cursor; 27 import android.graphics.Canvas; 28 import android.graphics.drawable.Drawable; 29 import android.net.Uri; 30 import android.provider.ContactsContract.CommonDataKinds.Email; 31 import android.provider.ContactsContract.Contacts; 32 import android.provider.ContactsContract.Intents; 33 import android.provider.ContactsContract.PhoneLookup; 34 import android.provider.ContactsContract.QuickContact; 35 import android.provider.ContactsContract.RawContacts; 36 import android.util.AttributeSet; 37 import android.view.View; 38 import android.view.View.OnClickListener; 39 40 /** 41 * Widget used to show an image with the standard QuickContact badge 42 * and on-click behavior. 43 */ 44 public class QuickContactBadge extends ImageView implements OnClickListener { 45 private Uri mContactUri; 46 private String mContactEmail; 47 private String mContactPhone; 48 private Drawable mOverlay; 49 private QueryHandler mQueryHandler; 50 private Drawable mDefaultAvatar; 51 52 protected String[] mExcludeMimes = null; 53 54 static final private int TOKEN_EMAIL_LOOKUP = 0; 55 static final private int TOKEN_PHONE_LOOKUP = 1; 56 static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2; 57 static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3; 58 59 static final String[] EMAIL_LOOKUP_PROJECTION = new String[] { 60 RawContacts.CONTACT_ID, 61 Contacts.LOOKUP_KEY, 62 }; 63 static final int EMAIL_ID_COLUMN_INDEX = 0; 64 static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1; 65 66 static final String[] PHONE_LOOKUP_PROJECTION = new String[] { 67 PhoneLookup._ID, 68 PhoneLookup.LOOKUP_KEY, 69 }; 70 static final int PHONE_ID_COLUMN_INDEX = 0; 71 static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1; 72 73 public QuickContactBadge(Context context) { 74 this(context, null); 75 } 76 77 public QuickContactBadge(Context context, AttributeSet attrs) { 78 this(context, attrs, 0); 79 } 80 81 public QuickContactBadge(Context context, AttributeSet attrs, int defStyle) { 82 super(context, attrs, defStyle); 83 84 TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme); 85 mOverlay = styledAttributes.getDrawable( 86 com.android.internal.R.styleable.Theme_quickContactBadgeOverlay); 87 styledAttributes.recycle(); 88 89 mQueryHandler = new QueryHandler(mContext.getContentResolver()); 90 setOnClickListener(this); 91 } 92 93 @Override 94 protected void drawableStateChanged() { 95 super.drawableStateChanged(); 96 if (mOverlay != null && mOverlay.isStateful()) { 97 mOverlay.setState(getDrawableState()); 98 invalidate(); 99 } 100 } 101 102 /** This call has no effect anymore, as there is only one QuickContact mode */ 103 @SuppressWarnings("unused") 104 public void setMode(int size) { 105 } 106 107 @Override 108 protected void onDraw(Canvas canvas) { 109 super.onDraw(canvas); 110 111 if (!isEnabled()) { 112 // not clickable? don't show triangle 113 return; 114 } 115 116 if (mOverlay == null || mOverlay.getIntrinsicWidth() == 0 || 117 mOverlay.getIntrinsicHeight() == 0) { 118 // nothing to draw 119 return; 120 } 121 122 mOverlay.setBounds(0, 0, getWidth(), getHeight()); 123 124 if (mPaddingTop == 0 && mPaddingLeft == 0) { 125 mOverlay.draw(canvas); 126 } else { 127 int saveCount = canvas.getSaveCount(); 128 canvas.save(); 129 canvas.translate(mPaddingLeft, mPaddingTop); 130 mOverlay.draw(canvas); 131 canvas.restoreToCount(saveCount); 132 } 133 } 134 135 /** True if a contact, an email address or a phone number has been assigned */ 136 private boolean isAssigned() { 137 return mContactUri != null || mContactEmail != null || mContactPhone != null; 138 } 139 140 /** 141 * Resets the contact photo to the default state. 142 */ 143 public void setImageToDefault() { 144 if (mDefaultAvatar == null) { 145 mDefaultAvatar = getResources().getDrawable(R.drawable.ic_contact_picture); 146 } 147 setImageDrawable(mDefaultAvatar); 148 } 149 150 /** 151 * Assign the contact uri that this QuickContactBadge should be associated 152 * with. Note that this is only used for displaying the QuickContact window and 153 * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the 154 * photo. 155 * 156 * @param contactUri Either a {@link Contacts#CONTENT_URI} or 157 * {@link Contacts#CONTENT_LOOKUP_URI} style URI. 158 */ 159 public void assignContactUri(Uri contactUri) { 160 mContactUri = contactUri; 161 mContactEmail = null; 162 mContactPhone = null; 163 onContactUriChanged(); 164 } 165 166 /** 167 * Assign a contact based on an email address. This should only be used when 168 * the contact's URI is not available, as an extra query will have to be 169 * performed to lookup the URI based on the email. 170 * 171 * @param emailAddress The email address of the contact. 172 * @param lazyLookup If this is true, the lookup query will not be performed 173 * until this view is clicked. 174 */ 175 public void assignContactFromEmail(String emailAddress, boolean lazyLookup) { 176 mContactEmail = emailAddress; 177 if (!lazyLookup) { 178 mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null, 179 Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), 180 EMAIL_LOOKUP_PROJECTION, null, null, null); 181 } else { 182 mContactUri = null; 183 onContactUriChanged(); 184 } 185 } 186 187 /** 188 * Assign a contact based on a phone number. This should only be used when 189 * the contact's URI is not available, as an extra query will have to be 190 * performed to lookup the URI based on the phone number. 191 * 192 * @param phoneNumber The phone number of the contact. 193 * @param lazyLookup If this is true, the lookup query will not be performed 194 * until this view is clicked. 195 */ 196 public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) { 197 mContactPhone = phoneNumber; 198 if (!lazyLookup) { 199 mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null, 200 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), 201 PHONE_LOOKUP_PROJECTION, null, null, null); 202 } else { 203 mContactUri = null; 204 onContactUriChanged(); 205 } 206 } 207 208 private void onContactUriChanged() { 209 setEnabled(isAssigned()); 210 } 211 212 @Override 213 public void onClick(View v) { 214 if (mContactUri != null) { 215 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri, 216 QuickContact.MODE_LARGE, mExcludeMimes); 217 } else if (mContactEmail != null) { 218 mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, mContactEmail, 219 Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), 220 EMAIL_LOOKUP_PROJECTION, null, null, null); 221 } else if (mContactPhone != null) { 222 mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, mContactPhone, 223 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), 224 PHONE_LOOKUP_PROJECTION, null, null, null); 225 } else { 226 // If a contact hasn't been assigned, don't react to click. 227 return; 228 } 229 } 230 231 /** 232 * Set a list of specific MIME-types to exclude and not display. For 233 * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE} 234 * profile icon. 235 */ 236 public void setExcludeMimes(String[] excludeMimes) { 237 mExcludeMimes = excludeMimes; 238 } 239 240 private class QueryHandler extends AsyncQueryHandler { 241 242 public QueryHandler(ContentResolver cr) { 243 super(cr); 244 } 245 246 @Override 247 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 248 Uri lookupUri = null; 249 Uri createUri = null; 250 boolean trigger = false; 251 252 try { 253 switch(token) { 254 case TOKEN_PHONE_LOOKUP_AND_TRIGGER: 255 trigger = true; 256 createUri = Uri.fromParts("tel", (String)cookie, null); 257 258 //$FALL-THROUGH$ 259 case TOKEN_PHONE_LOOKUP: { 260 if (cursor != null && cursor.moveToFirst()) { 261 long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX); 262 String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX); 263 lookupUri = Contacts.getLookupUri(contactId, lookupKey); 264 } 265 266 break; 267 } 268 case TOKEN_EMAIL_LOOKUP_AND_TRIGGER: 269 trigger = true; 270 createUri = Uri.fromParts("mailto", (String)cookie, null); 271 272 //$FALL-THROUGH$ 273 case TOKEN_EMAIL_LOOKUP: { 274 if (cursor != null && cursor.moveToFirst()) { 275 long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX); 276 String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX); 277 lookupUri = Contacts.getLookupUri(contactId, lookupKey); 278 } 279 break; 280 } 281 } 282 } finally { 283 if (cursor != null) { 284 cursor.close(); 285 } 286 } 287 288 mContactUri = lookupUri; 289 onContactUriChanged(); 290 291 if (trigger && lookupUri != null) { 292 // Found contact, so trigger QuickContact 293 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, lookupUri, 294 QuickContact.MODE_LARGE, mExcludeMimes); 295 } else if (createUri != null) { 296 // Prompt user to add this person to contacts 297 final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri); 298 getContext().startActivity(intent); 299 } 300 } 301 } 302 } 303