Home | History | Annotate | Download | only in widget
      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.os.Bundle;
     31 import android.provider.ContactsContract.CommonDataKinds.Email;
     32 import android.provider.ContactsContract.Contacts;
     33 import android.provider.ContactsContract.Intents;
     34 import android.provider.ContactsContract.PhoneLookup;
     35 import android.provider.ContactsContract.QuickContact;
     36 import android.provider.ContactsContract.RawContacts;
     37 import android.util.AttributeSet;
     38 import android.view.View;
     39 import android.view.View.OnClickListener;
     40 import android.view.accessibility.AccessibilityEvent;
     41 import android.view.accessibility.AccessibilityNodeInfo;
     42 
     43 /**
     44  * Widget used to show an image with the standard QuickContact badge
     45  * and on-click behavior.
     46  */
     47 public class QuickContactBadge extends ImageView implements OnClickListener {
     48     private Uri mContactUri;
     49     private String mContactEmail;
     50     private String mContactPhone;
     51     private Drawable mOverlay;
     52     private QueryHandler mQueryHandler;
     53     private Drawable mDefaultAvatar;
     54     private Bundle mExtras = null;
     55 
     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 
     63     static final private String EXTRA_URI_CONTENT = "uri_content";
     64 
     65     static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
     66         RawContacts.CONTACT_ID,
     67         Contacts.LOOKUP_KEY,
     68     };
     69     static final int EMAIL_ID_COLUMN_INDEX = 0;
     70     static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1;
     71 
     72     static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
     73         PhoneLookup._ID,
     74         PhoneLookup.LOOKUP_KEY,
     75     };
     76     static final int PHONE_ID_COLUMN_INDEX = 0;
     77     static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1;
     78 
     79     public QuickContactBadge(Context context) {
     80         this(context, null);
     81     }
     82 
     83     public QuickContactBadge(Context context, AttributeSet attrs) {
     84         this(context, attrs, 0);
     85     }
     86 
     87     public QuickContactBadge(Context context, AttributeSet attrs, int defStyle) {
     88         super(context, attrs, defStyle);
     89 
     90         TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme);
     91         mOverlay = styledAttributes.getDrawable(
     92                 com.android.internal.R.styleable.Theme_quickContactBadgeOverlay);
     93         styledAttributes.recycle();
     94 
     95         mQueryHandler = new QueryHandler(mContext.getContentResolver());
     96         setOnClickListener(this);
     97     }
     98 
     99     @Override
    100     protected void drawableStateChanged() {
    101         super.drawableStateChanged();
    102         if (mOverlay != null && mOverlay.isStateful()) {
    103             mOverlay.setState(getDrawableState());
    104             invalidate();
    105         }
    106     }
    107 
    108     /** This call has no effect anymore, as there is only one QuickContact mode */
    109     @SuppressWarnings("unused")
    110     public void setMode(int size) {
    111     }
    112 
    113     @Override
    114     protected void onDraw(Canvas canvas) {
    115         super.onDraw(canvas);
    116 
    117         if (!isEnabled()) {
    118             // not clickable? don't show triangle
    119             return;
    120         }
    121 
    122         if (mOverlay == null || mOverlay.getIntrinsicWidth() == 0 ||
    123                 mOverlay.getIntrinsicHeight() == 0) {
    124             // nothing to draw
    125             return;
    126         }
    127 
    128         mOverlay.setBounds(0, 0, getWidth(), getHeight());
    129 
    130         if (mPaddingTop == 0 && mPaddingLeft == 0) {
    131             mOverlay.draw(canvas);
    132         } else {
    133             int saveCount = canvas.getSaveCount();
    134             canvas.save();
    135             canvas.translate(mPaddingLeft, mPaddingTop);
    136             mOverlay.draw(canvas);
    137             canvas.restoreToCount(saveCount);
    138         }
    139     }
    140 
    141     /** True if a contact, an email address or a phone number has been assigned */
    142     private boolean isAssigned() {
    143         return mContactUri != null || mContactEmail != null || mContactPhone != null;
    144     }
    145 
    146     /**
    147      * Resets the contact photo to the default state.
    148      */
    149     public void setImageToDefault() {
    150         if (mDefaultAvatar == null) {
    151             mDefaultAvatar = getResources().getDrawable(R.drawable.ic_contact_picture);
    152         }
    153         setImageDrawable(mDefaultAvatar);
    154     }
    155 
    156     /**
    157      * Assign the contact uri that this QuickContactBadge should be associated
    158      * with. Note that this is only used for displaying the QuickContact window and
    159      * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the
    160      * photo.
    161      *
    162      * @param contactUri Either a {@link Contacts#CONTENT_URI} or
    163      *            {@link Contacts#CONTENT_LOOKUP_URI} style URI.
    164      */
    165     public void assignContactUri(Uri contactUri) {
    166         mContactUri = contactUri;
    167         mContactEmail = null;
    168         mContactPhone = null;
    169         onContactUriChanged();
    170     }
    171 
    172     /**
    173      * Assign a contact based on an email address. This should only be used when
    174      * the contact's URI is not available, as an extra query will have to be
    175      * performed to lookup the URI based on the email.
    176      *
    177      * @param emailAddress The email address of the contact.
    178      * @param lazyLookup If this is true, the lookup query will not be performed
    179      * until this view is clicked.
    180      */
    181     public void assignContactFromEmail(String emailAddress, boolean lazyLookup) {
    182         assignContactFromEmail(emailAddress, lazyLookup, null);
    183     }
    184 
    185     /**
    186      * Assign a contact based on an email address. This should only be used when
    187      * the contact's URI is not available, as an extra query will have to be
    188      * performed to lookup the URI based on the email.
    189 
    190      @param emailAddress The email address of the contact.
    191      @param lazyLookup If this is true, the lookup query will not be performed
    192      until this view is clicked.
    193      @param extras A bundle of extras to populate the contact edit page with if the contact
    194      is not found and the user chooses to add the email address to an existing contact or
    195      create a new contact. Uses the same string constants as those found in
    196      {@link android.provider.ContactsContract.Intents.Insert}
    197     */
    198 
    199     public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) {
    200         mContactEmail = emailAddress;
    201         mExtras = extras;
    202         if (!lazyLookup) {
    203             mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null,
    204                     Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
    205                     EMAIL_LOOKUP_PROJECTION, null, null, null);
    206         } else {
    207             mContactUri = null;
    208             onContactUriChanged();
    209         }
    210     }
    211 
    212 
    213     /**
    214      * Assign a contact based on a phone number. This should only be used when
    215      * the contact's URI is not available, as an extra query will have to be
    216      * performed to lookup the URI based on the phone number.
    217      *
    218      * @param phoneNumber The phone number of the contact.
    219      * @param lazyLookup If this is true, the lookup query will not be performed
    220      * until this view is clicked.
    221      */
    222     public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) {
    223         assignContactFromPhone(phoneNumber, lazyLookup, new Bundle());
    224     }
    225 
    226     /**
    227      * Assign a contact based on a phone number. This should only be used when
    228      * the contact's URI is not available, as an extra query will have to be
    229      * performed to lookup the URI based on the phone number.
    230      *
    231      * @param phoneNumber The phone number of the contact.
    232      * @param lazyLookup If this is true, the lookup query will not be performed
    233      * until this view is clicked.
    234      * @param extras A bundle of extras to populate the contact edit page with if the contact
    235      * is not found and the user chooses to add the phone number to an existing contact or
    236      * create a new contact. Uses the same string constants as those found in
    237      * {@link android.provider.ContactsContract.Intents.Insert}
    238      */
    239     public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) {
    240         mContactPhone = phoneNumber;
    241         mExtras = extras;
    242         if (!lazyLookup) {
    243             mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null,
    244                     Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
    245                     PHONE_LOOKUP_PROJECTION, null, null, null);
    246         } else {
    247             mContactUri = null;
    248             onContactUriChanged();
    249         }
    250     }
    251 
    252     private void onContactUriChanged() {
    253         setEnabled(isAssigned());
    254     }
    255 
    256     @Override
    257     public void onClick(View v) {
    258         // If contact has been assigned, mExtras should no longer be null, but do a null check
    259         // anyway just in case assignContactFromPhone or Email was called with a null bundle or
    260         // wasn't assigned previously.
    261         final Bundle extras = (mExtras == null) ? new Bundle() : mExtras;
    262         if (mContactUri != null) {
    263             QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri,
    264                     QuickContact.MODE_LARGE, mExcludeMimes);
    265         } else if (mContactEmail != null) {
    266             extras.putString(EXTRA_URI_CONTENT, mContactEmail);
    267             mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras,
    268                     Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
    269                     EMAIL_LOOKUP_PROJECTION, null, null, null);
    270         } else if (mContactPhone != null) {
    271             extras.putString(EXTRA_URI_CONTENT, mContactPhone);
    272             mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras,
    273                     Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
    274                     PHONE_LOOKUP_PROJECTION, null, null, null);
    275         } else {
    276             // If a contact hasn't been assigned, don't react to click.
    277             return;
    278         }
    279     }
    280 
    281     @Override
    282     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    283         super.onInitializeAccessibilityEvent(event);
    284         event.setClassName(QuickContactBadge.class.getName());
    285     }
    286 
    287     @Override
    288     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    289         super.onInitializeAccessibilityNodeInfo(info);
    290         info.setClassName(QuickContactBadge.class.getName());
    291     }
    292 
    293     /**
    294      * Set a list of specific MIME-types to exclude and not display. For
    295      * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE}
    296      * profile icon.
    297      */
    298     public void setExcludeMimes(String[] excludeMimes) {
    299         mExcludeMimes = excludeMimes;
    300     }
    301 
    302     private class QueryHandler extends AsyncQueryHandler {
    303 
    304         public QueryHandler(ContentResolver cr) {
    305             super(cr);
    306         }
    307 
    308         @Override
    309         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    310             Uri lookupUri = null;
    311             Uri createUri = null;
    312             boolean trigger = false;
    313             Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
    314             try {
    315                 switch(token) {
    316                     case TOKEN_PHONE_LOOKUP_AND_TRIGGER:
    317                         trigger = true;
    318                         createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null);
    319 
    320                         //$FALL-THROUGH$
    321                     case TOKEN_PHONE_LOOKUP: {
    322                         if (cursor != null && cursor.moveToFirst()) {
    323                             long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
    324                             String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX);
    325                             lookupUri = Contacts.getLookupUri(contactId, lookupKey);
    326                         }
    327 
    328                         break;
    329                     }
    330                     case TOKEN_EMAIL_LOOKUP_AND_TRIGGER:
    331                         trigger = true;
    332                         createUri = Uri.fromParts("mailto",
    333                                 extras.getString(EXTRA_URI_CONTENT), null);
    334 
    335                         //$FALL-THROUGH$
    336                     case TOKEN_EMAIL_LOOKUP: {
    337                         if (cursor != null && cursor.moveToFirst()) {
    338                             long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX);
    339                             String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX);
    340                             lookupUri = Contacts.getLookupUri(contactId, lookupKey);
    341                         }
    342                         break;
    343                     }
    344                 }
    345             } finally {
    346                 if (cursor != null) {
    347                     cursor.close();
    348                 }
    349             }
    350 
    351             mContactUri = lookupUri;
    352             onContactUriChanged();
    353 
    354             if (trigger && lookupUri != null) {
    355                 // Found contact, so trigger QuickContact
    356                 QuickContact.showQuickContact(getContext(), QuickContactBadge.this, lookupUri,
    357                         QuickContact.MODE_LARGE, mExcludeMimes);
    358             } else if (createUri != null) {
    359                 // Prompt user to add this person to contacts
    360                 final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
    361                 if (extras != null) {
    362                     extras.remove(EXTRA_URI_CONTENT);
    363                     intent.putExtras(extras);
    364                 }
    365                 getContext().startActivity(intent);
    366             }
    367         }
    368     }
    369 }
    370