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