blob: fd2f7549e70a17708fff0fab6adf16037da534ac [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
Evan Millarc1a2a822009-09-29 18:02:06 -070087 public QuickContactBadge(Context context, AttributeSet attrs, int defStyle) {
Evan Millare727f182009-08-27 11:15:55 -070088 super(context, attrs, defStyle);
89
Gilles Debunne92350582011-01-07 19:39:26 -080090 TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme);
Daniel Lehmanne13e55b2011-08-07 19:51:28 -070091 mOverlay = styledAttributes.getDrawable(
92 com.android.internal.R.styleable.Theme_quickContactBadgeOverlay);
Gilles Debunne92350582011-01-07 19:39:26 -080093 styledAttributes.recycle();
94
Deepanshu Gupta34e9c692013-10-18 11:22:46 -070095 if (!isInEditMode()) {
96 mQueryHandler = new QueryHandler(mContext.getContentResolver());
97 }
Daniel Lehmanne13e55b2011-08-07 19:51:28 -070098 setOnClickListener(this);
Evan Millare727f182009-08-27 11:15:55 -070099 }
100
Gilles Debunne5cba8622011-01-12 19:52:56 -0800101 @Override
102 protected void drawableStateChanged() {
103 super.drawableStateChanged();
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700104 if (mOverlay != null && mOverlay.isStateful()) {
105 mOverlay.setState(getDrawableState());
Gilles Debunne5cba8622011-01-12 19:52:56 -0800106 invalidate();
107 }
108 }
109
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700110 /** This call has no effect anymore, as there is only one QuickContact mode */
111 @SuppressWarnings("unused")
Evan Millare3ec9972009-09-30 19:37:36 -0700112 public void setMode(int size) {
Evan Millare3ec9972009-09-30 19:37:36 -0700113 }
Gilles Debunne92350582011-01-07 19:39:26 -0800114
115 @Override
116 protected void onDraw(Canvas canvas) {
117 super.onDraw(canvas);
118
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700119 if (!isEnabled()) {
120 // not clickable? don't show triangle
121 return;
122 }
123
Gilles Debunne5cba8622011-01-12 19:52:56 -0800124 if (mOverlay == null || mOverlay.getIntrinsicWidth() == 0 ||
125 mOverlay.getIntrinsicHeight() == 0) {
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700126 // nothing to draw
127 return;
Gilles Debunne92350582011-01-07 19:39:26 -0800128 }
129
130 mOverlay.setBounds(0, 0, getWidth(), getHeight());
131
132 if (mPaddingTop == 0 && mPaddingLeft == 0) {
133 mOverlay.draw(canvas);
134 } else {
135 int saveCount = canvas.getSaveCount();
136 canvas.save();
137 canvas.translate(mPaddingLeft, mPaddingTop);
138 mOverlay.draw(canvas);
139 canvas.restoreToCount(saveCount);
140 }
141 }
142
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700143 /** True if a contact, an email address or a phone number has been assigned */
144 private boolean isAssigned() {
145 return mContactUri != null || mContactEmail != null || mContactPhone != null;
146 }
147
Evan Millar9a20aa72010-04-13 09:51:21 -0700148 /**
149 * Resets the contact photo to the default state.
150 */
151 public void setImageToDefault() {
152 if (mDefaultAvatar == null) {
153 mDefaultAvatar = getResources().getDrawable(R.drawable.ic_contact_picture);
154 }
155 setImageDrawable(mDefaultAvatar);
156 }
Evan Millare3ec9972009-09-30 19:37:36 -0700157
158 /**
Evan Millarc1a2a822009-09-29 18:02:06 -0700159 * Assign the contact uri that this QuickContactBadge should be associated
160 * with. Note that this is only used for displaying the QuickContact window and
Gilles Debunne92350582011-01-07 19:39:26 -0800161 * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the
162 * photo.
Evan Millare727f182009-08-27 11:15:55 -0700163 *
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700164 * @param contactUri Either a {@link Contacts#CONTENT_URI} or
165 * {@link Contacts#CONTENT_LOOKUP_URI} style URI.
Evan Millare727f182009-08-27 11:15:55 -0700166 */
167 public void assignContactUri(Uri contactUri) {
168 mContactUri = contactUri;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700169 mContactEmail = null;
170 mContactPhone = null;
171 onContactUriChanged();
172 }
173
Evan Millare727f182009-08-27 11:15:55 -0700174 /**
175 * Assign a contact based on an email address. This should only be used when
176 * the contact's URI is not available, as an extra query will have to be
177 * performed to lookup the URI based on the email.
178 *
179 * @param emailAddress The email address of the contact.
180 * @param lazyLookup If this is true, the lookup query will not be performed
181 * until this view is clicked.
182 */
183 public void assignContactFromEmail(String emailAddress, boolean lazyLookup) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800184 assignContactFromEmail(emailAddress, lazyLookup, null);
185 }
186
187 /**
188 * Assign a contact based on an email address. 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 email.
191
192 @param emailAddress The email address of the contact.
193 @param lazyLookup If this is true, the lookup query will not be performed
194 until this view is clicked.
195 @param extras A bundle of extras to populate the contact edit page with if the contact
196 is not found and the user chooses to add the email address to an existing contact or
197 create a new contact. Uses the same string constants as those found in
Yorke Lee3e839f42013-01-15 16:46:00 -0800198 {@link android.provider.ContactsContract.Intents.Insert}
Yorke Lee02f304f2013-01-14 08:57:43 -0800199 */
200
201 public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) {
Evan Millare727f182009-08-27 11:15:55 -0700202 mContactEmail = emailAddress;
Yorke Lee02f304f2013-01-14 08:57:43 -0800203 mExtras = extras;
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700204 if (!lazyLookup && mQueryHandler != null) {
Evan Millare727f182009-08-27 11:15:55 -0700205 mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null,
206 Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
207 EMAIL_LOOKUP_PROJECTION, null, null, null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700208 } else {
209 mContactUri = null;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700210 onContactUriChanged();
Evan Millare727f182009-08-27 11:15:55 -0700211 }
212 }
213
Yorke Lee02f304f2013-01-14 08:57:43 -0800214
Evan Millare727f182009-08-27 11:15:55 -0700215 /**
216 * Assign a contact based on a phone number. This should only be used when
217 * the contact's URI is not available, as an extra query will have to be
218 * performed to lookup the URI based on the phone number.
219 *
220 * @param phoneNumber The phone number of the contact.
221 * @param lazyLookup If this is true, the lookup query will not be performed
222 * until this view is clicked.
223 */
224 public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800225 assignContactFromPhone(phoneNumber, lazyLookup, new Bundle());
226 }
227
228 /**
229 * Assign a contact based on a phone number. This should only be used when
230 * the contact's URI is not available, as an extra query will have to be
231 * performed to lookup the URI based on the phone number.
232 *
233 * @param phoneNumber The phone number of the contact.
234 * @param lazyLookup If this is true, the lookup query will not be performed
235 * until this view is clicked.
236 * @param extras A bundle of extras to populate the contact edit page with if the contact
237 * is not found and the user chooses to add the phone number to an existing contact or
238 * create a new contact. Uses the same string constants as those found in
Yorke Lee3e839f42013-01-15 16:46:00 -0800239 * {@link android.provider.ContactsContract.Intents.Insert}
Yorke Lee02f304f2013-01-14 08:57:43 -0800240 */
241 public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) {
Evan Millare727f182009-08-27 11:15:55 -0700242 mContactPhone = phoneNumber;
Yorke Lee02f304f2013-01-14 08:57:43 -0800243 mExtras = extras;
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700244 if (!lazyLookup && mQueryHandler != null) {
Evan Millare727f182009-08-27 11:15:55 -0700245 mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null,
246 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
247 PHONE_LOOKUP_PROJECTION, null, null, null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700248 } else {
249 mContactUri = null;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700250 onContactUriChanged();
Evan Millare727f182009-08-27 11:15:55 -0700251 }
252 }
253
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700254 private void onContactUriChanged() {
255 setEnabled(isAssigned());
256 }
257
258 @Override
Evan Millare727f182009-08-27 11:15:55 -0700259 public void onClick(View v) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800260 // If contact has been assigned, mExtras should no longer be null, but do a null check
261 // anyway just in case assignContactFromPhone or Email was called with a null bundle or
262 // wasn't assigned previously.
263 final Bundle extras = (mExtras == null) ? new Bundle() : mExtras;
Evan Millare727f182009-08-27 11:15:55 -0700264 if (mContactUri != null) {
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700265 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri,
266 QuickContact.MODE_LARGE, mExcludeMimes);
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700267 } else if (mContactEmail != null && mQueryHandler != null) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800268 extras.putString(EXTRA_URI_CONTENT, mContactEmail);
269 mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras,
Evan Millare727f182009-08-27 11:15:55 -0700270 Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
271 EMAIL_LOOKUP_PROJECTION, null, null, null);
Deepanshu Gupta34e9c692013-10-18 11:22:46 -0700272 } else if (mContactPhone != null && mQueryHandler != null) {
Yorke Lee02f304f2013-01-14 08:57:43 -0800273 extras.putString(EXTRA_URI_CONTENT, mContactPhone);
274 mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras,
Evan Millare727f182009-08-27 11:15:55 -0700275 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
276 PHONE_LOOKUP_PROJECTION, null, null, null);
277 } else {
278 // If a contact hasn't been assigned, don't react to click.
279 return;
280 }
281 }
282
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800283 @Override
284 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
285 super.onInitializeAccessibilityEvent(event);
286 event.setClassName(QuickContactBadge.class.getName());
287 }
288
289 @Override
290 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
291 super.onInitializeAccessibilityNodeInfo(info);
292 info.setClassName(QuickContactBadge.class.getName());
293 }
294
Evan Millare727f182009-08-27 11:15:55 -0700295 /**
296 * Set a list of specific MIME-types to exclude and not display. For
297 * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE}
298 * profile icon.
299 */
300 public void setExcludeMimes(String[] excludeMimes) {
301 mExcludeMimes = excludeMimes;
302 }
303
Evan Millare727f182009-08-27 11:15:55 -0700304 private class QueryHandler extends AsyncQueryHandler {
305
306 public QueryHandler(ContentResolver cr) {
307 super(cr);
308 }
309
310 @Override
311 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700312 Uri lookupUri = null;
313 Uri createUri = null;
Evan Millare727f182009-08-27 11:15:55 -0700314 boolean trigger = false;
Yorke Lee02f304f2013-01-14 08:57:43 -0800315 Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700316 try {
Evan Millare727f182009-08-27 11:15:55 -0700317 switch(token) {
318 case TOKEN_PHONE_LOOKUP_AND_TRIGGER:
319 trigger = true;
Yorke Lee02f304f2013-01-14 08:57:43 -0800320 createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700321
Makoto Onukia362e1d2010-09-07 10:46:59 -0700322 //$FALL-THROUGH$
Evan Millare727f182009-08-27 11:15:55 -0700323 case TOKEN_PHONE_LOOKUP: {
324 if (cursor != null && cursor.moveToFirst()) {
325 long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
326 String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700327 lookupUri = Contacts.getLookupUri(contactId, lookupKey);
Evan Millare727f182009-08-27 11:15:55 -0700328 }
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700329
Evan Millare727f182009-08-27 11:15:55 -0700330 break;
331 }
332 case TOKEN_EMAIL_LOOKUP_AND_TRIGGER:
333 trigger = true;
Yorke Lee02f304f2013-01-14 08:57:43 -0800334 createUri = Uri.fromParts("mailto",
335 extras.getString(EXTRA_URI_CONTENT), null);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700336
Makoto Onukia362e1d2010-09-07 10:46:59 -0700337 //$FALL-THROUGH$
Evan Millare727f182009-08-27 11:15:55 -0700338 case TOKEN_EMAIL_LOOKUP: {
339 if (cursor != null && cursor.moveToFirst()) {
340 long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX);
341 String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700342 lookupUri = Contacts.getLookupUri(contactId, lookupKey);
Evan Millare727f182009-08-27 11:15:55 -0700343 }
Makoto Onukia362e1d2010-09-07 10:46:59 -0700344 break;
Evan Millare727f182009-08-27 11:15:55 -0700345 }
346 }
347 } finally {
348 if (cursor != null) {
349 cursor.close();
350 }
351 }
352
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700353 mContactUri = lookupUri;
Mark Wagnerf5935a92009-09-22 13:49:26 -0700354 onContactUriChanged();
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700355
356 if (trigger && lookupUri != null) {
Daniel Lehmanne13e55b2011-08-07 19:51:28 -0700357 // Found contact, so trigger QuickContact
358 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, lookupUri,
359 QuickContact.MODE_LARGE, mExcludeMimes);
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700360 } else if (createUri != null) {
361 // Prompt user to add this person to contacts
362 final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
Yorke Lee02f304f2013-01-14 08:57:43 -0800363 if (extras != null) {
364 extras.remove(EXTRA_URI_CONTENT);
365 intent.putExtras(extras);
366 }
Jeff Sharkey0050ee32009-09-17 16:11:32 -0700367 getContext().startActivity(intent);
Evan Millare727f182009-08-27 11:15:55 -0700368 }
369 }
370 }
371}