Home | History | Annotate | Download | only in quickcontact
      1 /*
      2  * Copyright (C) 2010 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.contacts.quickcontact;
     18 
     19 import com.android.contacts.ContactsUtils;
     20 import com.android.contacts.R;
     21 import com.android.contacts.model.AccountType.EditType;
     22 import com.android.contacts.model.DataKind;
     23 import com.android.contacts.util.Constants;
     24 import com.android.contacts.util.PhoneCapabilityTester;
     25 import com.android.contacts.util.StructuredPostalUtils;
     26 
     27 import android.content.ContentUris;
     28 import android.content.ContentValues;
     29 import android.content.Context;
     30 import android.content.Intent;
     31 import android.content.pm.PackageManager;
     32 import android.graphics.drawable.Drawable;
     33 import android.net.Uri;
     34 import android.net.WebAddress;
     35 import android.provider.ContactsContract.CommonDataKinds.Email;
     36 import android.provider.ContactsContract.CommonDataKinds.Im;
     37 import android.provider.ContactsContract.CommonDataKinds.Phone;
     38 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
     39 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
     40 import android.provider.ContactsContract.CommonDataKinds.Website;
     41 import android.provider.ContactsContract.Data;
     42 import android.text.TextUtils;
     43 import android.util.Log;
     44 
     45 /**
     46  * Description of a specific {@link Data#_ID} item, with style information
     47  * defined by a {@link DataKind}.
     48  */
     49 public class DataAction implements Action {
     50     private static final String TAG = "DataAction";
     51 
     52     private final Context mContext;
     53     private final DataKind mKind;
     54     private final String mMimeType;
     55 
     56     private CharSequence mBody;
     57     private CharSequence mSubtitle;
     58     private Intent mIntent;
     59     private Intent mAlternateIntent;
     60     private int mAlternateIconDescriptionRes;
     61     private int mAlternateIconRes;
     62     private int mPresence = -1;
     63 
     64     private Uri mDataUri;
     65     private long mDataId;
     66     private boolean mIsPrimary;
     67 
     68     /**
     69      * Create an action from common {@link Data} elements.
     70      */
     71     public DataAction(Context context, String mimeType, DataKind kind, long dataId,
     72             ContentValues entryValues) {
     73         mContext = context;
     74         mKind = kind;
     75         mMimeType = mimeType;
     76 
     77         // Determine type for subtitle
     78         mSubtitle = "";
     79         if (kind.typeColumn != null) {
     80             if (entryValues.containsKey(kind.typeColumn)) {
     81                 final int typeValue = entryValues.getAsInteger(kind.typeColumn);
     82 
     83                 // get type string
     84                 for (EditType type : kind.typeList) {
     85                     if (type.rawValue == typeValue) {
     86                         if (type.customColumn == null) {
     87                             // Non-custom type. Get its description from the resource
     88                             mSubtitle = context.getString(type.labelRes);
     89                         } else {
     90                             // Custom type. Read it from the database
     91                             mSubtitle = entryValues.getAsString(type.customColumn);
     92                         }
     93                         break;
     94                     }
     95                 }
     96             }
     97         }
     98 
     99         final Integer superPrimary = entryValues.getAsInteger(Data.IS_SUPER_PRIMARY);
    100         mIsPrimary = superPrimary != null && superPrimary != 0;
    101 
    102         if (mKind.actionBody != null) {
    103             mBody = mKind.actionBody.inflateUsing(context, entryValues);
    104         }
    105 
    106         mDataId = dataId;
    107         mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
    108 
    109         final boolean hasPhone = PhoneCapabilityTester.isPhone(mContext);
    110         final boolean hasSms = PhoneCapabilityTester.isSmsIntentRegistered(mContext);
    111 
    112         // Handle well-known MIME-types with special care
    113         if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
    114             if (PhoneCapabilityTester.isPhone(mContext)) {
    115                 final String number = entryValues.getAsString(Phone.NUMBER);
    116                 if (!TextUtils.isEmpty(number)) {
    117 
    118                     final Intent phoneIntent = hasPhone ? ContactsUtils.getCallIntent(number)
    119                             : null;
    120                     final Intent smsIntent = hasSms ? new Intent(Intent.ACTION_SENDTO,
    121                             Uri.fromParts(Constants.SCHEME_SMSTO, number, null)) : null;
    122 
    123                     // Configure Icons and Intents. Notice actionIcon is already set to the phone
    124                     if (hasPhone && hasSms) {
    125                         mIntent = phoneIntent;
    126                         mAlternateIntent = smsIntent;
    127                         mAlternateIconRes = kind.iconAltRes;
    128                         mAlternateIconDescriptionRes = kind.iconAltDescriptionRes;
    129                     } else if (hasPhone) {
    130                         mIntent = phoneIntent;
    131                     } else if (hasSms) {
    132                         mIntent = smsIntent;
    133                     }
    134                 }
    135             }
    136         } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
    137             if (PhoneCapabilityTester.isSipPhone(mContext)) {
    138                 final String address = entryValues.getAsString(SipAddress.SIP_ADDRESS);
    139                 if (!TextUtils.isEmpty(address)) {
    140                     final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
    141                     mIntent = ContactsUtils.getCallIntent(callUri);
    142                     // Note that this item will get a SIP-specific variant
    143                     // of the "call phone" icon, rather than the standard
    144                     // app icon for the Phone app (which we show for
    145                     // regular phone numbers.)  That's because the phone
    146                     // app explicitly specifies an android:icon attribute
    147                     // for the SIP-related intent-filters in its manifest.
    148                 }
    149             }
    150         } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
    151             final String address = entryValues.getAsString(Email.DATA);
    152             if (!TextUtils.isEmpty(address)) {
    153                 final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null);
    154                 mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
    155             }
    156 
    157         } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
    158             final String url = entryValues.getAsString(Website.URL);
    159             if (!TextUtils.isEmpty(url)) {
    160                 WebAddress webAddress = new WebAddress(url);
    161                 mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
    162             }
    163 
    164         } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
    165             final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
    166                     entryValues.getAsString(Data.MIMETYPE));
    167             if (isEmail || isProtocolValid(entryValues)) {
    168                 final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK :
    169                         entryValues.getAsInteger(Im.PROTOCOL);
    170 
    171                 if (isEmail) {
    172                     // Use Google Talk string when using Email, and clear data
    173                     // Uri so we don't try saving Email as primary.
    174                     mSubtitle = Im.getProtocolLabel(context.getResources(), Im.PROTOCOL_GOOGLE_TALK,
    175                             null);
    176                     mDataUri = null;
    177                 }
    178 
    179                 String host = entryValues.getAsString(Im.CUSTOM_PROTOCOL);
    180                 String data = entryValues.getAsString(isEmail ? Email.DATA : Im.DATA);
    181                 if (protocol != Im.PROTOCOL_CUSTOM) {
    182                     // Try bringing in a well-known host for specific protocols
    183                     host = ContactsUtils.lookupProviderNameFromId(protocol);
    184                 }
    185 
    186                 if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
    187                     final String authority = host.toLowerCase();
    188                     final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
    189                             authority).appendPath(data).build();
    190                     mIntent = new Intent(Intent.ACTION_SENDTO, imUri);
    191 
    192                     // If the address is also available for a video chat, we'll show the capability
    193                     // as a secondary action.
    194                     final Integer chatCapabilityObj = entryValues.getAsInteger(Im.CHAT_CAPABILITY);
    195                     final int chatCapability = chatCapabilityObj == null ? 0 : chatCapabilityObj;
    196                     final boolean isVideoChatCapable =
    197                             (chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0;
    198                     final boolean isAudioChatCapable =
    199                             (chatCapability & Im.CAPABILITY_HAS_VOICE) != 0;
    200                     if (isVideoChatCapable || isAudioChatCapable) {
    201                         mAlternateIntent = new Intent(
    202                                 Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
    203                         if (isVideoChatCapable) {
    204                             mAlternateIconRes = R.drawable.sym_action_videochat_holo_light;
    205                             mAlternateIconDescriptionRes = R.string.video_chat;
    206                         } else {
    207                             mAlternateIconRes = R.drawable.sym_action_audiochat_holo_light;
    208                             mAlternateIconDescriptionRes = R.string.audio_chat;
    209                         }
    210                     }
    211                 }
    212             }
    213         } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
    214             final String postalAddress =
    215                     entryValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
    216             if (!TextUtils.isEmpty(postalAddress)) {
    217                 mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
    218             }
    219         }
    220 
    221         if (mIntent == null) {
    222             // Otherwise fall back to default VIEW action
    223             mIntent = new Intent(Intent.ACTION_VIEW);
    224             mIntent.setDataAndType(mDataUri, mimeType);
    225         }
    226 
    227         mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    228     }
    229 
    230     @Override
    231     public int getPresence() {
    232         return mPresence;
    233     }
    234 
    235     public void setPresence(int presence) {
    236         mPresence = presence;
    237     }
    238 
    239     private boolean isProtocolValid(ContentValues entryValues) {
    240         final String protocol = entryValues.getAsString(Im.PROTOCOL);
    241         if (protocol == null) {
    242             return false;
    243         }
    244         try {
    245             Integer.valueOf(protocol);
    246         } catch (NumberFormatException e) {
    247             return false;
    248         }
    249         return true;
    250     }
    251 
    252     @Override
    253     public CharSequence getSubtitle() {
    254         return mSubtitle;
    255     }
    256 
    257     @Override
    258     public CharSequence getBody() {
    259         return mBody;
    260     }
    261 
    262     @Override
    263     public String getMimeType() {
    264         return mMimeType;
    265     }
    266 
    267     @Override
    268     public Uri getDataUri() {
    269         return mDataUri;
    270     }
    271 
    272     @Override
    273     public long getDataId() {
    274         return mDataId;
    275     }
    276 
    277     @Override
    278     public Boolean isPrimary() {
    279         return mIsPrimary;
    280     }
    281 
    282     @Override
    283     public Drawable getAlternateIcon() {
    284         if (mAlternateIconRes == 0) return null;
    285 
    286         final String resourcePackageName = mKind.resourcePackageName;
    287         if (resourcePackageName == null) {
    288             return mContext.getResources().getDrawable(mAlternateIconRes);
    289         }
    290 
    291         final PackageManager pm = mContext.getPackageManager();
    292         return pm.getDrawable(resourcePackageName, mAlternateIconRes, null);
    293     }
    294 
    295     @Override
    296     public String getAlternateIconDescription() {
    297         if (mAlternateIconDescriptionRes == 0) return null;
    298         return mContext.getResources().getString(mAlternateIconDescriptionRes);
    299     }
    300 
    301     @Override
    302     public Intent getIntent() {
    303         return mIntent;
    304     }
    305 
    306     @Override
    307     public Intent getAlternateIntent() {
    308         return mAlternateIntent;
    309     }
    310 
    311     @Override
    312     public boolean collapseWith(Action other) {
    313         if (!shouldCollapseWith(other)) {
    314             return false;
    315         }
    316         return true;
    317     }
    318 
    319     @Override
    320     public boolean shouldCollapseWith(Action t) {
    321         if (t == null) {
    322             return false;
    323         }
    324         if (!(t instanceof DataAction)) {
    325             Log.e(TAG, "t must be DataAction");
    326             return false;
    327         }
    328         DataAction that = (DataAction)t;
    329         if (!ContactsUtils.shouldCollapse(mMimeType, mBody, that.mMimeType, that.mBody)) {
    330             return false;
    331         }
    332         if (!TextUtils.equals(mMimeType, that.mMimeType)
    333                 || !ContactsUtils.areIntentActionEqual(mIntent, that.mIntent)) {
    334             return false;
    335         }
    336         return true;
    337     }
    338 }
    339