Home | History | Annotate | Download | only in util
      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 com.android.loaderapp.util;
     18 
     19 
     20 
     21 import android.content.ContentResolver;
     22 import android.content.ContentUris;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.database.Cursor;
     27 import android.graphics.Bitmap;
     28 import android.graphics.BitmapFactory;
     29 import android.net.Uri;
     30 import android.provider.ContactsContract.Contacts;
     31 import android.provider.ContactsContract.Data;
     32 import android.provider.ContactsContract.RawContacts;
     33 import android.provider.ContactsContract.CommonDataKinds.Email;
     34 import android.provider.ContactsContract.CommonDataKinds.Im;
     35 import android.provider.ContactsContract.CommonDataKinds.Organization;
     36 import android.provider.ContactsContract.CommonDataKinds.Phone;
     37 import android.provider.ContactsContract.CommonDataKinds.Photo;
     38 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
     39 import android.telephony.PhoneNumberUtils;
     40 import android.text.TextUtils;
     41 
     42 import java.util.ArrayList;
     43 
     44 public class ContactsUtils {
     45     private static final String TAG = "ContactsUtils";
     46     /**
     47      * Build the display title for the {@link Data#CONTENT_URI} entry in the
     48      * provided cursor, assuming the given mimeType.
     49      */
     50     public static final CharSequence getDisplayLabel(Context context,
     51             String mimeType, Cursor cursor) {
     52         // Try finding the type and label for this mimetype
     53         int colType;
     54         int colLabel;
     55 
     56         if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
     57                 || Constants.MIME_SMS_ADDRESS.equals(mimeType)) {
     58             // Reset to phone mimetype so we generate a label for SMS case
     59             mimeType = Phone.CONTENT_ITEM_TYPE;
     60             colType = cursor.getColumnIndex(Phone.TYPE);
     61             colLabel = cursor.getColumnIndex(Phone.LABEL);
     62         } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
     63             colType = cursor.getColumnIndex(Email.TYPE);
     64             colLabel = cursor.getColumnIndex(Email.LABEL);
     65         } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
     66             colType = cursor.getColumnIndex(StructuredPostal.TYPE);
     67             colLabel = cursor.getColumnIndex(StructuredPostal.LABEL);
     68         } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
     69             colType = cursor.getColumnIndex(Organization.TYPE);
     70             colLabel = cursor.getColumnIndex(Organization.LABEL);
     71         } else {
     72             return null;
     73         }
     74 
     75         final int type = cursor.getInt(colType);
     76         final CharSequence label = cursor.getString(colLabel);
     77 
     78         return getDisplayLabel(context, mimeType, type, label);
     79     }
     80 
     81     public static final CharSequence getDisplayLabel(Context context, String mimetype, int type,
     82             CharSequence label) {
     83         CharSequence display = "";
     84         final int customType;
     85         final int defaultType;
     86         final int arrayResId;
     87 
     88         if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
     89             defaultType = Phone.TYPE_HOME;
     90             customType = Phone.TYPE_CUSTOM;
     91             arrayResId = com.android.internal.R.array.phoneTypes;
     92         } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
     93             defaultType = Email.TYPE_HOME;
     94             customType = Email.TYPE_CUSTOM;
     95             arrayResId = com.android.internal.R.array.emailAddressTypes;
     96         } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimetype)) {
     97             defaultType = StructuredPostal.TYPE_HOME;
     98             customType = StructuredPostal.TYPE_CUSTOM;
     99             arrayResId = com.android.internal.R.array.postalAddressTypes;
    100         } else if (Organization.CONTENT_ITEM_TYPE.equals(mimetype)) {
    101             defaultType = Organization.TYPE_WORK;
    102             customType = Organization.TYPE_CUSTOM;
    103             arrayResId = com.android.internal.R.array.organizationTypes;
    104         } else {
    105             // Can't return display label for given mimetype.
    106             return display;
    107         }
    108 
    109         if (type != customType) {
    110             CharSequence[] labels = context.getResources().getTextArray(arrayResId);
    111             try {
    112                 display = labels[type - 1];
    113             } catch (ArrayIndexOutOfBoundsException e) {
    114                 display = labels[defaultType - 1];
    115             }
    116         } else {
    117             if (!TextUtils.isEmpty(label)) {
    118                 display = label;
    119             }
    120         }
    121         return display;
    122     }
    123 
    124     /**
    125      * Opens an InputStream for the person's photo and returns the photo as a Bitmap.
    126      * If the person's photo isn't present returns null.
    127      *
    128      * @param aggCursor the Cursor pointing to the data record containing the photo.
    129      * @param bitmapColumnIndex the column index where the photo Uri is stored.
    130      * @param options the decoding options, can be set to null
    131      * @return the photo Bitmap
    132      */
    133     public static Bitmap loadContactPhoto(Cursor cursor, int bitmapColumnIndex,
    134             BitmapFactory.Options options) {
    135         if (cursor == null) {
    136             return null;
    137         }
    138 
    139         byte[] data = cursor.getBlob(bitmapColumnIndex);
    140         return BitmapFactory.decodeByteArray(data, 0, data.length, options);
    141     }
    142 
    143     /**
    144      * Loads a placeholder photo.
    145      *
    146      * @param placeholderImageResource the resource to use for the placeholder image
    147      * @param context the Context
    148      * @param options the decoding options, can be set to null
    149      * @return the placeholder Bitmap.
    150      */
    151     public static Bitmap loadPlaceholderPhoto(int placeholderImageResource, Context context,
    152             BitmapFactory.Options options) {
    153         if (placeholderImageResource == 0) {
    154             return null;
    155         }
    156         return BitmapFactory.decodeResource(context.getResources(),
    157                 placeholderImageResource, options);
    158     }
    159 
    160     public static Bitmap loadContactPhoto(Context context, long photoId,
    161             BitmapFactory.Options options) {
    162         Cursor photoCursor = null;
    163         Bitmap photoBm = null;
    164 
    165         try {
    166             photoCursor = context.getContentResolver().query(
    167                     ContentUris.withAppendedId(Data.CONTENT_URI, photoId),
    168                     new String[] { Photo.PHOTO },
    169                     null, null, null);
    170 
    171             if (photoCursor.moveToFirst() && !photoCursor.isNull(0)) {
    172                 byte[] photoData = photoCursor.getBlob(0);
    173                 photoBm = BitmapFactory.decodeByteArray(photoData, 0,
    174                         photoData.length, options);
    175             }
    176         } finally {
    177             if (photoCursor != null) {
    178                 photoCursor.close();
    179             }
    180         }
    181 
    182         return photoBm;
    183     }
    184 
    185     // TODO find a proper place for the canonical version of these
    186     public interface ProviderNames {
    187         String YAHOO = "Yahoo";
    188         String GTALK = "GTalk";
    189         String MSN = "MSN";
    190         String ICQ = "ICQ";
    191         String AIM = "AIM";
    192         String XMPP = "XMPP";
    193         String JABBER = "JABBER";
    194         String SKYPE = "SKYPE";
    195         String QQ = "QQ";
    196     }
    197 
    198     /**
    199      * This looks up the provider name defined in
    200      * ProviderNames from the predefined IM protocol id.
    201      * This is used for interacting with the IM application.
    202      *
    203      * @param protocol the protocol ID
    204      * @return the provider name the IM app uses for the given protocol, or null if no
    205      * provider is defined for the given protocol
    206      * @hide
    207      */
    208     public static String lookupProviderNameFromId(int protocol) {
    209         switch (protocol) {
    210             case Im.PROTOCOL_GOOGLE_TALK:
    211                 return ProviderNames.GTALK;
    212             case Im.PROTOCOL_AIM:
    213                 return ProviderNames.AIM;
    214             case Im.PROTOCOL_MSN:
    215                 return ProviderNames.MSN;
    216             case Im.PROTOCOL_YAHOO:
    217                 return ProviderNames.YAHOO;
    218             case Im.PROTOCOL_ICQ:
    219                 return ProviderNames.ICQ;
    220             case Im.PROTOCOL_JABBER:
    221                 return ProviderNames.JABBER;
    222             case Im.PROTOCOL_SKYPE:
    223                 return ProviderNames.SKYPE;
    224             case Im.PROTOCOL_QQ:
    225                 return ProviderNames.QQ;
    226         }
    227         return null;
    228     }
    229 
    230     /**
    231      * Build {@link Intent} to launch an action for the given {@link Im} or
    232      * {@link Email} row. Returns null when missing protocol or data.
    233      */
    234     public static Intent buildImIntent(ContentValues values) {
    235         final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(values.getAsString(Data.MIMETYPE));
    236 
    237         if (!isEmail && !isProtocolValid(values)) {
    238             return null;
    239         }
    240 
    241         final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : values.getAsInteger(Im.PROTOCOL);
    242 
    243         String host = values.getAsString(Im.CUSTOM_PROTOCOL);
    244         String data = values.getAsString(isEmail ? Email.DATA : Im.DATA);
    245         if (protocol != Im.PROTOCOL_CUSTOM) {
    246             // Try bringing in a well-known host for specific protocols
    247             host = ContactsUtils.lookupProviderNameFromId(protocol);
    248         }
    249 
    250         if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
    251             final String authority = host.toLowerCase();
    252             final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
    253                     authority).appendPath(data).build();
    254             return new Intent(Intent.ACTION_SENDTO, imUri);
    255         } else {
    256             return null;
    257         }
    258     }
    259 
    260     private static boolean isProtocolValid(ContentValues values) {
    261         String protocolString = values.getAsString(Im.PROTOCOL);
    262         if (protocolString == null) {
    263             return false;
    264         }
    265         try {
    266             Integer.valueOf(protocolString);
    267         } catch (NumberFormatException e) {
    268             return false;
    269         }
    270         return true;
    271     }
    272 
    273     public static long queryForContactId(ContentResolver cr, long rawContactId) {
    274         Cursor contactIdCursor = null;
    275         long contactId = -1;
    276         try {
    277             contactIdCursor = cr.query(RawContacts.CONTENT_URI,
    278                     new String[] {RawContacts.CONTACT_ID},
    279                     RawContacts._ID + "=" + rawContactId, null, null);
    280             if (contactIdCursor != null && contactIdCursor.moveToFirst()) {
    281                 contactId = contactIdCursor.getLong(0);
    282             }
    283         } finally {
    284             if (contactIdCursor != null) {
    285                 contactIdCursor.close();
    286             }
    287         }
    288         return contactId;
    289     }
    290 
    291     public static String querySuperPrimaryPhone(ContentResolver cr, long contactId) {
    292         Cursor c = null;
    293         String phone = null;
    294         try {
    295             Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
    296             Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
    297 
    298             c = cr.query(dataUri,
    299                     new String[] {Phone.NUMBER},
    300                     Data.MIMETYPE + "=" + Phone.MIMETYPE +
    301                         " AND " + Data.IS_SUPER_PRIMARY + "=1",
    302                     null, null);
    303             if (c != null && c.moveToFirst()) {
    304                 // Just return the first one.
    305                 phone = c.getString(0);
    306             }
    307         } finally {
    308             if (c != null) {
    309                 c.close();
    310             }
    311         }
    312         return phone;
    313     }
    314 
    315     public static long queryForRawContactId(ContentResolver cr, long contactId) {
    316         Cursor rawContactIdCursor = null;
    317         long rawContactId = -1;
    318         try {
    319             rawContactIdCursor = cr.query(RawContacts.CONTENT_URI,
    320                     new String[] {RawContacts._ID},
    321                     RawContacts.CONTACT_ID + "=" + contactId, null, null);
    322             if (rawContactIdCursor != null && rawContactIdCursor.moveToFirst()) {
    323                 // Just return the first one.
    324                 rawContactId = rawContactIdCursor.getLong(0);
    325             }
    326         } finally {
    327             if (rawContactIdCursor != null) {
    328                 rawContactIdCursor.close();
    329             }
    330         }
    331         return rawContactId;
    332     }
    333 
    334     public static ArrayList<Long> queryForAllRawContactIds(ContentResolver cr, long contactId) {
    335         Cursor rawContactIdCursor = null;
    336         ArrayList<Long> rawContactIds = new ArrayList<Long>();
    337         try {
    338             rawContactIdCursor = cr.query(RawContacts.CONTENT_URI,
    339                     new String[] {RawContacts._ID},
    340                     RawContacts.CONTACT_ID + "=" + contactId, null, null);
    341             if (rawContactIdCursor != null) {
    342                 while (rawContactIdCursor.moveToNext()) {
    343                     rawContactIds.add(rawContactIdCursor.getLong(0));
    344                 }
    345             }
    346         } finally {
    347             if (rawContactIdCursor != null) {
    348                 rawContactIdCursor.close();
    349             }
    350         }
    351         return rawContactIds;
    352     }
    353 
    354 
    355     /**
    356      * Kick off an intent to initiate a call.
    357      *
    358      * @param phoneNumber must not be null.
    359      * @throws NullPointerException when the given argument is null.
    360      */
    361     public static void initiateCall(Context context, CharSequence phoneNumber) {
    362         Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
    363                 Uri.fromParts("tel", phoneNumber.toString(), null));
    364         context.startActivity(intent);
    365     }
    366 
    367     /**
    368      * Kick off an intent to initiate an Sms/Mms message.
    369      *
    370      * @param phoneNumber must not be null.
    371      * @throws NullPointerException when the given argument is null.
    372      */
    373     public static void initiateSms(Context context, CharSequence phoneNumber) {
    374         Intent intent = new Intent(Intent.ACTION_SENDTO,
    375                 Uri.fromParts("sms", phoneNumber.toString(), null));
    376         context.startActivity(intent);
    377     }
    378 
    379     /**
    380      * Test if the given {@link CharSequence} contains any graphic characters,
    381      * first checking {@link TextUtils#isEmpty(CharSequence)} to handle null.
    382      */
    383     public static boolean isGraphic(CharSequence str) {
    384         return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str);
    385     }
    386 
    387     /**
    388      * Returns true if two objects are considered equal.  Two null references are equal here.
    389      */
    390     public static boolean areObjectsEqual(Object a, Object b) {
    391         return a == b || (a != null && a.equals(b));
    392     }
    393 
    394     /**
    395      * Returns true if two data with mimetypes which represent values in contact entries are
    396      * considered equal.
    397      */
    398     public static final boolean areDataEqual(Context context, CharSequence mimetype1,
    399             CharSequence data1, CharSequence mimetype2, CharSequence data2) {
    400         if (TextUtils.equals(Phone.CONTENT_ITEM_TYPE, mimetype1)
    401                 && TextUtils.equals(Phone.CONTENT_ITEM_TYPE, mimetype2)) {
    402             if (data1 == data2) {
    403                 return true;
    404             }
    405             if (data1 == null || data2 == null) {
    406                 return false;
    407             }
    408             return PhoneNumberUtils.compare(context, data1.toString(), data2.toString());
    409         } else {
    410             if (mimetype1 == mimetype2 && data1 == data2) {
    411                 return true;
    412             }
    413             return TextUtils.equals(mimetype1, mimetype2) && TextUtils.equals(data1, data2);
    414         }
    415     }
    416 
    417     /**
    418      * Returns true if two {@link Intent}s are both null, or have the same action.
    419      */
    420     public static final boolean areIntentActionEqual(Intent a, Intent b) {
    421         if (a == b) {
    422             return true;
    423         }
    424         if (a == null || b == null) {
    425             return false;
    426         }
    427         return TextUtils.equals(a.getAction(), b.getAction());
    428     }
    429 }
    430