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