blob: 23fa402a7ae55cee3886ba735776f56ba478c905 [file] [log] [blame]
Evan Millare727f182009-08-27 11:15:55 -07001/*
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
17package android.widget;
18
Gilles Debunne92350582011-01-07 19:39:26 -080019import com.android.internal.R;
20
Evan Millare727f182009-08-27 11:15:55 -070021import android.content.AsyncQueryHandler;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.res.TypedArray;
26import android.database.Cursor;
Gilles Debunne92350582011-01-07 19:39:26 -080027import android.graphics.Canvas;
Mark Wagnerf5935a92009-09-22 13:49:26 -070028import android.graphics.drawable.Drawable;
Evan Millare727f182009-08-27 11:15:55 -070029import android.net.Uri;
Yorke Lee02f304f2013-01-14 08:57:43 -080030import android.os.Bundle;
Gilles Debunne92350582011-01-07 19:39:26 -080031import android.provider.ContactsContract.CommonDataKinds.Email;
Evan Millare727f182009-08-27 11:15:55 -070032import android.provider.ContactsContract.Contacts;
33import android.provider.ContactsContract.Intents;
34import android.provider.ContactsContract.PhoneLookup;
Tadashi G. Takaoka01fd4e12009-10-01 18:10:46 -070035import android.provider.ContactsContract.QuickContact;
Dmitri Plotnikovea367472009-09-18 15:04:58 -070036import android.provider.ContactsContract.RawContacts;
Evan Millare727f182009-08-27 11:15:55 -070037import android.util.AttributeSet;
38import android.view.View;
39import android.view.View.OnClickListener;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080040import android.view.accessibility.AccessibilityEvent;
41import android.view.accessibility.AccessibilityNodeInfo;
Evan Millare727f182009-08-27 11:15:55 -070042
43/**
Evan Millarc1a2a822009-09-29 18:02:06 -070044 * Widget used to show an image with the standard QuickContact badge
Evan Millare727f182009-08-27 11:15:55 -070045 * and on-click behavior.
Evan Millare727f182009-08-27 11:15:55 -070046 */
Evan Millarc1a2a822009-09-29 18:02:06 -070047public class QuickContactBadge extends ImageView implements OnClickListener {
Evan Millare727f182009-08-27 11:15:55 -070048 private Uri mContactUri;
49 private String mContactEmail;
50 private String mContactPhone;
Gilles Debunne92350582011-01-07 19:39:26 -080051 private Drawable mOverlay;
Evan Millare727f182009-08-27 11:15:55 -070052 private QueryHandler mQueryHandler;
Evan Millar9a20aa72010-04-13 09:51:21 -070053 private Drawable mDefaultAvatar;
Yorke Lee02f304f2013-01-14 08:57:43 -080054 private Bundle mExtras = null;
Evan Millare727f182009-08-27 11:15:55 -070055
56 protected String[] mExcludeMimes = null;
57
58 static final private int TOKEN_EMAIL_LOOKUP = 0;
59 static final private int TOKEN_PHONE_LOOKUP = 1;
60 static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2;
61 static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3;
62
Yorke Lee02f304f2013-01-14 08:57:43 -080063 static final private String EXTRA_URI_CONTENT = "uri_content";
64
Evan Millare727f182009-08-27 11:15:55 -070065 static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
Dmitri Plotnikovea367472009-09-18 15:04:58 -070066 RawContacts.CONTACT_ID,
Evan Millare727f182009-08-27 11:15:55 -070067 Contacts.LOOKUP_KEY,
68 };
Tadashi G. Takaoka01fd4e12009-10-01 18:10:46 -070069 static final int EMAIL_ID_COLUMN_INDEX = 0;
70 static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1;
Evan Millare727f182009-08-27 11:15:55 -070071
72 static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
73 PhoneLookup._ID,
74 PhoneLookup.LOOKUP_KEY,
75 };
Tadashi G. Takaoka01fd4e12009-10-01 18:10:46 -070076 static final int PHONE_ID_COLUMN_INDEX = 0;
77 static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1;
Evan Millare727f182009-08-27 11:15:55 -070078
Evan Millarc1a2a822009-09-29 18:02:06 -070079 public QuickContactBadge(Context context) {
Evan Millare727f182009-08-27 11:15:55 -070080 this(context, null);
81 }
82
Evan Millarc1a2a822009-09-29 18:02:06 -070083 public QuickContactBadge(Context context, AttributeSet attrs) {
Evan Millare727f182009-08-27 11:15:55 -070084 this(context, attrs, 0);
85 }
86
Alan Viverette617feb92013-09-09 18:09:13 -070087 public QuickContactBadge(Context context, AttributeSet attrs, int defStyleAttr) {
88 this(context, attrs, defStyleAttr, 0);
89 }
90
91 public QuickContactBadge(
92 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
93 super(context, attrs, defStyleAttr, defStyleRes);
Evan Millare727f182009-08-27 11:15:55 -070094
Gilles Debunne92350582011-01-07 19:39:26 -080095 TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme);
Daniel Lehmanne13e55b2011-08-07 19:51:28 -070096 mOverlay = styledAttributes.getDrawable(
97 com.android.internal.R.styleable.Theme_quickContactBadgeOverlay);
Gilles Debunne92350582011-01-07 19:39:26 -080098 styledAttributes.recycle();
99
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700100 if (!isInEditMode()) {
101 mQueryHandler = new QueryHandler(mContext.getContentResolver());
102 }
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700103 setOnClickListener(this);
Evan Millare727f182009-08-27 11:15:55 -0700104 }
105
Gilles Debunne5cba8622011-01-12 19:52:56 -0800106 @Override
107 protected void drawableStateChanged() {
108 super.drawableStateChanged();
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700109 if (mOverlay != null && mOverlay.isStateful()) {
110 mOverlay.setState(getDrawableState());
Gilles Debunne5cba8622011-01-12 19:52:56 -0800111 invalidate();
112 }
113 }
114
Alan Viverettecebc6ba2014-06-13 15:52:13 -0700115 @Override
Alan Viverette8de14942014-06-18 18:05:15 -0700116 public void drawableHotspotChanged(float x, float y) {
117 super.drawableHotspotChanged(x, y);
Alan Viverettecebc6ba2014-06-13 15:52:13 -0700118
119 if (mOverlay != null) {
120 mOverlay.setHotspot(x, y);
121 }
122 }
123
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700124 /** This call has no effect anymore, as there is only one QuickContact mode */
125 @SuppressWarnings("unused")
Evan Millare3ec9972009-09-30 19:37:36 -0700126 public void setMode(int size) {
Evan Millare3ec9972009-09-30 19:37:36 -0700127 }
Gilles Debunne92350582011-01-07 19:39:26 -0800128
129 @Override
130 protected void onDraw(Canvas canvas) {
131 super.onDraw(canvas);
132
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700133 if (!isEnabled()) {
134 // not clickable? don't show triangle
135 return;
136 }
137
Gilles Debunne5cba8622011-01-12 19:52:56 -0800138 if (mOverlay == null || mOverlay.getIntrinsicWidth() == 0 ||
139 mOverlay.getIntrinsicHeight() == 0) {
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700140 // nothing to draw
141 return;
Gilles Debunne92350582011-01-07 19:39:26 -0800142 }
143
144 mOverlay.setBounds(0, 0, getWidth(), getHeight());
145
146 if (mPaddingTop == 0 && mPaddingLeft == 0) {
147 mOverlay.draw(canvas);
148 } else {
149 int saveCount = canvas.getSaveCount();
150 canvas.save();
151 canvas.translate(mPaddingLeft, mPaddingTop);
152 mOverlay.draw(canvas);
153 canvas.restoreToCount(saveCount);
154 }
155 }
156
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700157 /** True if a contact, an email address or a phone number has been assigned */
158 private boolean isAssigned() {
159 return mContactUri != null || mContactEmail != null || mContactPhone != null;
160 }
161
Evan Millar9a20aa72010-04-13 09:51:21 -0700162 /**
163 * Resets the contact photo to the default state.
164 */
165 public void setImageToDefault() {
166 if (mDefaultAvatar == null) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800167 mDefaultAvatar = mContext.getDrawable(R.drawable.ic_contact_picture);
Evan Millar9a20aa72010-04-13 09:51:21 -0700168 }
169 setImageDrawable(mDefaultAvatar);
170 }
Evan Millare3ec9972009-09-30 19:37:36 -0700171
172 /**
Evan Millarc1a2a822009-09-29 18:02:06 -0700173 * Assign the contact uri that this QuickContactBadge should be associated
174 * with. Note that this is only used for displaying the QuickContact window and
Gilles Debunne92350582011-01-07 19:39:26 -0800175 * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the
176 * photo.
Evan Millare727f182009-08-27 11:15:55 -0700177 *
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700178 * @param contactUri Either a {@link Contacts#CONTENT_URI} or
179 * {@link Contacts#CONTENT_LOOKUP_URI} style URI.
Evan Millare727f182009-08-27 11:15:55 -0700180 */
181 public void assignContactUri(Uri contactUri) {
182 mContactUri = contactUri;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700183 mContactEmail = null;
184 mContactPhone = null;
185 onContactUriChanged();
186 }
187
Evan Millare727f182009-08-27 11:15:55 -0700188 /**
189 * Assign a contact based on an email address. This should only be used when
190 * the contact's URI is not available, as an extra query will have to be
191 * performed to lookup the URI based on the email.
192 *
193 * @param emailAddress The email address of the contact.
194 * @param lazyLookup If this is true, the lookup query will not be performed
195 * until this view is clicked.
196 */
197 public void assignContactFromEmail(String emailAddress, boolean lazyLookup) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800198 assignContactFromEmail(emailAddress, lazyLookup, null);
199 }
200
201 /**
202 * Assign a contact based on an email address. This should only be used when
203 * the contact's URI is not available, as an extra query will have to be
204 * performed to lookup the URI based on the email.
205
206 @param emailAddress The email address of the contact.
207 @param lazyLookup If this is true, the lookup query will not be performed
208 until this view is clicked.
209 @param extras A bundle of extras to populate the contact edit page with if the contact
210 is not found and the user chooses to add the email address to an existing contact or
211 create a new contact. Uses the same string constants as those found in
Yorke Lee3e839f42013-01-15 16:46:00 -0800212 {@link android.provider.ContactsContract.Intents.Insert}
Yorke Lee02f304f2013-01-14 08:57:43 -0800213 */
214
215 public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) {
Evan Millare727f182009-08-27 11:15:55 -0700216 mContactEmail = emailAddress;
Yorke Lee02f304f2013-01-14 08:57:43 -0800217 mExtras = extras;
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700218 if (!lazyLookup && mQueryHandler != null) {
Evan Millare727f182009-08-27 11:15:55 -0700219 mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null,
220 Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
221 EMAIL_LOOKUP_PROJECTION, null, null, null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700222 } else {
223 mContactUri = null;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700224 onContactUriChanged();
Evan Millare727f182009-08-27 11:15:55 -0700225 }
226 }
227
Yorke Lee02f304f2013-01-14 08:57:43 -0800228
Evan Millare727f182009-08-27 11:15:55 -0700229 /**
230 * Assign a contact based on a phone number. This should only be used when
231 * the contact's URI is not available, as an extra query will have to be
232 * performed to lookup the URI based on the phone number.
233 *
234 * @param phoneNumber The phone number of the contact.
235 * @param lazyLookup If this is true, the lookup query will not be performed
236 * until this view is clicked.
237 */
238 public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800239 assignContactFromPhone(phoneNumber, lazyLookup, new Bundle());
240 }
241
242 /**
243 * Assign a contact based on a phone number. This should only be used when
244 * the contact's URI is not available, as an extra query will have to be
245 * performed to lookup the URI based on the phone number.
246 *
247 * @param phoneNumber The phone number of the contact.
248 * @param lazyLookup If this is true, the lookup query will not be performed
249 * until this view is clicked.
250 * @param extras A bundle of extras to populate the contact edit page with if the contact
251 * is not found and the user chooses to add the phone number to an existing contact or
252 * create a new contact. Uses the same string constants as those found in
Yorke Lee3e839f42013-01-15 16:46:00 -0800253 * {@link android.provider.ContactsContract.Intents.Insert}
Yorke Lee02f304f2013-01-14 08:57:43 -0800254 */
255 public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) {
Evan Millare727f182009-08-27 11:15:55 -0700256 mContactPhone = phoneNumber;
Yorke Lee02f304f2013-01-14 08:57:43 -0800257 mExtras = extras;
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700258 if (!lazyLookup && mQueryHandler != null) {
Evan Millare727f182009-08-27 11:15:55 -0700259 mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null,
260 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
261 PHONE_LOOKUP_PROJECTION, null, null, null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700262 } else {
263 mContactUri = null;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700264 onContactUriChanged();
Evan Millare727f182009-08-27 11:15:55 -0700265 }
266 }
267
Yorke Leea702ccd2014-04-28 12:41:44 -0700268 /**
269 * Assigns the drawable that is to be drawn on top of the assigned contact photo.
270 *
271 * @param overlay Drawable to be drawn over the assigned contact photo. Must have a non-zero
272 * instrinsic width and height.
273 */
274 public void setOverlay(Drawable overlay) {
275 mOverlay = overlay;
276 }
277
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700278 private void onContactUriChanged() {
279 setEnabled(isAssigned());
280 }
281
282 @Override
Evan Millare727f182009-08-27 11:15:55 -0700283 public void onClick(View v) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800284 // If contact has been assigned, mExtras should no longer be null, but do a null check
285 // anyway just in case assignContactFromPhone or Email was called with a null bundle or
286 // wasn't assigned previously.
287 final Bundle extras = (mExtras == null) ? new Bundle() : mExtras;
Evan Millare727f182009-08-27 11:15:55 -0700288 if (mContactUri != null) {
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700289 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri,
290 QuickContact.MODE_LARGE, mExcludeMimes);
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700291 } else if (mContactEmail != null && mQueryHandler != null) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800292 extras.putString(EXTRA_URI_CONTENT, mContactEmail);
293 mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras,
Evan Millare727f182009-08-27 11:15:55 -0700294 Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
295 EMAIL_LOOKUP_PROJECTION, null, null, null);
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700296 } else if (mContactPhone != null && mQueryHandler != null) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800297 extras.putString(EXTRA_URI_CONTENT, mContactPhone);
298 mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras,
Evan Millare727f182009-08-27 11:15:55 -0700299 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
300 PHONE_LOOKUP_PROJECTION, null, null, null);
301 } else {
302 // If a contact hasn't been assigned, don't react to click.
303 return;
304 }
305 }
306
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800307 @Override
308 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
309 super.onInitializeAccessibilityEvent(event);
310 event.setClassName(QuickContactBadge.class.getName());
311 }
312
313 @Override
314 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
315 super.onInitializeAccessibilityNodeInfo(info);
316 info.setClassName(QuickContactBadge.class.getName());
317 }
318
Evan Millare727f182009-08-27 11:15:55 -0700319 /**
320 * Set a list of specific MIME-types to exclude and not display. For
321 * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE}
322 * profile icon.
323 */
324 public void setExcludeMimes(String[] excludeMimes) {
325 mExcludeMimes = excludeMimes;
326 }
327
Evan Millare727f182009-08-27 11:15:55 -0700328 private class QueryHandler extends AsyncQueryHandler {
329
330 public QueryHandler(ContentResolver cr) {
331 super(cr);
332 }
333
334 @Override
335 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700336 Uri lookupUri = null;
337 Uri createUri = null;
Evan Millare727f182009-08-27 11:15:55 -0700338 boolean trigger = false;
Yorke Lee02f304f2013-01-14 08:57:43 -0800339 Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700340 try {
Evan Millare727f182009-08-27 11:15:55 -0700341 switch(token) {
342 case TOKEN_PHONE_LOOKUP_AND_TRIGGER:
343 trigger = true;
Yorke Lee02f304f2013-01-14 08:57:43 -0800344 createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700345
Makoto Onukia362e1d2010-09-07 10:46:59 -0700346 //$FALL-THROUGH$
Evan Millare727f182009-08-27 11:15:55 -0700347 case TOKEN_PHONE_LOOKUP: {
348 if (cursor != null && cursor.moveToFirst()) {
349 long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
350 String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700351 lookupUri = Contacts.getLookupUri(contactId, lookupKey);
Evan Millare727f182009-08-27 11:15:55 -0700352 }
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700353
Evan Millare727f182009-08-27 11:15:55 -0700354 break;
355 }
356 case TOKEN_EMAIL_LOOKUP_AND_TRIGGER:
357 trigger = true;
Yorke Lee02f304f2013-01-14 08:57:43 -0800358 createUri = Uri.fromParts("mailto",
359 extras.getString(EXTRA_URI_CONTENT), null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700360
Makoto Onukia362e1d2010-09-07 10:46:59 -0700361 //$FALL-THROUGH$
Evan Millare727f182009-08-27 11:15:55 -0700362 case TOKEN_EMAIL_LOOKUP: {
363 if (cursor != null && cursor.moveToFirst()) {
364 long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX);
365 String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700366 lookupUri = Contacts.getLookupUri(contactId, lookupKey);
Evan Millare727f182009-08-27 11:15:55 -0700367 }
Makoto Onukia362e1d2010-09-07 10:46:59 -0700368 break;
Evan Millare727f182009-08-27 11:15:55 -0700369 }
370 }
371 } finally {
372 if (cursor != null) {
373 cursor.close();
374 }
375 }
376
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700377 mContactUri = lookupUri;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700378 onContactUriChanged();
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700379
380 if (trigger && lookupUri != null) {
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700381 // Found contact, so trigger QuickContact
382 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, lookupUri,
383 QuickContact.MODE_LARGE, mExcludeMimes);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700384 } else if (createUri != null) {
385 // Prompt user to add this person to contacts
386 final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
Yorke Lee02f304f2013-01-14 08:57:43 -0800387 if (extras != null) {
388 extras.remove(EXTRA_URI_CONTENT);
389 intent.putExtras(extras);
390 }
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700391 getContext().startActivity(intent);
Evan Millare727f182009-08-27 11:15:55 -0700392 }
393 }
394 }
395}