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