| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.widget; |
| |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.AsyncQueryHandler; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.TypedArray; |
| import android.database.Cursor; |
| import android.graphics.Canvas; |
| import android.graphics.drawable.Drawable; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.ContactsContract.CommonDataKinds.Email; |
| import android.provider.ContactsContract.Contacts; |
| import android.provider.ContactsContract.Intents; |
| import android.provider.ContactsContract.PhoneLookup; |
| import android.provider.ContactsContract.QuickContact; |
| import android.provider.ContactsContract.RawContacts; |
| import android.util.AttributeSet; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| |
| import com.android.internal.R; |
| |
| /** |
| * Widget used to show an image with the standard QuickContact badge |
| * and on-click behavior. |
| */ |
| public class QuickContactBadge extends ImageView implements OnClickListener { |
| private Uri mContactUri; |
| private String mContactEmail; |
| private String mContactPhone; |
| @UnsupportedAppUsage |
| private Drawable mOverlay; |
| private QueryHandler mQueryHandler; |
| private Drawable mDefaultAvatar; |
| private Bundle mExtras = null; |
| private String mPrioritizedMimeType; |
| |
| protected String[] mExcludeMimes = null; |
| |
| static final private int TOKEN_EMAIL_LOOKUP = 0; |
| static final private int TOKEN_PHONE_LOOKUP = 1; |
| static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2; |
| static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3; |
| |
| static final private String EXTRA_URI_CONTENT = "uri_content"; |
| |
| static final String[] EMAIL_LOOKUP_PROJECTION = new String[] { |
| RawContacts.CONTACT_ID, |
| Contacts.LOOKUP_KEY, |
| }; |
| static final int EMAIL_ID_COLUMN_INDEX = 0; |
| static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1; |
| |
| static final String[] PHONE_LOOKUP_PROJECTION = new String[] { |
| PhoneLookup._ID, |
| PhoneLookup.LOOKUP_KEY, |
| }; |
| static final int PHONE_ID_COLUMN_INDEX = 0; |
| static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1; |
| |
| public QuickContactBadge(Context context) { |
| this(context, null); |
| } |
| |
| public QuickContactBadge(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public QuickContactBadge(Context context, AttributeSet attrs, int defStyleAttr) { |
| this(context, attrs, defStyleAttr, 0); |
| } |
| |
| public QuickContactBadge( |
| Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
| super(context, attrs, defStyleAttr, defStyleRes); |
| |
| TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme); |
| mOverlay = styledAttributes.getDrawable( |
| com.android.internal.R.styleable.Theme_quickContactBadgeOverlay); |
| styledAttributes.recycle(); |
| |
| setOnClickListener(this); |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| |
| if (!isInEditMode()) { |
| mQueryHandler = new QueryHandler(mContext.getContentResolver()); |
| } |
| } |
| |
| @Override |
| protected void drawableStateChanged() { |
| super.drawableStateChanged(); |
| |
| final Drawable overlay = mOverlay; |
| if (overlay != null && overlay.isStateful() |
| && overlay.setState(getDrawableState())) { |
| invalidateDrawable(overlay); |
| } |
| } |
| |
| @Override |
| public void drawableHotspotChanged(float x, float y) { |
| super.drawableHotspotChanged(x, y); |
| |
| if (mOverlay != null) { |
| mOverlay.setHotspot(x, y); |
| } |
| } |
| |
| /** This call has no effect anymore, as there is only one QuickContact mode */ |
| @SuppressWarnings("unused") |
| public void setMode(int size) { |
| } |
| |
| /** |
| * Set which mimetype should be prioritized in the QuickContacts UI. For example, passing the |
| * value {@link Email#CONTENT_ITEM_TYPE} can cause emails to be displayed more prominently in |
| * QuickContacts. |
| */ |
| public void setPrioritizedMimeType(String prioritizedMimeType) { |
| mPrioritizedMimeType = prioritizedMimeType; |
| } |
| |
| @Override |
| protected void onDraw(Canvas canvas) { |
| super.onDraw(canvas); |
| |
| if (!isEnabled()) { |
| // not clickable? don't show triangle |
| return; |
| } |
| |
| if (mOverlay == null || mOverlay.getIntrinsicWidth() == 0 || |
| mOverlay.getIntrinsicHeight() == 0) { |
| // nothing to draw |
| return; |
| } |
| |
| mOverlay.setBounds(0, 0, getWidth(), getHeight()); |
| |
| if (mPaddingTop == 0 && mPaddingLeft == 0) { |
| mOverlay.draw(canvas); |
| } else { |
| int saveCount = canvas.getSaveCount(); |
| canvas.save(); |
| canvas.translate(mPaddingLeft, mPaddingTop); |
| mOverlay.draw(canvas); |
| canvas.restoreToCount(saveCount); |
| } |
| } |
| |
| /** True if a contact, an email address or a phone number has been assigned */ |
| private boolean isAssigned() { |
| return mContactUri != null || mContactEmail != null || mContactPhone != null; |
| } |
| |
| /** |
| * Resets the contact photo to the default state. |
| */ |
| public void setImageToDefault() { |
| if (mDefaultAvatar == null) { |
| mDefaultAvatar = mContext.getDrawable(R.drawable.ic_contact_picture); |
| } |
| setImageDrawable(mDefaultAvatar); |
| } |
| |
| /** |
| * Assign the contact uri that this QuickContactBadge should be associated |
| * with. Note that this is only used for displaying the QuickContact window and |
| * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the |
| * photo. |
| * |
| * @param contactUri Either a {@link Contacts#CONTENT_URI} or |
| * {@link Contacts#CONTENT_LOOKUP_URI} style URI. |
| */ |
| public void assignContactUri(Uri contactUri) { |
| mContactUri = contactUri; |
| mContactEmail = null; |
| mContactPhone = null; |
| onContactUriChanged(); |
| } |
| |
| /** |
| * Assign a contact based on an email address. This should only be used when |
| * the contact's URI is not available, as an extra query will have to be |
| * performed to lookup the URI based on the email. |
| * |
| * @param emailAddress The email address of the contact. |
| * @param lazyLookup If this is true, the lookup query will not be performed |
| * until this view is clicked. |
| */ |
| public void assignContactFromEmail(String emailAddress, boolean lazyLookup) { |
| assignContactFromEmail(emailAddress, lazyLookup, null); |
| } |
| |
| /** |
| * Assign a contact based on an email address. This should only be used when |
| * the contact's URI is not available, as an extra query will have to be |
| * performed to lookup the URI based on the email. |
| |
| @param emailAddress The email address of the contact. |
| @param lazyLookup If this is true, the lookup query will not be performed |
| until this view is clicked. |
| @param extras A bundle of extras to populate the contact edit page with if the contact |
| is not found and the user chooses to add the email address to an existing contact or |
| create a new contact. Uses the same string constants as those found in |
| {@link android.provider.ContactsContract.Intents.Insert} |
| */ |
| |
| public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) { |
| mContactEmail = emailAddress; |
| mExtras = extras; |
| if (!lazyLookup && mQueryHandler != null) { |
| mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null, |
| Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), |
| EMAIL_LOOKUP_PROJECTION, null, null, null); |
| } else { |
| mContactUri = null; |
| onContactUriChanged(); |
| } |
| } |
| |
| |
| /** |
| * Assign a contact based on a phone number. This should only be used when |
| * the contact's URI is not available, as an extra query will have to be |
| * performed to lookup the URI based on the phone number. |
| * |
| * @param phoneNumber The phone number of the contact. |
| * @param lazyLookup If this is true, the lookup query will not be performed |
| * until this view is clicked. |
| */ |
| public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) { |
| assignContactFromPhone(phoneNumber, lazyLookup, new Bundle()); |
| } |
| |
| /** |
| * Assign a contact based on a phone number. This should only be used when |
| * the contact's URI is not available, as an extra query will have to be |
| * performed to lookup the URI based on the phone number. |
| * |
| * @param phoneNumber The phone number of the contact. |
| * @param lazyLookup If this is true, the lookup query will not be performed |
| * until this view is clicked. |
| * @param extras A bundle of extras to populate the contact edit page with if the contact |
| * is not found and the user chooses to add the phone number to an existing contact or |
| * create a new contact. Uses the same string constants as those found in |
| * {@link android.provider.ContactsContract.Intents.Insert} |
| */ |
| public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) { |
| mContactPhone = phoneNumber; |
| mExtras = extras; |
| if (!lazyLookup && mQueryHandler != null) { |
| mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null, |
| Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), |
| PHONE_LOOKUP_PROJECTION, null, null, null); |
| } else { |
| mContactUri = null; |
| onContactUriChanged(); |
| } |
| } |
| |
| /** |
| * Assigns the drawable that is to be drawn on top of the assigned contact photo. |
| * |
| * @param overlay Drawable to be drawn over the assigned contact photo. Must have a non-zero |
| * instrinsic width and height. |
| */ |
| public void setOverlay(Drawable overlay) { |
| mOverlay = overlay; |
| } |
| |
| private void onContactUriChanged() { |
| setEnabled(isAssigned()); |
| } |
| |
| @Override |
| public void onClick(View v) { |
| // If contact has been assigned, mExtras should no longer be null, but do a null check |
| // anyway just in case assignContactFromPhone or Email was called with a null bundle or |
| // wasn't assigned previously. |
| final Bundle extras = (mExtras == null) ? new Bundle() : mExtras; |
| if (mContactUri != null) { |
| QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri, |
| mExcludeMimes, mPrioritizedMimeType); |
| } else if (mContactEmail != null && mQueryHandler != null) { |
| extras.putString(EXTRA_URI_CONTENT, mContactEmail); |
| mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras, |
| Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), |
| EMAIL_LOOKUP_PROJECTION, null, null, null); |
| } else if (mContactPhone != null && mQueryHandler != null) { |
| extras.putString(EXTRA_URI_CONTENT, mContactPhone); |
| mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras, |
| Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), |
| PHONE_LOOKUP_PROJECTION, null, null, null); |
| } else { |
| // If a contact hasn't been assigned, don't react to click. |
| return; |
| } |
| } |
| |
| @Override |
| public CharSequence getAccessibilityClassName() { |
| return QuickContactBadge.class.getName(); |
| } |
| |
| /** |
| * Set a list of specific MIME-types to exclude and not display. For |
| * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE} |
| * profile icon. |
| */ |
| public void setExcludeMimes(String[] excludeMimes) { |
| mExcludeMimes = excludeMimes; |
| } |
| |
| private class QueryHandler extends AsyncQueryHandler { |
| |
| public QueryHandler(ContentResolver cr) { |
| super(cr); |
| } |
| |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| Uri lookupUri = null; |
| Uri createUri = null; |
| boolean trigger = false; |
| Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle(); |
| try { |
| switch(token) { |
| case TOKEN_PHONE_LOOKUP_AND_TRIGGER: |
| trigger = true; |
| createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null); |
| |
| //$FALL-THROUGH$ |
| case TOKEN_PHONE_LOOKUP: { |
| if (cursor != null && cursor.moveToFirst()) { |
| long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX); |
| String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX); |
| lookupUri = Contacts.getLookupUri(contactId, lookupKey); |
| } |
| |
| break; |
| } |
| case TOKEN_EMAIL_LOOKUP_AND_TRIGGER: |
| trigger = true; |
| createUri = Uri.fromParts("mailto", |
| extras.getString(EXTRA_URI_CONTENT), null); |
| |
| //$FALL-THROUGH$ |
| case TOKEN_EMAIL_LOOKUP: { |
| if (cursor != null && cursor.moveToFirst()) { |
| long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX); |
| String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX); |
| lookupUri = Contacts.getLookupUri(contactId, lookupKey); |
| } |
| break; |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| mContactUri = lookupUri; |
| onContactUriChanged(); |
| |
| if (trigger && mContactUri != null) { |
| // Found contact, so trigger QuickContact |
| QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri, |
| mExcludeMimes, mPrioritizedMimeType); |
| } else if (createUri != null) { |
| // Prompt user to add this person to contacts |
| final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri); |
| if (extras != null) { |
| Bundle bundle = new Bundle(extras); |
| bundle.remove(EXTRA_URI_CONTENT); |
| intent.putExtras(bundle); |
| } |
| getContext().startActivity(intent); |
| } |
| } |
| } |
| } |