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