/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contacts.quickcontact; import com.android.contacts.ContactsUtils; import com.android.contacts.R; import com.android.contacts.model.AccountType.EditType; import com.android.contacts.model.DataKind; import com.android.contacts.util.Constants; import com.android.contacts.util.StructuredPostalUtils; import com.android.contacts.util.PhoneCapabilityTester; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.WebAddress; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Im; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.SipAddress; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; import android.provider.ContactsContract.Data; import android.text.TextUtils; import android.util.Log; /** * Description of a specific {@link Data#_ID} item, with style information * defined by a {@link DataKind}. */ public class DataAction implements Action { private static final String TAG = "DataAction"; private final Context mContext; private final DataKind mKind; private final String mMimeType; private CharSequence mBody; private CharSequence mSubtitle; private Intent mIntent; private Intent mAlternateIntent; private int mAlternateIconDescriptionRes; private int mAlternateIconRes; private Uri mDataUri; private long mDataId; private boolean mIsPrimary; /** * Create an action from common {@link Data} elements. */ public DataAction(Context context, String mimeType, DataKind kind, long dataId, Cursor cursor) { mContext = context; mKind = kind; mMimeType = mimeType; // Determine type for subtitle mSubtitle = ""; if (kind.typeColumn != null) { final int typeColumnIndex = cursor.getColumnIndex(kind.typeColumn); if (typeColumnIndex != -1) { final int typeValue = cursor.getInt(typeColumnIndex); // get type string for (EditType type : kind.typeList) { if (type.rawValue == typeValue) { if (type.customColumn == null) { // Non-custom type. Get its description from the resource mSubtitle = context.getString(type.labelRes); } else { // Custom type. Read it from the database mSubtitle = cursor.getString(cursor.getColumnIndexOrThrow( type.customColumn)); } break; } } } } if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) { mIsPrimary = true; } if (mKind.actionBody != null) { mBody = mKind.actionBody.inflateUsing(context, cursor); } mDataId = dataId; mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId); final boolean hasPhone = PhoneCapabilityTester.isPhone(mContext); final boolean hasSms = PhoneCapabilityTester.isSmsIntentRegistered(mContext); // Handle well-known MIME-types with special care if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) { if (PhoneCapabilityTester.isPhone(mContext)) { final String number = getAsString(cursor, Phone.NUMBER); if (!TextUtils.isEmpty(number)) { final Intent phoneIntent = hasPhone ? new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(Constants.SCHEME_TEL, number, null)) : null; final Intent smsIntent = hasSms ? new Intent(Intent.ACTION_SENDTO, Uri.fromParts(Constants.SCHEME_SMSTO, number, null)) : null; // Configure Icons and Intents. Notice actionIcon is already set to the phone if (hasPhone && hasSms) { mIntent = phoneIntent; mAlternateIntent = smsIntent; mAlternateIconRes = kind.iconAltRes; mAlternateIconDescriptionRes = kind.iconAltDescriptionRes; } else if (hasPhone) { mIntent = phoneIntent; } else if (hasSms) { mIntent = smsIntent; } } } } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) { if (PhoneCapabilityTester.isSipPhone(mContext)) { final String address = getAsString(cursor, SipAddress.SIP_ADDRESS); if (!TextUtils.isEmpty(address)) { final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null); mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri); // Note that this item will get a SIP-specific variant // of the "call phone" icon, rather than the standard // app icon for the Phone app (which we show for // regular phone numbers.) That's because the phone // app explicitly specifies an android:icon attribute // for the SIP-related intent-filters in its manifest. } } } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) { final String address = getAsString(cursor, Email.DATA); if (!TextUtils.isEmpty(address)) { final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null); mIntent = new Intent(Intent.ACTION_SENDTO, mailUri); } } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) { final String url = getAsString(cursor, Website.URL); if (!TextUtils.isEmpty(url)) { WebAddress webAddress = new WebAddress(url); mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString())); } } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) { final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals( getAsString(cursor, Data.MIMETYPE)); if (isEmail || isProtocolValid(cursor)) { final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : getAsInt(cursor, Im.PROTOCOL); if (isEmail) { // Use Google Talk string when using Email, and clear data // Uri so we don't try saving Email as primary. mSubtitle = context.getText(R.string.chat_gtalk); mDataUri = null; } String host = getAsString(cursor, Im.CUSTOM_PROTOCOL); String data = getAsString(cursor, isEmail ? Email.DATA : Im.DATA); if (protocol != Im.PROTOCOL_CUSTOM) { // Try bringing in a well-known host for specific protocols host = ContactsUtils.lookupProviderNameFromId(protocol); } if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) { final String authority = host.toLowerCase(); final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority( authority).appendPath(data).build(); mIntent = new Intent(Intent.ACTION_SENDTO, imUri); // If the address is also available for a video chat, we'll show the capability // as a secondary action. final int chatCapability = getAsInt(cursor, Data.CHAT_CAPABILITY); final boolean isVideoChatCapable = (chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0; final boolean isAudioChatCapable = (chatCapability & Im.CAPABILITY_HAS_VOICE) != 0; if (isVideoChatCapable || isAudioChatCapable) { mAlternateIntent = new Intent( Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call")); if (isVideoChatCapable) { mAlternateIconRes = R.drawable.sym_action_videochat_holo_light; mAlternateIconDescriptionRes = R.string.video_chat; } else { mAlternateIconRes = R.drawable.sym_action_audiochat_holo_light; mAlternateIconDescriptionRes = R.string.audio_chat; } } } } } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { final String postalAddress = getAsString(cursor, StructuredPostal.FORMATTED_ADDRESS); if (!TextUtils.isEmpty(postalAddress)) { mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress); } } if (mIntent == null) { // Otherwise fall back to default VIEW action mIntent = new Intent(Intent.ACTION_VIEW); mIntent.setDataAndType(mDataUri, mimeType); } // Always launch as new task, since we're like a launcher mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); } /** Read {@link String} from the given {@link Cursor}. */ private static String getAsString(Cursor cursor, String columnName) { final int index = cursor.getColumnIndex(columnName); return cursor.getString(index); } /** Read {@link Integer} from the given {@link Cursor}. */ private static int getAsInt(Cursor cursor, String columnName) { final int index = cursor.getColumnIndex(columnName); return cursor.getInt(index); } private boolean isProtocolValid(Cursor cursor) { final int columnIndex = cursor.getColumnIndex(Im.PROTOCOL); if (cursor.isNull(columnIndex)) { return false; } try { Integer.valueOf(cursor.getString(columnIndex)); } catch (NumberFormatException e) { return false; } return true; } @Override public CharSequence getSubtitle() { return mSubtitle; } @Override public CharSequence getBody() { return mBody; } @Override public String getMimeType() { return mMimeType; } @Override public Uri getDataUri() { return mDataUri; } @Override public long getDataId() { return mDataId; } @Override public Boolean isPrimary() { return mIsPrimary; } @Override public Drawable getAlternateIcon() { if (mAlternateIconRes == 0) return null; final String resPackageName = mKind.resPackageName; if (resPackageName == null) { return mContext.getResources().getDrawable(mAlternateIconRes); } final PackageManager pm = mContext.getPackageManager(); return pm.getDrawable(resPackageName, mAlternateIconRes, null); } @Override public String getAlternateIconDescription() { if (mAlternateIconDescriptionRes == 0) return null; return mContext.getResources().getString(mAlternateIconDescriptionRes); } @Override public Intent getIntent() { return mIntent; } @Override public Intent getAlternateIntent() { return mAlternateIntent; } @Override public boolean collapseWith(Action other) { if (!shouldCollapseWith(other)) { return false; } return true; } @Override public boolean shouldCollapseWith(Action t) { if (t == null) { return false; } if (!(t instanceof DataAction)) { Log.e(TAG, "t must be DataAction"); return false; } DataAction that = (DataAction)t; if (!ContactsUtils.shouldCollapse(mMimeType, mBody, that.mMimeType, that.mBody)) { return false; } if (!TextUtils.equals(mMimeType, that.mMimeType) || !ContactsUtils.areIntentActionEqual(mIntent, that.mIntent)) { return false; } return true; } }