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