| /* |
| * 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 android.content.ComponentName; |
| import android.content.ContentUris; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.graphics.drawable.Drawable; |
| import android.net.Uri; |
| import android.provider.ContactsContract.CommonDataKinds.Im; |
| import android.provider.ContactsContract.Data; |
| import android.telecom.PhoneAccount; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.contacts.R; |
| import com.android.contacts.common.CallUtil; |
| import com.android.contacts.common.ContactsUtils; |
| import com.android.contacts.common.MoreContactUtils; |
| import com.android.contacts.common.model.account.AccountType.EditType; |
| import com.android.contacts.common.model.dataitem.DataItem; |
| import com.android.contacts.common.model.dataitem.DataKind; |
| import com.android.contacts.common.model.dataitem.EmailDataItem; |
| import com.android.contacts.common.model.dataitem.ImDataItem; |
| import com.android.contacts.common.model.dataitem.PhoneDataItem; |
| import com.android.contacts.common.model.dataitem.SipAddressDataItem; |
| import com.android.contacts.common.model.dataitem.StructuredPostalDataItem; |
| import com.android.contacts.common.model.dataitem.WebsiteDataItem; |
| import com.android.contacts.quickcontact.WebAddress.ParseException; |
| import com.android.contacts.util.PhoneCapabilityTester; |
| import com.android.contacts.util.StructuredPostalUtils; |
| |
| /** |
| * 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 final Integer mTimesUsed; |
| private final Long mLastTimeUsed; |
| |
| private CharSequence mBody; |
| private CharSequence mSubtitle; |
| private Intent mIntent; |
| private Intent mAlternateIntent; |
| private int mAlternateIconDescriptionRes; |
| private int mAlternateIconRes; |
| private int mPresence = -1; |
| |
| private Uri mDataUri; |
| private long mDataId; |
| private boolean mIsPrimary; |
| private boolean mIsSuperPrimary; |
| |
| /** |
| * Create an action from common {@link Data} elements. |
| */ |
| public DataAction(Context context, DataItem item, DataKind kind) { |
| mContext = context; |
| mKind = kind; |
| mMimeType = item.getMimeType(); |
| mTimesUsed = item.getTimesUsed(); |
| mLastTimeUsed = item.getLastTimeUsed(); |
| |
| // Determine type for subtitle |
| mSubtitle = ""; |
| if (item.hasKindTypeColumn(kind)) { |
| final int typeValue = item.getKindTypeColumn(kind); |
| |
| // 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 = item.getContentValues().getAsString(type.customColumn); |
| } |
| break; |
| } |
| } |
| } |
| |
| mIsPrimary = item.isPrimary(); |
| mIsSuperPrimary = item.isSuperPrimary(); |
| mBody = item.buildDataStringForDisplay(context, kind); |
| |
| mDataId = item.getId(); |
| mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, mDataId); |
| |
| final boolean hasPhone = PhoneCapabilityTester.isPhone(mContext); |
| final ComponentName smsComponent = PhoneCapabilityTester.getSmsComponent(mContext); |
| final boolean hasSms = (smsComponent != null); |
| |
| // Handle well-known MIME-types with special care |
| if (item instanceof PhoneDataItem) { |
| if (PhoneCapabilityTester.isPhone(mContext)) { |
| PhoneDataItem phone = (PhoneDataItem) item; |
| final String number = phone.getNumber(); |
| if (!TextUtils.isEmpty(number)) { |
| |
| final Intent phoneIntent = hasPhone ? CallUtil.getCallIntent(number) |
| : null; |
| Intent smsIntent = null; |
| if (hasSms) { |
| smsIntent = new Intent(Intent.ACTION_SENDTO, |
| Uri.fromParts(ContactsUtils.SCHEME_SMSTO, number, null)); |
| smsIntent.setComponent(smsComponent); |
| } |
| |
| // 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 (item instanceof SipAddressDataItem) { |
| if (PhoneCapabilityTester.isSipPhone(mContext)) { |
| final SipAddressDataItem sip = (SipAddressDataItem) item; |
| final String address = sip.getSipAddress(); |
| if (!TextUtils.isEmpty(address)) { |
| final Uri callUri = Uri.fromParts(PhoneAccount.SCHEME_SIP, address, null); |
| mIntent = CallUtil.getCallIntent(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 (item instanceof EmailDataItem) { |
| final EmailDataItem email = (EmailDataItem) item; |
| final String address = email.getData(); |
| if (!TextUtils.isEmpty(address)) { |
| final Uri mailUri = Uri.fromParts(ContactsUtils.SCHEME_MAILTO, address, null); |
| mIntent = new Intent(Intent.ACTION_SENDTO, mailUri); |
| } |
| |
| } else if (item instanceof WebsiteDataItem) { |
| final WebsiteDataItem website = (WebsiteDataItem) item; |
| final String url = website.getUrl(); |
| if (!TextUtils.isEmpty(url)) { |
| try { |
| final WebAddress webAddress = new WebAddress(url); |
| mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString())); |
| } catch (ParseException e) { |
| mIntent = null; |
| } |
| } |
| |
| } else if (item instanceof ImDataItem) { |
| ImDataItem im = (ImDataItem) item; |
| final boolean isEmail = im.isCreatedFromEmail(); |
| if (isEmail || im.isProtocolValid()) { |
| final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol(); |
| |
| if (isEmail) { |
| // Use Google Talk string when using Email, and clear data |
| // Uri so we don't try saving Email as primary. |
| mSubtitle = Im.getProtocolLabel(context.getResources(), Im.PROTOCOL_GOOGLE_TALK, |
| null); |
| mDataUri = null; |
| } |
| |
| String host = im.getCustomProtocol(); |
| String data = im.getData(); |
| 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(ContactsUtils.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 = im.getChatCapability(); |
| 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 (item instanceof StructuredPostalDataItem) { |
| StructuredPostalDataItem postal = (StructuredPostalDataItem) item; |
| final String postalAddress = postal.getFormattedAddress(); |
| 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, item.getMimeType()); |
| } |
| |
| mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); |
| } |
| |
| @Override |
| public int getPresence() { |
| return mPresence; |
| } |
| |
| public void setPresence(int presence) { |
| mPresence = presence; |
| } |
| |
| @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 boolean isSuperPrimary() { |
| return mIsSuperPrimary; |
| } |
| |
| @Override |
| public Drawable getAlternateIcon() { |
| if (mAlternateIconRes == 0) return null; |
| |
| final String resourcePackageName = mKind.resourcePackageName; |
| if (resourcePackageName == null) { |
| return mContext.getResources().getDrawable(mAlternateIconRes); |
| } |
| |
| final PackageManager pm = mContext.getPackageManager(); |
| return pm.getDrawable(resourcePackageName, 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 void collapseWith(Action other) { |
| // No-op |
| } |
| |
| @Override |
| public boolean shouldCollapseWith(Action t, Context context) { |
| if (t == null) { |
| return false; |
| } |
| if (!(t instanceof DataAction)) { |
| Log.e(TAG, "t must be DataAction"); |
| return false; |
| } |
| DataAction that = (DataAction)t; |
| if (!MoreContactUtils.shouldCollapse(mMimeType, mBody, that.mMimeType, that.mBody)) { |
| return false; |
| } |
| if (!TextUtils.equals(mMimeType, that.mMimeType) |
| || !ContactsUtils.areIntentActionEqual(mIntent, that.mIntent)) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public Integer getTimesUsed() { |
| return mTimesUsed; |
| } |
| |
| @Override |
| public Long getLastTimeUsed() { |
| return mLastTimeUsed; |
| } |
| } |