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
17package com.android.contacts.quickcontact;
18
19import android.content.ComponentName;
20import android.content.ContentUris;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.graphics.drawable.Drawable;
25import android.net.Uri;
26import android.net.WebAddress;
27import android.provider.ContactsContract.CommonDataKinds.Im;
28import android.provider.ContactsContract.Data;
29import android.text.TextUtils;
30import android.util.Log;
31
32import com.android.contacts.common.CallUtil;
33import com.android.contacts.common.ContactsUtils;
34import com.android.contacts.R;
35import com.android.contacts.common.MoreContactUtils;
36import com.android.contacts.common.model.account.AccountType.EditType;
37import com.android.contacts.common.model.dataitem.DataItem;
38import com.android.contacts.common.model.dataitem.DataKind;
39import com.android.contacts.common.model.dataitem.EmailDataItem;
40import com.android.contacts.common.model.dataitem.ImDataItem;
41import com.android.contacts.common.model.dataitem.PhoneDataItem;
42import com.android.contacts.common.model.dataitem.SipAddressDataItem;
43import com.android.contacts.common.model.dataitem.StructuredPostalDataItem;
44import com.android.contacts.common.model.dataitem.WebsiteDataItem;
45import com.android.contacts.util.PhoneCapabilityTester;
46import 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 */
52public 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